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 "extensions/browser/api/declarative/rules_registry_service.h"
6
7#include "base/bind.h"
8#include "base/lazy_instance.h"
9#include "base/logging.h"
10#include "base/memory/scoped_ptr.h"
11#include "content/public/browser/browser_thread.h"
12#include "content/public/browser/notification_details.h"
13#include "content/public/browser/notification_service.h"
14#include "content/public/browser/notification_source.h"
15#include "content/public/browser/notification_types.h"
16#include "content/public/browser/render_process_host.h"
17#include "extensions/browser/api/declarative/rules_cache_delegate.h"
18#include "extensions/browser/api/declarative_content/content_rules_registry.h"
19#include "extensions/browser/api/declarative_webrequest/webrequest_constants.h"
20#include "extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h"
21#include "extensions/browser/api/extensions_api_client.h"
22#include "extensions/browser/api/web_request/web_request_api.h"
23#include "extensions/browser/extension_registry.h"
24#include "extensions/common/extension.h"
25
26namespace extensions {
27
28namespace {
29
30// Registers |web_request_rules_registry| on the IO thread.
31void RegisterToExtensionWebRequestEventRouterOnIO(
32    content::BrowserContext* browser_context,
33    const RulesRegistryService::WebViewKey& webview_key,
34    scoped_refptr<WebRequestRulesRegistry> web_request_rules_registry) {
35  ExtensionWebRequestEventRouter::GetInstance()->RegisterRulesRegistry(
36      browser_context, webview_key, web_request_rules_registry);
37}
38
39bool IsWebView(const RulesRegistryService::WebViewKey& webview_key) {
40  return webview_key.embedder_process_id && webview_key.webview_instance_id;
41}
42
43}  // namespace
44
45RulesRegistryService::RulesRegistryService(content::BrowserContext* context)
46    : content_rules_registry_(NULL),
47      extension_registry_observer_(this),
48      browser_context_(context) {
49  if (browser_context_) {
50    extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
51    registrar_.Add(
52        this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
53        content::NotificationService::AllBrowserContextsAndSources());
54    EnsureDefaultRulesRegistriesRegistered(WebViewKey(0, 0));
55  }
56}
57
58RulesRegistryService::~RulesRegistryService() {}
59
60void RulesRegistryService::EnsureDefaultRulesRegistriesRegistered(
61    const WebViewKey& webview_key) {
62  if (!browser_context_)
63    return;
64
65  RulesRegistryKey key(declarative_webrequest_constants::kOnRequest,
66                       webview_key);
67  // If we can find the key in the |rule_registries_| then we have already
68  // installed the default registries.
69  if (ContainsKey(rule_registries_, key))
70    return;
71
72
73  RulesCacheDelegate* web_request_cache_delegate = NULL;
74  if (!IsWebView(webview_key)) {
75    web_request_cache_delegate =
76        new RulesCacheDelegate(true /*log_storage_init_delay*/);
77    cache_delegates_.push_back(web_request_cache_delegate);
78  }
79  scoped_refptr<WebRequestRulesRegistry> web_request_rules_registry(
80      new WebRequestRulesRegistry(browser_context_,
81                                  web_request_cache_delegate,
82                                  webview_key));
83
84  RegisterRulesRegistry(web_request_rules_registry);
85  content::BrowserThread::PostTask(
86      content::BrowserThread::IO, FROM_HERE,
87      base::Bind(&RegisterToExtensionWebRequestEventRouterOnIO,
88          browser_context_, webview_key, web_request_rules_registry));
89
90  // Only create a ContentRulesRegistry for regular pages and not webviews.
91  if (!IsWebView(webview_key)) {
92    RulesCacheDelegate* content_rules_cache_delegate =
93        new RulesCacheDelegate(false /*log_storage_init_delay*/);
94    cache_delegates_.push_back(content_rules_cache_delegate);
95    scoped_refptr<ContentRulesRegistry> content_rules_registry =
96        ExtensionsAPIClient::Get()->CreateContentRulesRegistry(
97            browser_context_, content_rules_cache_delegate);
98    RegisterRulesRegistry(content_rules_registry);
99    content_rules_registry_ = content_rules_registry.get();
100  }
101}
102
103void RulesRegistryService::Shutdown() {
104  // Release the references to all registries. This would happen soon during
105  // destruction of |*this|, but we need the ExtensionWebRequestEventRouter to
106  // be the last to reference the WebRequestRulesRegistry objects, so that
107  // the posted task below causes their destruction on the IO thread, not on UI
108  // where the destruction of |*this| takes place.
109  // TODO(vabr): Remove once http://crbug.com/218451#c6 gets addressed.
110  rule_registries_.clear();
111  content::BrowserThread::PostTask(
112      content::BrowserThread::IO, FROM_HERE,
113      base::Bind(&RegisterToExtensionWebRequestEventRouterOnIO,
114          browser_context_, WebViewKey(0, 0),
115          scoped_refptr<WebRequestRulesRegistry>(NULL)));
116}
117
118static base::LazyInstance<BrowserContextKeyedAPIFactory<RulesRegistryService> >
119    g_factory = LAZY_INSTANCE_INITIALIZER;
120
121// static
122BrowserContextKeyedAPIFactory<RulesRegistryService>*
123RulesRegistryService::GetFactoryInstance() {
124  return g_factory.Pointer();
125}
126
127// static
128RulesRegistryService* RulesRegistryService::Get(
129    content::BrowserContext* context) {
130  return BrowserContextKeyedAPIFactory<RulesRegistryService>::Get(context);
131}
132
133void RulesRegistryService::RegisterRulesRegistry(
134    scoped_refptr<RulesRegistry> rule_registry) {
135  const std::string event_name(rule_registry->event_name());
136  RulesRegistryKey key(event_name, rule_registry->webview_key());
137  DCHECK(rule_registries_.find(key) == rule_registries_.end());
138  rule_registries_[key] = rule_registry;
139}
140
141scoped_refptr<RulesRegistry> RulesRegistryService::GetRulesRegistry(
142    const WebViewKey& webview_key,
143    const std::string& event_name) {
144  EnsureDefaultRulesRegistriesRegistered(webview_key);
145
146  RulesRegistryKey key(event_name, webview_key);
147  RulesRegistryMap::const_iterator i = rule_registries_.find(key);
148  if (i == rule_registries_.end())
149    return scoped_refptr<RulesRegistry>();
150  return i->second;
151}
152
153void RulesRegistryService::RemoveWebViewRulesRegistries(int process_id) {
154  DCHECK_NE(0, process_id);
155
156  std::set<RulesRegistryKey> registries_to_delete;
157  for (RulesRegistryMap::iterator it = rule_registries_.begin();
158       it != rule_registries_.end(); ++it) {
159    const RulesRegistryKey& key = it->first;
160    const WebViewKey& webview_key = key.webview_key;
161    int embedder_process_id = webview_key.embedder_process_id;
162    // |process_id| will always be non-zero.
163    // |embedder_process_id| will only be non-zero if the key corresponds to a
164    // webview registry.
165    // Thus, |embedder_process_id| == |process_id| ==> the process ID is a
166    // webview embedder.
167    if (embedder_process_id != process_id)
168      continue;
169
170    // Modifying the container while iterating is bad so we'll save the keys we
171    // wish to delete in another container, and delete them in another loop.
172    registries_to_delete.insert(key);
173  }
174  for (std::set<RulesRegistryKey>::iterator it = registries_to_delete.begin();
175       it != registries_to_delete.end(); ++it) {
176    rule_registries_.erase(*it);
177  }
178}
179
180void RulesRegistryService::SimulateExtensionUninstalled(
181    const std::string& extension_id) {
182  NotifyRegistriesHelper(&RulesRegistry::OnExtensionUninstalled, extension_id);
183}
184
185void RulesRegistryService::NotifyRegistriesHelper(
186    void (RulesRegistry::*notification_callback)(const std::string&),
187    const std::string& extension_id) {
188  RulesRegistryMap::iterator i;
189  for (i = rule_registries_.begin(); i != rule_registries_.end(); ++i) {
190    scoped_refptr<RulesRegistry> registry = i->second;
191    if (content::BrowserThread::CurrentlyOn(registry->owner_thread())) {
192      (registry.get()->*notification_callback)(extension_id);
193    } else {
194      content::BrowserThread::PostTask(
195          registry->owner_thread(),
196          FROM_HERE,
197          base::Bind(notification_callback, registry, extension_id));
198    }
199  }
200}
201
202void RulesRegistryService::OnExtensionLoaded(
203    content::BrowserContext* browser_context,
204    const Extension* extension) {
205  NotifyRegistriesHelper(&RulesRegistry::OnExtensionLoaded, extension->id());
206}
207
208void RulesRegistryService::OnExtensionUnloaded(
209    content::BrowserContext* browser_context,
210    const Extension* extension,
211    UnloadedExtensionInfo::Reason reason) {
212  NotifyRegistriesHelper(&RulesRegistry::OnExtensionUnloaded, extension->id());
213}
214
215void RulesRegistryService::OnExtensionUninstalled(
216    content::BrowserContext* browser_context,
217    const Extension* extension,
218    extensions::UninstallReason reason) {
219  NotifyRegistriesHelper(&RulesRegistry::OnExtensionUninstalled,
220                         extension->id());
221}
222
223void RulesRegistryService::Observe(
224    int type,
225    const content::NotificationSource& source,
226    const content::NotificationDetails& details) {
227  DCHECK_EQ(content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, type);
228
229  content::RenderProcessHost* process =
230      content::Source<content::RenderProcessHost>(source).ptr();
231  RemoveWebViewRulesRegistries(process->GetID());
232}
233
234}  // namespace extensions
235