1// Copyright 2013 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/api/declarative/rules_cache_delegate.h"
6
7#include "content/public/browser/browser_context.h"
8#include "content/public/browser/notification_details.h"
9#include "content/public/browser/notification_source.h"
10#include "extensions/browser/api/declarative/rules_registry.h"
11#include "extensions/browser/extension_prefs.h"
12#include "extensions/browser/extension_registry.h"
13#include "extensions/browser/extension_system.h"
14#include "extensions/browser/info_map.h"
15#include "extensions/browser/state_store.h"
16#include "extensions/common/permissions/permissions_data.h"
17
18namespace {
19
20// Returns the key to use for storing declarative rules in the state store.
21std::string GetDeclarativeRuleStorageKey(const std::string& event_name,
22                                         bool incognito) {
23  if (incognito)
24    return "declarative_rules.incognito." + event_name;
25  else
26    return "declarative_rules." + event_name;
27}
28
29
30}  // namespace
31
32namespace extensions {
33
34// RulesCacheDelegate
35
36const char RulesCacheDelegate::kRulesStoredKey[] =
37    "has_declarative_rules";
38
39RulesCacheDelegate::RulesCacheDelegate(bool log_storage_init_delay)
40    : browser_context_(NULL),
41      log_storage_init_delay_(log_storage_init_delay),
42      notified_registry_(false),
43      weak_ptr_factory_(this) {
44}
45
46RulesCacheDelegate::~RulesCacheDelegate() {}
47
48// Returns the key to use for storing whether the rules have been stored.
49// static
50std::string RulesCacheDelegate::GetRulesStoredKey(const std::string& event_name,
51                                                  bool incognito) {
52  std::string result(kRulesStoredKey);
53  result += incognito ? ".incognito." : ".";
54  return result + event_name;
55}
56
57// This is called from the constructor of RulesRegistry, so it is
58// important that it both
59// 1. calls no (in particular virtual) methods of the rules registry, and
60// 2. does not create scoped_refptr holding the registry. (A short-lived
61// scoped_refptr might delete the rules registry before it is constructed.)
62void RulesCacheDelegate::Init(RulesRegistry* registry) {
63  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
64
65  // WARNING: The first use of |registry_| will bind it to the calling thread
66  // so don't use this here.
67  registry_ = registry->GetWeakPtr();
68
69  browser_context_ = registry->browser_context();
70  storage_key_ =
71      GetDeclarativeRuleStorageKey(registry->event_name(),
72                                   browser_context_->IsOffTheRecord());
73  rules_stored_key_ = GetRulesStoredKey(registry->event_name(),
74                                        browser_context_->IsOffTheRecord());
75  rules_registry_thread_ = registry->owner_thread();
76
77  ExtensionSystem& system = *ExtensionSystem::Get(browser_context_);
78  StateStore* store = system.rules_store();
79  if (store)
80    store->RegisterKey(storage_key_);
81
82  if (browser_context_->IsOffTheRecord())
83    log_storage_init_delay_ = false;
84
85  system.ready().Post(
86      FROM_HERE,
87      base::Bind(&RulesCacheDelegate::ReadRulesForInstalledExtensions,
88                 weak_ptr_factory_.GetWeakPtr()));
89  system.ready().Post(FROM_HERE,
90                      base::Bind(&RulesCacheDelegate::CheckIfReady,
91                                 weak_ptr_factory_.GetWeakPtr()));
92}
93
94void RulesCacheDelegate::WriteToStorage(const std::string& extension_id,
95                                     scoped_ptr<base::Value> value) {
96  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
97  if (!browser_context_)
98    return;
99
100  const base::ListValue* rules = NULL;
101  CHECK(value->GetAsList(&rules));
102  bool rules_stored_previously = GetDeclarativeRulesStored(extension_id);
103  bool store_rules = !rules->empty();
104  SetDeclarativeRulesStored(extension_id, store_rules);
105  if (!rules_stored_previously && !store_rules)
106    return;
107
108  StateStore* store = ExtensionSystem::Get(browser_context_)->rules_store();
109  if (store)
110    store->SetExtensionValue(extension_id, storage_key_, value.Pass());
111}
112
113void RulesCacheDelegate::CheckIfReady() {
114  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
115  if (notified_registry_ || !waiting_for_extensions_.empty())
116    return;
117
118  content::BrowserThread::PostTask(
119      rules_registry_thread_,
120      FROM_HERE,
121      base::Bind(
122          &RulesRegistry::MarkReady, registry_, storage_init_time_));
123  notified_registry_ = true;
124}
125
126void RulesCacheDelegate::ReadRulesForInstalledExtensions() {
127  bool is_ready = ExtensionSystem::Get(browser_context_)->ready().is_signaled();
128  // In an OTR context, we start on top of a normal context already, so the
129  // extension service should be ready.
130  DCHECK(!browser_context_->IsOffTheRecord() || is_ready);
131  if (is_ready) {
132    const ExtensionSet& extensions =
133        ExtensionRegistry::Get(browser_context_)->enabled_extensions();
134    const ExtensionPrefs* extension_prefs =
135        ExtensionPrefs::Get(browser_context_);
136    for (ExtensionSet::const_iterator i = extensions.begin();
137         i != extensions.end();
138         ++i) {
139      bool needs_apis_storing_rules =
140          (*i)->permissions_data()->HasAPIPermission(
141              APIPermission::kDeclarativeContent) ||
142          (*i)->permissions_data()->HasAPIPermission(
143              APIPermission::kDeclarativeWebRequest);
144      bool respects_off_the_record =
145          !(browser_context_->IsOffTheRecord()) ||
146          extension_prefs->IsIncognitoEnabled((*i)->id());
147      if (needs_apis_storing_rules && respects_off_the_record)
148        ReadFromStorage((*i)->id());
149    }
150  }
151}
152
153void RulesCacheDelegate::ReadFromStorage(const std::string& extension_id) {
154  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
155  if (!browser_context_)
156    return;
157
158  if (log_storage_init_delay_ && storage_init_time_.is_null())
159    storage_init_time_ = base::Time::Now();
160
161  if (!GetDeclarativeRulesStored(extension_id)) {
162    ExtensionSystem::Get(browser_context_)->ready().Post(
163        FROM_HERE, base::Bind(&RulesCacheDelegate::CheckIfReady,
164                              weak_ptr_factory_.GetWeakPtr()));
165    return;
166  }
167
168  StateStore* store = ExtensionSystem::Get(browser_context_)->rules_store();
169  if (!store)
170    return;
171  waiting_for_extensions_.insert(extension_id);
172  store->GetExtensionValue(
173      extension_id,
174      storage_key_,
175      base::Bind(&RulesCacheDelegate::ReadFromStorageCallback,
176                 weak_ptr_factory_.GetWeakPtr(),
177                 extension_id));
178}
179
180void RulesCacheDelegate::ReadFromStorageCallback(
181    const std::string& extension_id,
182    scoped_ptr<base::Value> value) {
183  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
184  content::BrowserThread::PostTask(
185      rules_registry_thread_,
186      FROM_HERE,
187      base::Bind(&RulesRegistry::DeserializeAndAddRules,
188                 registry_,
189                 extension_id,
190                 base::Passed(&value)));
191
192  waiting_for_extensions_.erase(extension_id);
193
194  if (waiting_for_extensions_.empty())
195    ExtensionSystem::Get(browser_context_)->ready().Post(
196        FROM_HERE, base::Bind(&RulesCacheDelegate::CheckIfReady,
197                              weak_ptr_factory_.GetWeakPtr()));
198}
199
200bool RulesCacheDelegate::GetDeclarativeRulesStored(
201    const std::string& extension_id) const {
202  CHECK(browser_context_);
203  const ExtensionScopedPrefs* extension_prefs =
204      ExtensionPrefs::Get(browser_context_);
205
206  bool rules_stored = true;
207  if (extension_prefs->ReadPrefAsBoolean(
208          extension_id, rules_stored_key_, &rules_stored))
209    return rules_stored;
210
211  // Safe default -- if we don't know that the rules are not stored, we force
212  // a read by returning true.
213  return true;
214}
215
216void RulesCacheDelegate::SetDeclarativeRulesStored(
217    const std::string& extension_id,
218    bool rules_stored) {
219  CHECK(browser_context_);
220  DCHECK(ExtensionRegistry::Get(browser_context_)
221             ->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING));
222
223  ExtensionScopedPrefs* extension_prefs = ExtensionPrefs::Get(browser_context_);
224  extension_prefs->UpdateExtensionPref(
225      extension_id,
226      rules_stored_key_,
227      new base::FundamentalValue(rules_stored));
228}
229
230}  // namespace extensions
231