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 "extensions/browser/api/storage/storage_frontend.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/files/file_path.h"
10#include "base/json/json_reader.h"
11#include "base/lazy_instance.h"
12#include "content/public/browser/browser_context.h"
13#include "content/public/browser/browser_thread.h"
14#include "extensions/browser/api/extensions_api_client.h"
15#include "extensions/browser/api/storage/leveldb_settings_storage_factory.h"
16#include "extensions/browser/api/storage/local_value_store_cache.h"
17#include "extensions/browser/event_router.h"
18#include "extensions/browser/extension_registry.h"
19#include "extensions/common/api/storage.h"
20
21using content::BrowserContext;
22using content::BrowserThread;
23
24namespace extensions {
25
26namespace {
27
28base::LazyInstance<BrowserContextKeyedAPIFactory<StorageFrontend> > g_factory =
29    LAZY_INSTANCE_INITIALIZER;
30
31// Settings change Observer which forwards changes on to the extension
32// processes for |context| and its incognito partner if it exists.
33class DefaultObserver : public SettingsObserver {
34 public:
35  explicit DefaultObserver(BrowserContext* context)
36      : browser_context_(context) {}
37
38  // SettingsObserver implementation.
39  virtual void OnSettingsChanged(
40      const std::string& extension_id,
41      settings_namespace::Namespace settings_namespace,
42      const std::string& change_json) OVERRIDE {
43    // TODO(gdk): This is a temporary hack while the refactoring for
44    // string-based event payloads is removed. http://crbug.com/136045
45    scoped_ptr<base::ListValue> args(new base::ListValue());
46    args->Append(base::JSONReader::Read(change_json));
47    args->Append(new base::StringValue(settings_namespace::ToString(
48        settings_namespace)));
49    scoped_ptr<Event> event(new Event(
50        core_api::storage::OnChanged::kEventName, args.Pass()));
51    EventRouter::Get(browser_context_)
52        ->DispatchEventToExtension(extension_id, event.Pass());
53  }
54
55 private:
56  BrowserContext* const browser_context_;
57};
58
59}  // namespace
60
61// static
62StorageFrontend* StorageFrontend::Get(BrowserContext* context) {
63  return BrowserContextKeyedAPIFactory<StorageFrontend>::Get(context);
64}
65
66// static
67StorageFrontend* StorageFrontend::CreateForTesting(
68    const scoped_refptr<SettingsStorageFactory>& storage_factory,
69    BrowserContext* context) {
70  return new StorageFrontend(storage_factory, context);
71}
72
73StorageFrontend::StorageFrontend(BrowserContext* context)
74    : browser_context_(context) {
75  Init(new LeveldbSettingsStorageFactory());
76}
77
78StorageFrontend::StorageFrontend(
79    const scoped_refptr<SettingsStorageFactory>& factory,
80    BrowserContext* context)
81    : browser_context_(context) {
82  Init(factory);
83}
84
85void StorageFrontend::Init(
86    const scoped_refptr<SettingsStorageFactory>& factory) {
87  observers_ = new SettingsObserverList();
88  browser_context_observer_.reset(new DefaultObserver(browser_context_));
89  DCHECK_CURRENTLY_ON(BrowserThread::UI);
90  DCHECK(!browser_context_->IsOffTheRecord());
91
92  observers_->AddObserver(browser_context_observer_.get());
93
94  caches_[settings_namespace::LOCAL] =
95      new LocalValueStoreCache(factory, browser_context_->GetPath());
96
97  // Add any additional caches the embedder supports (for example, caches
98  // for chrome.storage.managed and chrome.storage.sync).
99  ExtensionsAPIClient::Get()->AddAdditionalValueStoreCaches(
100      browser_context_, factory, observers_, &caches_);
101}
102
103StorageFrontend::~StorageFrontend() {
104  DCHECK_CURRENTLY_ON(BrowserThread::UI);
105  observers_->RemoveObserver(browser_context_observer_.get());
106  for (CacheMap::iterator it = caches_.begin(); it != caches_.end(); ++it) {
107    ValueStoreCache* cache = it->second;
108    cache->ShutdownOnUI();
109    BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, cache);
110  }
111}
112
113ValueStoreCache* StorageFrontend::GetValueStoreCache(
114    settings_namespace::Namespace settings_namespace) const {
115  CacheMap::const_iterator it = caches_.find(settings_namespace);
116  if (it != caches_.end())
117    return it->second;
118  return NULL;
119}
120
121bool StorageFrontend::IsStorageEnabled(
122    settings_namespace::Namespace settings_namespace) const {
123  return caches_.find(settings_namespace) != caches_.end();
124}
125
126void StorageFrontend::RunWithStorage(
127    scoped_refptr<const Extension> extension,
128    settings_namespace::Namespace settings_namespace,
129    const ValueStoreCache::StorageCallback& callback) {
130  DCHECK_CURRENTLY_ON(BrowserThread::UI);
131  CHECK(extension.get());
132
133  ValueStoreCache* cache = caches_[settings_namespace];
134  CHECK(cache);
135
136  BrowserThread::PostTask(
137      BrowserThread::FILE, FROM_HERE,
138      base::Bind(&ValueStoreCache::RunWithValueStoreForExtension,
139                 base::Unretained(cache), callback, extension));
140}
141
142void StorageFrontend::DeleteStorageSoon(const std::string& extension_id) {
143  DCHECK_CURRENTLY_ON(BrowserThread::UI);
144  for (CacheMap::iterator it = caches_.begin(); it != caches_.end(); ++it) {
145    ValueStoreCache* cache = it->second;
146    BrowserThread::PostTask(
147        BrowserThread::FILE, FROM_HERE,
148        base::Bind(&ValueStoreCache::DeleteStorageSoon,
149                   base::Unretained(cache),
150                   extension_id));
151  }
152}
153
154scoped_refptr<SettingsObserverList> StorageFrontend::GetObservers() {
155  DCHECK_CURRENTLY_ON(BrowserThread::UI);
156  return observers_;
157}
158
159void StorageFrontend::DisableStorageForTesting(
160    settings_namespace::Namespace settings_namespace) {
161  CacheMap::iterator it = caches_.find(settings_namespace);
162  if (it != caches_.end()) {
163    ValueStoreCache* cache = it->second;
164    cache->ShutdownOnUI();
165    BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, cache);
166    caches_.erase(it);
167  }
168}
169
170// BrowserContextKeyedAPI implementation.
171
172// static
173BrowserContextKeyedAPIFactory<StorageFrontend>*
174StorageFrontend::GetFactoryInstance() {
175  return g_factory.Pointer();
176}
177
178// static
179const char* StorageFrontend::service_name() { return "StorageFrontend"; }
180
181}  // namespace extensions
182