blob: b5854469ecbcaac37b17ec7ed9b05105647c1902 [file] [log] [blame]
// Copyright (c) 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 "chrome/browser/chromeos/extensions/external_pref_cache_loader.h"
#include <map>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/values.h"
#include "base/version.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/extensions/external_cache.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chromeos/chromeos_switches.h"
#include "content/public/browser/browser_thread.h"
namespace chromeos {
namespace {
// Directory where the extensions are cached.
const char kPreinstalledAppsCacheDir[] = "/var/cache/external_cache";
} // namespace
// Ref-counted class that holds ExternalCache and dispatches cache update events
// to per-profile instances of ExternalPrefCacheLoader. This multiplexing
// is required for multi-profile case.
class ExternalCacheDispatcher
: public ExternalCache::Delegate,
public base::RefCounted<ExternalCacheDispatcher> {
public:
static ExternalCacheDispatcher* GetInstance() {
if (!ExternalCacheDispatcher::instance_)
ExternalCacheDispatcher::instance_ = new ExternalCacheDispatcher;
return instance_;
}
// Implementation of ExternalCache::Delegate:
virtual void OnExtensionListsUpdated(
const base::DictionaryValue* prefs) OVERRIDE {
is_extensions_list_ready_ = true;
for (LoadersMap::iterator it = pref_loaders_.begin();
it != pref_loaders_.end(); ++it) {
it->first->OnExtensionListsUpdated(prefs);
}
}
virtual std::string GetInstalledExtensionVersion(
const std::string& id) OVERRIDE {
// Return lowest installed version. Updater will download an update if
// CWS has higher version. Version returned here matters only if file is
// missing in .crx cache.
base::Version version;
for (LoadersMap::iterator it = pref_loaders_.begin();
it != pref_loaders_.end(); ++it) {
ExtensionServiceInterface* extension_service =
extensions::ExtensionSystem::Get(it->second)->extension_service();
const extensions::Extension* extension = extension_service ?
extension_service->GetExtensionById(id, true) : NULL;
if (extension) {
if (!version.IsValid() || extension->version()->CompareTo(version) < 0)
version = *extension->version();
}
}
return version.IsValid() ? version.GetString() : std::string();
}
void UpdateExtensionsList(scoped_ptr<base::DictionaryValue> prefs) {
DCHECK(!is_extensions_list_ready_);
external_cache_.UpdateExtensionsList(prefs.Pass());
}
// Return false if cache doesn't have list of extensions and it needs to
// be provided via UpdateExtensionsList.
bool RegisterExternalPrefCacheLoader(ExternalPrefCacheLoader* observer,
int base_path_id,
Profile* profile) {
pref_loaders_.insert(std::make_pair(observer, profile));
if (base_path_id_ == 0) {
// First ExternalPrefCacheLoader is registered.
base_path_id_ = base_path_id;
return false;
} else {
CHECK_EQ(base_path_id_, base_path_id);
if (is_extensions_list_ready_) {
// If list of extensions is not ready, |observer| will be notified later
// in OnExtensionListsUpdated.
observer->OnExtensionListsUpdated(external_cache_.cached_extensions());
}
return true;
}
}
void UnregisterExternalPrefCacheLoader(ExternalPrefCacheLoader* observer) {
pref_loaders_.erase(observer);
}
private:
friend class base::RefCounted<ExternalCacheDispatcher>;
typedef std::map<ExternalPrefCacheLoader*, Profile*> LoadersMap;
ExternalCacheDispatcher()
: external_cache_(base::FilePath(kPreinstalledAppsCacheDir),
g_browser_process->system_request_context(),
content::BrowserThread::GetBlockingPool()->
GetSequencedTaskRunnerWithShutdownBehavior(
content::BrowserThread::GetBlockingPool()->
GetSequenceToken(),
base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
this,
true,
true),
base_path_id_(0),
is_extensions_list_ready_(false) {
DCHECK(!ExternalCacheDispatcher::instance_);
}
virtual ~ExternalCacheDispatcher() {
ExternalCacheDispatcher::instance_ = NULL;
}
ExternalCache external_cache_;
LoadersMap pref_loaders_;
int base_path_id_;
bool is_extensions_list_ready_;
static ExternalCacheDispatcher* instance_;
DISALLOW_COPY_AND_ASSIGN(ExternalCacheDispatcher);
};
ExternalCacheDispatcher* ExternalCacheDispatcher::instance_ = NULL;
ExternalPrefCacheLoader::ExternalPrefCacheLoader(int base_path_id,
Profile* profile)
: ExternalPrefLoader(base_path_id, ExternalPrefLoader::NONE),
profile_(profile),
cache_dispatcher_(ExternalCacheDispatcher::GetInstance()) {
}
ExternalPrefCacheLoader::~ExternalPrefCacheLoader() {
cache_dispatcher_->UnregisterExternalPrefCacheLoader(this);
}
void ExternalPrefCacheLoader::OnExtensionListsUpdated(
const base::DictionaryValue* prefs) {
prefs_.reset(prefs->DeepCopy());
ExternalPrefLoader::LoadFinished();
}
void ExternalPrefCacheLoader::StartLoading() {
if (!cache_dispatcher_->RegisterExternalPrefCacheLoader(
this, base_path_id_, profile_)) {
// ExternalCacheDispatcher doesn't know list of extensions load it.
ExternalPrefLoader::StartLoading();
}
}
void ExternalPrefCacheLoader::LoadFinished() {
// TODO(dzhioev): Remove "Tips & Tricks" app from default apps till M33.
if (!CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableFirstRunUI) &&
prefs_.get() && prefs_->HasKey(extension_misc::kTipsAndTricksAppId)) {
prefs_->Remove(extension_misc::kTipsAndTricksAppId, NULL);
}
cache_dispatcher_->UpdateExtensionsList(prefs_.Pass());
}
} // namespace chromeos