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 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: 34 DelayedTaskQueue() : ready_(false) {} 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_|. 45 bool ready() const { return ready_; } 46 47 private: 48 bool ready_; 49 std::vector<base::Closure> pending_tasks_; 50 }; 51 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 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 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 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 99 StateStore::~StateStore() { 100 } 101 102 void StateStore::RegisterKey(const std::string& key) { 103 registered_keys_.insert(key); 104 } 105 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 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 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 130 bool StateStore::IsInitialized() const { return task_queue_->ready(); } 131 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 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 153 void StateStore::OnExtensionUninstalled( 154 content::BrowserContext* browser_context, 155 const Extension* extension) { 156 RemoveKeysForExtension(extension->id()); 157 } 158 159 void StateStore::Init() { 160 if (!db_path_.empty()) 161 store_.Init(db_path_); 162 task_queue_->SetReady(); 163 } 164 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