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 15 namespace { 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 19 const int kInitDelaySeconds = 1; 20 21 std::string GetFullKey(const std::string& extension_id, 22 const std::string& key) { 23 return extension_id + "." + key; 24 } 25 26 } // namespace 27 28 namespace extensions { 29 30 // Helper class to delay tasks until we're ready to start executing them. 31 class 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 51 void StateStore::DelayedTaskQueue::InvokeWhenReady(base::Closure task) { 52 if (ready_) { 53 task.Run(); 54 } else { 55 pending_tasks_.push_back(task); 56 } 57 } 58 59 void 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 67 StateStore::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 87 StateStore::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 98 StateStore::~StateStore() { 99 } 100 101 void StateStore::RequestInitAfterDelay() { 102 InitAfterDelay(); 103 } 104 105 void StateStore::RegisterKey(const std::string& key) { 106 registered_keys_.insert(key); 107 } 108 109 void 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 118 void 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 127 void 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 134 bool StateStore::IsInitialized() const { 135 return task_queue_->ready(); 136 } 137 138 void 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 146 void 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 155 void StateStore::OnExtensionUninstalled( 156 content::BrowserContext* browser_context, 157 const Extension* extension, 158 extensions::UninstallReason reason) { 159 RemoveKeysForExtension(extension->id()); 160 } 161 162 void 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 173 void 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 183 void 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