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