Home | History | Annotate | Download | only in browser
      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