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