blob: e0ed9a1dc37c1955d9ed91be432bf2f5ca4b8135 [file] [log] [blame]
// Copyright (c) 2012 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 "chrome/browser/extensions/pending_extension_manager.h"
#include <algorithm>
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/version.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/pending_extension_info.h"
#include "chrome/common/extensions/extension.h"
#include "content/public/browser/browser_thread.h"
using content::BrowserThread;
namespace {
// Install predicate used by AddFromExternalUpdateUrl().
bool AlwaysInstall(const extensions::Extension& extension) {
return true;
}
} // 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 {
PendingExtensionList::const_iterator iter;
for (iter = pending_extension_list_.begin();
iter != pending_extension_list_.end();
++iter) {
if (id == iter->id())
return true;
}
return false;
}
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 Extension::Location kSyncLocation = Extension::INTERNAL;
return AddExtensionImpl(id, update_url, Version(), should_allow_install,
kIsFromSync, install_silently, kSyncLocation);
}
bool PendingExtensionManager::AddFromExternalUpdateUrl(
const std::string& id,
const GURL& update_url,
Extension::Location location) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
const bool kIsFromSync = false;
const bool kInstallSilently = true;
const Extension* extension = service_.GetInstalledExtension(id);
if (extension &&
location == Extension::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);
}
bool PendingExtensionManager::AddFromExternalFile(
const std::string& id,
Extension::Location install_source,
const Version& version) {
// 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);
}
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) {
Extension::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 == Extension::EXTERNAL_PREF ||
install_source == Extension::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,
Extension::Location install_source) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
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;
// 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 (version.IsValid() &&
pending->version().IsValid() &&
pending->version().CompareTo(version) == 1) {
VLOG(1) << "Keep existing record (has a newer version).";
return false;
}
Extension::Location higher_priority_location =
Extension::GetHigherPriorityLocation(
install_source, pending->install_source());
if (higher_priority_location != install_source) {
VLOG(1) << "Keep existing record (has a higher priority location).";
return false;
}
VLOG(1) << "Overwrite existing record.";
std::replace(pending_extension_list_.begin(),
pending_extension_list_.end(),
*pending,
PendingExtensionInfo(id,
update_url,
version,
should_allow_install,
is_from_sync,
install_silently,
install_source));
} else {
pending_extension_list_.push_back(
PendingExtensionInfo(id,
update_url,
version,
should_allow_install,
is_from_sync,
install_silently,
install_source));
}
return true;
}
void PendingExtensionManager::AddForTesting(
const PendingExtensionInfo& pending_extension_info) {
pending_extension_list_.push_back(pending_extension_info);
}
} // namespace extensions