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/state_store.h"
6
7#include "base/bind.h"
8#include "base/message_loop/message_loop.h"
9#include "content/public/browser/browser_context.h"
10#include "content/public/browser/notification_service.h"
11#include "content/public/browser/notification_types.h"
12#include "extensions/browser/extension_registry.h"
13#include "extensions/common/extension.h"
14
15namespace {
16
17// Delay, in seconds, before we should open the State Store database. We
18// defer it to avoid slowing down startup. See http://crbug.com/161848
19const int kInitDelaySeconds = 1;
20
21std::string GetFullKey(const std::string& extension_id,
22                       const std::string& key) {
23  return extension_id + "." + key;
24}
25
26}  // namespace
27
28namespace extensions {
29
30// Helper class to delay tasks until we're ready to start executing them.
31class StateStore::DelayedTaskQueue {
32 public:
33  DelayedTaskQueue() : ready_(false) {}
34  ~DelayedTaskQueue() {}
35
36  // Queues up a task for invoking once we're ready. Invokes immediately if
37  // we're already ready.
38  void InvokeWhenReady(base::Closure task);
39
40  // Marks us ready, and invokes all pending tasks.
41  void SetReady();
42
43  // Return whether or not the DelayedTaskQueue is |ready_|.
44  bool ready() const { return ready_; }
45
46 private:
47  bool ready_;
48  std::vector<base::Closure> pending_tasks_;
49};
50
51void StateStore::DelayedTaskQueue::InvokeWhenReady(base::Closure task) {
52  if (ready_) {
53    task.Run();
54  } else {
55    pending_tasks_.push_back(task);
56  }
57}
58
59void StateStore::DelayedTaskQueue::SetReady() {
60  ready_ = true;
61
62  for (size_t i = 0; i < pending_tasks_.size(); ++i)
63    pending_tasks_[i].Run();
64  pending_tasks_.clear();
65}
66
67StateStore::StateStore(content::BrowserContext* context,
68                       const base::FilePath& db_path,
69                       bool deferred_load)
70    : db_path_(db_path),
71      task_queue_(new DelayedTaskQueue()),
72      extension_registry_observer_(this) {
73  extension_registry_observer_.Add(ExtensionRegistry::Get(context));
74
75  if (deferred_load) {
76    // Don't Init() until the first page is loaded or the embedder explicitly
77    // requests it.
78    registrar_.Add(
79        this,
80        content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
81        content::NotificationService::AllBrowserContextsAndSources());
82  } else {
83    Init();
84  }
85}
86
87StateStore::StateStore(content::BrowserContext* context,
88                       scoped_ptr<ValueStore> value_store)
89    : store_(value_store.Pass()),
90      task_queue_(new DelayedTaskQueue()),
91      extension_registry_observer_(this) {
92  extension_registry_observer_.Add(ExtensionRegistry::Get(context));
93
94  // This constructor is for testing. No need to delay Init.
95  Init();
96}
97
98StateStore::~StateStore() {
99}
100
101void StateStore::RequestInitAfterDelay() {
102  InitAfterDelay();
103}
104
105void StateStore::RegisterKey(const std::string& key) {
106  registered_keys_.insert(key);
107}
108
109void StateStore::GetExtensionValue(const std::string& extension_id,
110                                   const std::string& key,
111                                   ReadCallback callback) {
112  task_queue_->InvokeWhenReady(base::Bind(&ValueStoreFrontend::Get,
113                                          base::Unretained(&store_),
114                                          GetFullKey(extension_id, key),
115                                          callback));
116}
117
118void StateStore::SetExtensionValue(const std::string& extension_id,
119                                   const std::string& key,
120                                   scoped_ptr<base::Value> value) {
121  task_queue_->InvokeWhenReady(base::Bind(&ValueStoreFrontend::Set,
122                                          base::Unretained(&store_),
123                                          GetFullKey(extension_id, key),
124                                          base::Passed(&value)));
125}
126
127void StateStore::RemoveExtensionValue(const std::string& extension_id,
128                                      const std::string& key) {
129  task_queue_->InvokeWhenReady(base::Bind(&ValueStoreFrontend::Remove,
130                                          base::Unretained(&store_),
131                                          GetFullKey(extension_id, key)));
132}
133
134bool StateStore::IsInitialized() const {
135  return task_queue_->ready();
136}
137
138void StateStore::Observe(int type,
139                         const content::NotificationSource& source,
140                         const content::NotificationDetails& details) {
141  DCHECK_EQ(type, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME);
142  registrar_.RemoveAll();
143  InitAfterDelay();
144}
145
146void StateStore::OnExtensionWillBeInstalled(
147    content::BrowserContext* browser_context,
148    const Extension* extension,
149    bool is_update,
150    bool from_ephemeral,
151    const std::string& old_name) {
152  RemoveKeysForExtension(extension->id());
153}
154
155void StateStore::OnExtensionUninstalled(
156    content::BrowserContext* browser_context,
157    const Extension* extension,
158    extensions::UninstallReason reason) {
159  RemoveKeysForExtension(extension->id());
160}
161
162void StateStore::Init() {
163  // Could be called twice if InitAfterDelay() is requested explicitly by the
164  // embedder in addition to internally after first page load.
165  if (IsInitialized())
166    return;
167
168  if (!db_path_.empty())
169    store_.Init(db_path_);
170  task_queue_->SetReady();
171}
172
173void StateStore::InitAfterDelay() {
174  if (IsInitialized())
175    return;
176
177  base::MessageLoop::current()->PostDelayedTask(
178      FROM_HERE,
179      base::Bind(&StateStore::Init, AsWeakPtr()),
180      base::TimeDelta::FromSeconds(kInitDelaySeconds));
181}
182
183void StateStore::RemoveKeysForExtension(const std::string& extension_id) {
184  for (std::set<std::string>::iterator key = registered_keys_.begin();
185       key != registered_keys_.end();
186       ++key) {
187    task_queue_->InvokeWhenReady(base::Bind(&ValueStoreFrontend::Remove,
188                                            base::Unretained(&store_),
189                                            GetFullKey(extension_id, *key)));
190  }
191}
192
193}  // namespace extensions
194