// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "extensions/browser/pending_extension_manager.h" #include <algorithm> #include "base/logging.h" #include "base/version.h" #include "chrome/browser/extensions/extension_service.h" #include "content/public/browser/browser_thread.h" #include "extensions/common/extension.h" #include "url/gurl.h" using content::BrowserThread; namespace { // Install predicate used by AddFromExternalUpdateUrl(). bool AlwaysInstall(const extensions::Extension* extension) { return true; } std::string GetVersionString(const Version& version) { return version.IsValid() ? version.GetString() : "invalid"; } } // namespace namespace extensions { PendingExtensionManager::PendingExtensionManager( const ExtensionServiceInterface& service) : service_(service) { } PendingExtensionManager::~PendingExtensionManager() {} const PendingExtensionInfo* PendingExtensionManager::GetById( const std::string& id) const { PendingExtensionList::const_iterator iter; for (iter = pending_extension_list_.begin(); iter != pending_extension_list_.end(); ++iter) { if (id == iter->id()) return &(*iter); } return NULL; } bool PendingExtensionManager::Remove(const std::string& id) { PendingExtensionList::iterator iter; for (iter = pending_extension_list_.begin(); iter != pending_extension_list_.end(); ++iter) { if (id == iter->id()) { pending_extension_list_.erase(iter); return true; } } return false; } bool PendingExtensionManager::IsIdPending(const std::string& id) const { return GetById(id) != NULL; } bool PendingExtensionManager::HasPendingExtensions() const { return !pending_extension_list_.empty(); } bool PendingExtensionManager::HasPendingExtensionFromSync() const { PendingExtensionList::const_iterator iter; for (iter = pending_extension_list_.begin(); iter != pending_extension_list_.end(); ++iter) { if (iter->is_from_sync()) return true; } return false; } bool PendingExtensionManager::AddFromSync( const std::string& id, const GURL& update_url, PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install, bool install_silently) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (service_.GetInstalledExtension(id)) { LOG(ERROR) << "Trying to add pending extension " << id << " which already exists"; return false; } // Make sure we don't ever try to install the CWS app, because even though // it is listed as a syncable app (because its values need to be synced) it // should already be installed on every instance. if (id == extension_misc::kWebStoreAppId) { NOTREACHED(); return false; } const bool kIsFromSync = true; const Manifest::Location kSyncLocation = Manifest::INTERNAL; const bool kMarkAcknowledged = false; return AddExtensionImpl(id, update_url, Version(), should_allow_install, kIsFromSync, install_silently, kSyncLocation, Extension::NO_FLAGS, kMarkAcknowledged); } bool PendingExtensionManager::AddFromExtensionImport( const std::string& id, const GURL& update_url, PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (service_.GetInstalledExtension(id)) { LOG(ERROR) << "Trying to add pending extension " << id << " which already exists"; return false; } const bool kIsFromSync = false; const bool kInstallSilently = true; const Manifest::Location kManifestLocation = Manifest::INTERNAL; const bool kMarkAcknowledged = false; return AddExtensionImpl(id, update_url, Version(), should_allow_install, kIsFromSync, kInstallSilently, kManifestLocation, Extension::NO_FLAGS, kMarkAcknowledged); } bool PendingExtensionManager::AddFromExternalUpdateUrl( const std::string& id, const GURL& update_url, Manifest::Location location, int creation_flags, bool mark_acknowledged) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); const bool kIsFromSync = false; const bool kInstallSilently = true; const Extension* extension = service_.GetInstalledExtension(id); if (extension && location == Manifest::GetHigherPriorityLocation(location, extension->location())) { // If the new location has higher priority than the location of an existing // extension, let the update process overwrite the existing extension. } else { if (service_.IsExternalExtensionUninstalled(id)) return false; if (extension) { LOG(DFATAL) << "Trying to add extension " << id << " by external update, but it is already installed."; return false; } } return AddExtensionImpl(id, update_url, Version(), &AlwaysInstall, kIsFromSync, kInstallSilently, location, creation_flags, mark_acknowledged); } bool PendingExtensionManager::AddFromExternalFile( const std::string& id, Manifest::Location install_source, const Version& version, int creation_flags, bool mark_acknowledged) { // TODO(skerner): AddFromSync() checks to see if the extension is // installed, but this method assumes that the caller already // made sure it is not installed. Make all AddFrom*() methods // consistent. GURL kUpdateUrl = GURL(); bool kIsFromSync = false; bool kInstallSilently = true; return AddExtensionImpl( id, kUpdateUrl, version, &AlwaysInstall, kIsFromSync, kInstallSilently, install_source, creation_flags, mark_acknowledged); } void PendingExtensionManager::GetPendingIdsForUpdateCheck( std::list<std::string>* out_ids_for_update_check) const { PendingExtensionList::const_iterator iter; for (iter = pending_extension_list_.begin(); iter != pending_extension_list_.end(); ++iter) { Manifest::Location install_source = iter->install_source(); // Some install sources read a CRX from the filesystem. They can // not be fetched from an update URL, so don't include them in the // set of ids. if (install_source == Manifest::EXTERNAL_PREF || install_source == Manifest::EXTERNAL_REGISTRY) continue; out_ids_for_update_check->push_back(iter->id()); } } bool PendingExtensionManager::AddExtensionImpl( const std::string& id, const GURL& update_url, const Version& version, PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install, bool is_from_sync, bool install_silently, Manifest::Location install_source, int creation_flags, bool mark_acknowledged) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); PendingExtensionInfo info(id, update_url, version, should_allow_install, is_from_sync, install_silently, install_source, creation_flags, mark_acknowledged); if (const PendingExtensionInfo* pending = GetById(id)) { // Bugs in this code will manifest as sporadic incorrect extension // locations in situations where multiple install sources run at the // same time. For example, on first login to a chrome os machine, an // extension may be requested by sync and the default extension set. // The following logging will help diagnose such issues. VLOG(1) << "Extension id " << id << " was entered for update more than once." << " old location: " << pending->install_source() << " new location: " << install_source << " old version: " << GetVersionString(pending->version()) << " new version: " << GetVersionString(version); // Never override an existing extension with an older version. Only // extensions from local CRX files have a known version; extensions from an // update URL will get the latest version. // If |pending| has the same or higher precedence than |info| then don't // install |info| over |pending|. if (pending->CompareTo(info) >= 0) return false; VLOG(1) << "Overwrite existing record."; std::replace(pending_extension_list_.begin(), pending_extension_list_.end(), *pending, info); } else { pending_extension_list_.push_back(info); } return true; } void PendingExtensionManager::AddForTesting( const PendingExtensionInfo& pending_extension_info) { pending_extension_list_.push_back(pending_extension_info); } } // namespace extensions