Home | History | Annotate | Download | only in extensions
      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/common/extensions/extension.h"
     11 #include "content/public/browser/notification_service.h"
     12 #include "content/public/browser/notification_types.h"
     13 
     14 namespace {
     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
     18 const int kInitDelaySeconds = 1;
     19 
     20 std::string GetFullKey(const std::string& extension_id,
     21                        const std::string& key) {
     22   return extension_id + "." + key;
     23 }
     24 
     25 }  // namespace
     26 
     27 namespace extensions {
     28 
     29 // Helper class to delay tasks until we're ready to start executing them.
     30 class 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 
     47 void StateStore::DelayedTaskQueue::InvokeWhenReady(base::Closure task) {
     48   if (ready_) {
     49     task.Run();
     50   } else {
     51     pending_tasks_.push_back(task);
     52   }
     53 }
     54 
     55 void 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 
     63 StateStore::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 
     85 StateStore::StateStore(Profile* profile, ValueStore* value_store)
     86     : store_(value_store), 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 
     96 StateStore::~StateStore() {
     97 }
     98 
     99 void StateStore::RegisterKey(const std::string& key) {
    100   registered_keys_.insert(key);
    101 }
    102 
    103 void 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 
    111 void 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 
    120 void 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 
    127 void 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 
    156 void StateStore::Init() {
    157   if (!db_path_.empty())
    158     store_.Init(db_path_);
    159   task_queue_->SetReady();
    160 }
    161 
    162 void 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