1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/extensions/pending_extension_manager.h" 6 7 #include <algorithm> 8 9 #include "base/logging.h" 10 #include "base/version.h" 11 #include "chrome/browser/extensions/extension_service.h" 12 #include "content/public/browser/browser_thread.h" 13 #include "url/gurl.h" 14 15 using content::BrowserThread; 16 17 namespace { 18 19 // Install predicate used by AddFromExternalUpdateUrl(). 20 bool AlwaysInstall(const extensions::Extension* extension) { 21 return true; 22 } 23 24 std::string GetVersionString(const Version& version) { 25 return version.IsValid() ? version.GetString() : "invalid"; 26 } 27 28 } // namespace 29 30 namespace extensions { 31 32 PendingExtensionManager::PendingExtensionManager( 33 const ExtensionServiceInterface& service) 34 : service_(service) { 35 } 36 37 PendingExtensionManager::~PendingExtensionManager() {} 38 39 const PendingExtensionInfo* PendingExtensionManager::GetById( 40 const std::string& id) const { 41 PendingExtensionList::const_iterator iter; 42 for (iter = pending_extension_list_.begin(); 43 iter != pending_extension_list_.end(); 44 ++iter) { 45 if (id == iter->id()) 46 return &(*iter); 47 } 48 49 return NULL; 50 } 51 52 bool PendingExtensionManager::Remove(const std::string& id) { 53 PendingExtensionList::iterator iter; 54 for (iter = pending_extension_list_.begin(); 55 iter != pending_extension_list_.end(); 56 ++iter) { 57 if (id == iter->id()) { 58 pending_extension_list_.erase(iter); 59 return true; 60 } 61 } 62 63 return false; 64 } 65 66 bool PendingExtensionManager::IsIdPending(const std::string& id) const { 67 PendingExtensionList::const_iterator iter; 68 for (iter = pending_extension_list_.begin(); 69 iter != pending_extension_list_.end(); 70 ++iter) { 71 if (id == iter->id()) 72 return true; 73 } 74 75 return false; 76 } 77 78 bool PendingExtensionManager::HasPendingExtensions() const { 79 return !pending_extension_list_.empty(); 80 } 81 82 bool PendingExtensionManager::HasPendingExtensionFromSync() const { 83 PendingExtensionList::const_iterator iter; 84 for (iter = pending_extension_list_.begin(); 85 iter != pending_extension_list_.end(); 86 ++iter) { 87 if (iter->is_from_sync()) 88 return true; 89 } 90 91 return false; 92 } 93 94 bool PendingExtensionManager::AddFromSync( 95 const std::string& id, 96 const GURL& update_url, 97 PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install, 98 bool install_silently) { 99 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 100 101 if (service_.GetInstalledExtension(id)) { 102 LOG(ERROR) << "Trying to add pending extension " << id 103 << " which already exists"; 104 return false; 105 } 106 107 // Make sure we don't ever try to install the CWS app, because even though 108 // it is listed as a syncable app (because its values need to be synced) it 109 // should already be installed on every instance. 110 if (id == extension_misc::kWebStoreAppId) { 111 NOTREACHED(); 112 return false; 113 } 114 115 const bool kIsFromSync = true; 116 const Manifest::Location kSyncLocation = Manifest::INTERNAL; 117 118 return AddExtensionImpl(id, update_url, Version(), should_allow_install, 119 kIsFromSync, install_silently, kSyncLocation); 120 } 121 122 bool PendingExtensionManager::AddFromExtensionImport( 123 const std::string& id, 124 const GURL& update_url, 125 PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install) { 126 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 127 128 if (service_.GetInstalledExtension(id)) { 129 LOG(ERROR) << "Trying to add pending extension " << id 130 << " which already exists"; 131 return false; 132 } 133 134 const bool kIsFromSync = false; 135 const bool kInstallSilently = true; 136 const Manifest::Location kManifestLocation = Manifest::INTERNAL; 137 138 return AddExtensionImpl(id, update_url, Version(), should_allow_install, 139 kIsFromSync, kInstallSilently, kManifestLocation); 140 } 141 142 bool PendingExtensionManager::AddFromExternalUpdateUrl( 143 const std::string& id, 144 const GURL& update_url, 145 Manifest::Location location) { 146 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 147 148 const bool kIsFromSync = false; 149 const bool kInstallSilently = true; 150 151 const Extension* extension = service_.GetInstalledExtension(id); 152 if (extension && 153 location == Manifest::GetHigherPriorityLocation(location, 154 extension->location())) { 155 // If the new location has higher priority than the location of an existing 156 // extension, let the update process overwrite the existing extension. 157 } else { 158 if (service_.IsExternalExtensionUninstalled(id)) 159 return false; 160 161 if (extension) { 162 LOG(DFATAL) << "Trying to add extension " << id 163 << " by external update, but it is already installed."; 164 return false; 165 } 166 } 167 168 return AddExtensionImpl(id, update_url, Version(), &AlwaysInstall, 169 kIsFromSync, kInstallSilently, 170 location); 171 } 172 173 174 bool PendingExtensionManager::AddFromExternalFile( 175 const std::string& id, 176 Manifest::Location install_source, 177 const Version& version) { 178 // TODO(skerner): AddFromSync() checks to see if the extension is 179 // installed, but this method assumes that the caller already 180 // made sure it is not installed. Make all AddFrom*() methods 181 // consistent. 182 GURL kUpdateUrl = GURL(); 183 bool kIsFromSync = false; 184 bool kInstallSilently = true; 185 186 return AddExtensionImpl( 187 id, 188 kUpdateUrl, 189 version, 190 &AlwaysInstall, 191 kIsFromSync, 192 kInstallSilently, 193 install_source); 194 } 195 196 void PendingExtensionManager::GetPendingIdsForUpdateCheck( 197 std::list<std::string>* out_ids_for_update_check) const { 198 PendingExtensionList::const_iterator iter; 199 for (iter = pending_extension_list_.begin(); 200 iter != pending_extension_list_.end(); 201 ++iter) { 202 Manifest::Location install_source = iter->install_source(); 203 204 // Some install sources read a CRX from the filesystem. They can 205 // not be fetched from an update URL, so don't include them in the 206 // set of ids. 207 if (install_source == Manifest::EXTERNAL_PREF || 208 install_source == Manifest::EXTERNAL_REGISTRY) 209 continue; 210 211 out_ids_for_update_check->push_back(iter->id()); 212 } 213 } 214 215 bool PendingExtensionManager::AddExtensionImpl( 216 const std::string& id, 217 const GURL& update_url, 218 const Version& version, 219 PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install, 220 bool is_from_sync, 221 bool install_silently, 222 Manifest::Location install_source) { 223 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 224 225 PendingExtensionInfo info(id, 226 update_url, 227 version, 228 should_allow_install, 229 is_from_sync, 230 install_silently, 231 install_source); 232 233 if (const PendingExtensionInfo* pending = GetById(id)) { 234 // Bugs in this code will manifest as sporadic incorrect extension 235 // locations in situations where multiple install sources run at the 236 // same time. For example, on first login to a chrome os machine, an 237 // extension may be requested by sync and the default extension set. 238 // The following logging will help diagnose such issues. 239 VLOG(1) << "Extension id " << id 240 << " was entered for update more than once." 241 << " old location: " << pending->install_source() 242 << " new location: " << install_source 243 << " old version: " << GetVersionString(pending->version()) 244 << " new version: " << GetVersionString(version); 245 246 // Never override an existing extension with an older version. Only 247 // extensions from local CRX files have a known version; extensions from an 248 // update URL will get the latest version. 249 250 // If |pending| has the same or higher precedence than |info| then don't 251 // install |info| over |pending|. 252 if (pending->CompareTo(info) >= 0) 253 return false; 254 255 VLOG(1) << "Overwrite existing record."; 256 257 std::replace(pending_extension_list_.begin(), 258 pending_extension_list_.end(), 259 *pending, 260 info); 261 } else { 262 pending_extension_list_.push_back(info); 263 } 264 265 return true; 266 } 267 268 void PendingExtensionManager::AddForTesting( 269 const PendingExtensionInfo& pending_extension_info) { 270 pending_extension_list_.push_back(pending_extension_info); 271 } 272 273 } // namespace extensions 274