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