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