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