Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(911)

Side by Side Diff: chrome/browser/extensions/api/declarative_content/chrome_content_rules_registry.cc

Issue 1159733004: Encapsulate CSS selector declarative content condition tracking (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@stars-declarative-content-range-for
Patch Set: only notify navigation for tracked WebContents, address comments Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/api/declarative_content/chrome_content_rules _registry.h" 5 #include "chrome/browser/extensions/api/declarative_content/chrome_content_rules _registry.h"
6 6
7 #include "chrome/browser/chrome_notification_types.h" 7 #include "chrome/browser/chrome_notification_types.h"
8 #include "chrome/browser/extensions/api/declarative_content/content_action.h" 8 #include "chrome/browser/extensions/api/declarative_content/content_action.h"
9 #include "chrome/browser/extensions/api/declarative_content/content_condition.h" 9 #include "chrome/browser/extensions/api/declarative_content/content_condition.h"
10 #include "chrome/browser/extensions/api/declarative_content/content_constants.h" 10 #include "chrome/browser/extensions/api/declarative_content/content_constants.h"
11 #include "chrome/browser/extensions/extension_tab_util.h"
12 #include "chrome/browser/extensions/extension_util.h" 11 #include "chrome/browser/extensions/extension_util.h"
13 #include "chrome/browser/profiles/profile.h" 12 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ui/browser.h" 13 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_iterator.h" 14 #include "chrome/browser/ui/browser_iterator.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model.h" 15 #include "chrome/browser/ui/tabs/tab_strip_model.h"
17 #include "content/public/browser/navigation_details.h" 16 #include "content/public/browser/navigation_details.h"
18 #include "content/public/browser/notification_service.h" 17 #include "content/public/browser/notification_service.h"
19 #include "content/public/browser/notification_source.h" 18 #include "content/public/browser/notification_source.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "content/public/browser/web_contents.h" 19 #include "content/public/browser/web_contents.h"
22 #include "extensions/browser/api/declarative/rules_registry_service.h" 20 #include "extensions/browser/api/declarative/rules_registry_service.h"
23 #include "extensions/browser/extension_registry.h" 21 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/extension_system.h" 22 #include "extensions/browser/extension_system.h"
25 #include "extensions/common/extension_messages.h"
26 23
27 using url_matcher::URLMatcherConditionSet; 24 using url_matcher::URLMatcherConditionSet;
28 25
29 namespace extensions { 26 namespace extensions {
30 27
28 template <class Func>
29 void ChromeContentRulesRegistry::ForEachWebContents(const Func& func) {
30 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
31 Browser* browser = *it;
32 if (!ManagingRulesForBrowserContext(browser->profile()))
33 continue;
34
35 for (int i = 0, tab_count = browser->tab_strip_model()->count();
36 i < tab_count; ++i) {
37 func(browser->tab_strip_model()->GetWebContentsAt(i));
38 }
39 }
40 }
41
31 ChromeContentRulesRegistry::ChromeContentRulesRegistry( 42 ChromeContentRulesRegistry::ChromeContentRulesRegistry(
32 content::BrowserContext* browser_context, 43 content::BrowserContext* browser_context,
33 RulesCacheDelegate* cache_delegate) 44 RulesCacheDelegate* cache_delegate)
34 : ContentRulesRegistry(browser_context, 45 : ContentRulesRegistry(browser_context,
35 declarative_content_constants::kOnPageChanged, 46 declarative_content_constants::kOnPageChanged,
36 content::BrowserThread::UI, 47 content::BrowserThread::UI,
37 cache_delegate, 48 cache_delegate,
38 RulesRegistryService::kDefaultRulesRegistryID) { 49 RulesRegistryService::kDefaultRulesRegistryID),
50 css_condition_tracker_(browser_context, this) {
39 extension_info_map_ = ExtensionSystem::Get(browser_context)->info_map(); 51 extension_info_map_ = ExtensionSystem::Get(browser_context)->info_map();
40 52
53 // Set up rule tracking for any existing WebContents.
54 ForEachWebContents([this](content::WebContents* contents) {
55 TrackRulesForWebContents(contents);
56 });
57
41 registrar_.Add(this, 58 registrar_.Add(this,
42 content::NOTIFICATION_RENDERER_PROCESS_CREATED, 59 chrome::NOTIFICATION_TAB_ADDED,
43 content::NotificationService::AllBrowserContextsAndSources()); 60 content::NotificationService::AllSources());
44 registrar_.Add(this, 61 registrar_.Add(this,
45 content::NOTIFICATION_WEB_CONTENTS_DESTROYED, 62 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
46 content::NotificationService::AllBrowserContextsAndSources()); 63 content::NotificationService::AllBrowserContextsAndSources());
47 } 64 }
48 65
49 void ChromeContentRulesRegistry::Observe( 66 void ChromeContentRulesRegistry::Observe(
50 int type, 67 int type,
51 const content::NotificationSource& source, 68 const content::NotificationSource& source,
52 const content::NotificationDetails& details) { 69 const content::NotificationDetails& details) {
53 switch (type) { 70 switch (type) {
54 case content::NOTIFICATION_RENDERER_PROCESS_CREATED: { 71 case chrome::NOTIFICATION_TAB_ADDED: {
55 content::RenderProcessHost* process = 72 content::WebContents* contents =
56 content::Source<content::RenderProcessHost>(source).ptr(); 73 content::Details<content::WebContents>(details).ptr();
57 InstructRenderProcessIfSameBrowserContext(process); 74 TrackRulesForWebContents(contents);
58 break; 75 break;
59 } 76 }
60 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: { 77 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
61 content::WebContents* tab = 78 content::WebContents* tab =
62 content::Source<content::WebContents>(source).ptr(); 79 content::Source<content::WebContents>(source).ptr();
63 // Note that neither non-tab WebContents nor tabs from other browser 80 // Note that neither non-tab WebContents nor tabs from other browser
64 // contexts will be in the map. 81 // contexts will be in the map.
65 active_rules_.erase(tab); 82 active_rules_.erase(tab);
66 matching_css_selectors_.erase(tab);
67 break; 83 break;
68 } 84 }
69 } 85 }
70 } 86 }
71 87
72 void ChromeContentRulesRegistry::Apply( 88 void ChromeContentRulesRegistry::RequestEvaluation(
73 content::WebContents* contents, 89 content::WebContents* contents) {
74 const std::vector<std::string>& matching_css_selectors) { 90 EvaluateConditionsForTab(contents);
75 matching_css_selectors_[contents] = matching_css_selectors; 91 }
76 92
77 EvaluateConditionsForTab(contents); 93 bool ChromeContentRulesRegistry::ShouldManageConditionsForBrowserContext(
94 content::BrowserContext* context) {
95 return ManagingRulesForBrowserContext(context);
78 } 96 }
79 97
80 void ChromeContentRulesRegistry::DidNavigateMainFrame( 98 void ChromeContentRulesRegistry::DidNavigateMainFrame(
81 content::WebContents* contents, 99 content::WebContents* contents,
82 const content::LoadCommittedDetails& details, 100 const content::LoadCommittedDetails& details,
83 const content::FrameNavigateParams& params) { 101 const content::FrameNavigateParams& params) {
84 OnTabNavigation(contents, details.is_in_page); 102 if (ContainsKey(active_rules_, contents))
103 css_condition_tracker_.OnWebContentsNavigation(contents, details, params);
85 } 104 }
86 105
87 void ChromeContentRulesRegistry::DidNavigateMainFrameOfOriginalContext( 106 void ChromeContentRulesRegistry::DidNavigateMainFrameOfOriginalContext(
88 content::WebContents* contents, 107 content::WebContents* contents,
89 const content::LoadCommittedDetails& details, 108 const content::LoadCommittedDetails& details,
90 const content::FrameNavigateParams& params) { 109 const content::FrameNavigateParams& params) {
91 DCHECK(browser_context()->IsOffTheRecord()); 110 DCHECK(browser_context()->IsOffTheRecord());
92 OnTabNavigation(contents, details.is_in_page); 111 if (ContainsKey(active_rules_, contents))
112 css_condition_tracker_.OnWebContentsNavigation(contents, details, params);
93 } 113 }
94 114
95 void ChromeContentRulesRegistry::OnTabNavigation(content::WebContents* tab, 115 void ChromeContentRulesRegistry::TrackRulesForWebContents(
96 bool is_in_page_navigation) { 116 content::WebContents* contents) {
97 if (is_in_page_navigation) { 117 // We rely on the_rules_ to have a key-value pair for |contents| to know which
98 // Within-page navigations don't change the set of elements that 118 // WebContents we are working with.
99 // exist, and we only support filtering on the top-level URL, so 119 active_rules_[contents] = std::set<const ContentRule*>();
100 // this can't change which rules match. 120 css_condition_tracker_.TrackForWebContents(contents);
101 return;
102 }
103
104 // Top-level navigation produces a new document. Initially, the
105 // document's empty, so no CSS rules match. The renderer will send
106 // an ExtensionHostMsg_OnWatchedPageChange later if any CSS rules
107 // match.
108 matching_css_selectors_[tab].clear();
109 EvaluateConditionsForTab(tab);
110 } 121 }
111 122
112 bool ChromeContentRulesRegistry::ManagingRulesForBrowserContext( 123 bool ChromeContentRulesRegistry::ManagingRulesForBrowserContext(
113 content::BrowserContext* context) { 124 content::BrowserContext* context) {
114 // Manage both the normal context and incognito contexts associated with it. 125 // Manage both the normal context and incognito contexts associated with it.
115 return Profile::FromBrowserContext(context)->GetOriginalProfile() == 126 return Profile::FromBrowserContext(context)->GetOriginalProfile() ==
116 Profile::FromBrowserContext(browser_context()); 127 Profile::FromBrowserContext(browser_context());
117 } 128 }
118 129
119 std::set<const ContentRule*> ChromeContentRulesRegistry::GetMatches( 130 std::set<const ContentRule*> ChromeContentRulesRegistry::GetMatches(
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
212 // Register url patterns in url_matcher_. 223 // Register url patterns in url_matcher_.
213 URLMatcherConditionSet::Vector all_new_condition_sets; 224 URLMatcherConditionSet::Vector all_new_condition_sets;
214 for (const std::pair<ContentRule::GlobalRuleId, 225 for (const std::pair<ContentRule::GlobalRuleId,
215 linked_ptr<const ContentRule>>& rule_id_rule_pair : 226 linked_ptr<const ContentRule>>& rule_id_rule_pair :
216 new_content_rules) { 227 new_content_rules) {
217 const linked_ptr<const ContentRule>& rule = rule_id_rule_pair.second; 228 const linked_ptr<const ContentRule>& rule = rule_id_rule_pair.second;
218 rule->conditions().GetURLMatcherConditionSets(&all_new_condition_sets); 229 rule->conditions().GetURLMatcherConditionSets(&all_new_condition_sets);
219 } 230 }
220 url_matcher_.AddConditionSets(all_new_condition_sets); 231 url_matcher_.AddConditionSets(all_new_condition_sets);
221 232
222 UpdateConditionCache(); 233 UpdateCssSelectorsFromRules();
223 EvaluateConditionsForAllTabs(); 234 EvaluateConditionsForAllTabs();
224 235
225 return std::string(); 236 return std::string();
226 } 237 }
227 238
228 std::string ChromeContentRulesRegistry::RemoveRulesImpl( 239 std::string ChromeContentRulesRegistry::RemoveRulesImpl(
229 const std::string& extension_id, 240 const std::string& extension_id,
230 const std::vector<std::string>& rule_identifiers) { 241 const std::vector<std::string>& rule_identifiers) {
231 // URLMatcherConditionSet IDs that can be removed from URLMatcher. 242 // URLMatcherConditionSet IDs that can be removed from URLMatcher.
232 std::vector<URLMatcherConditionSet::ID> remove_from_url_matcher; 243 std::vector<URLMatcherConditionSet::ID> remove_from_url_matcher;
(...skipping 26 matching lines...) Expand all
259 } 270 }
260 } 271 }
261 272
262 // Remove reference to actual rule. 273 // Remove reference to actual rule.
263 content_rules_.erase(content_rules_entry); 274 content_rules_.erase(content_rules_entry);
264 } 275 }
265 276
266 // Clear URLMatcher based on condition_set_ids that are not needed any more. 277 // Clear URLMatcher based on condition_set_ids that are not needed any more.
267 url_matcher_.RemoveConditionSets(remove_from_url_matcher); 278 url_matcher_.RemoveConditionSets(remove_from_url_matcher);
268 279
269 UpdateConditionCache(); 280 UpdateCssSelectorsFromRules();
270 281
271 return std::string(); 282 return std::string();
272 } 283 }
273 284
274 std::string ChromeContentRulesRegistry::RemoveAllRulesImpl( 285 std::string ChromeContentRulesRegistry::RemoveAllRulesImpl(
275 const std::string& extension_id) { 286 const std::string& extension_id) {
276 // Search all identifiers of rules that belong to extension |extension_id|. 287 // Search all identifiers of rules that belong to extension |extension_id|.
277 std::vector<std::string> rule_identifiers; 288 std::vector<std::string> rule_identifiers;
278 for (const std::pair<ContentRule::GlobalRuleId, 289 for (const std::pair<ContentRule::GlobalRuleId,
279 linked_ptr<const ContentRule>>& rule_id_rule_pair : 290 linked_ptr<const ContentRule>>& rule_id_rule_pair :
280 content_rules_) { 291 content_rules_) {
281 const ContentRule::GlobalRuleId& global_rule_id = rule_id_rule_pair.first; 292 const ContentRule::GlobalRuleId& global_rule_id = rule_id_rule_pair.first;
282 if (global_rule_id.first == extension_id) 293 if (global_rule_id.first == extension_id)
283 rule_identifiers.push_back(global_rule_id.second); 294 rule_identifiers.push_back(global_rule_id.second);
284 } 295 }
285 296
286 return RemoveRulesImpl(extension_id, rule_identifiers); 297 return RemoveRulesImpl(extension_id, rule_identifiers);
287 } 298 }
288 299
289 void ChromeContentRulesRegistry::UpdateConditionCache() { 300 void ChromeContentRulesRegistry::UpdateCssSelectorsFromRules() {
290 std::set<std::string> css_selectors; // We rely on this being sorted. 301 std::set<std::string> css_selectors; // We rely on this being sorted.
291 for (const std::pair<ContentRule::GlobalRuleId, 302 for (const std::pair<ContentRule::GlobalRuleId,
292 linked_ptr<const ContentRule>>& rule_id_rule_pair : 303 linked_ptr<const ContentRule>>& rule_id_rule_pair :
293 content_rules_) { 304 content_rules_) {
294 const ContentRule& rule = *rule_id_rule_pair.second; 305 const ContentRule& rule = *rule_id_rule_pair.second;
295 for (const linked_ptr<const ContentCondition>& condition : 306 for (const linked_ptr<const ContentCondition>& condition :
296 rule.conditions()) { 307 rule.conditions()) {
297 const std::vector<std::string>& condition_css_selectors = 308 const std::vector<std::string>& condition_css_selectors =
298 condition->css_selectors(); 309 condition->css_selectors();
299 css_selectors.insert(condition_css_selectors.begin(), 310 css_selectors.insert(condition_css_selectors.begin(),
300 condition_css_selectors.end()); 311 condition_css_selectors.end());
301 } 312 }
302 } 313 }
303 314
304 if (css_selectors.size() != watched_css_selectors_.size() || 315 css_condition_tracker_.SetWatchedCssSelectors(css_selectors);
305 !std::equal(css_selectors.begin(),
306 css_selectors.end(),
307 watched_css_selectors_.begin())) {
308 watched_css_selectors_.assign(css_selectors.begin(), css_selectors.end());
309
310 for (content::RenderProcessHost::iterator it(
311 content::RenderProcessHost::AllHostsIterator());
312 !it.IsAtEnd();
313 it.Advance()) {
314 content::RenderProcessHost* process = it.GetCurrentValue();
315 InstructRenderProcessIfSameBrowserContext(process);
316 }
317 }
318 }
319
320 void ChromeContentRulesRegistry::InstructRenderProcessIfSameBrowserContext(
321 content::RenderProcessHost* process) {
322 if (ManagingRulesForBrowserContext(process->GetBrowserContext()))
323 process->Send(new ExtensionMsg_WatchPages(watched_css_selectors_));
324 } 316 }
325 317
326 void ChromeContentRulesRegistry::EvaluateConditionsForTab( 318 void ChromeContentRulesRegistry::EvaluateConditionsForTab(
327 content::WebContents* tab) { 319 content::WebContents* tab) {
328 extensions::RendererContentMatchData renderer_data; 320 extensions::RendererContentMatchData renderer_data;
329 renderer_data.page_url_matches = url_matcher_.MatchURL(tab->GetURL()); 321 renderer_data.page_url_matches = url_matcher_.MatchURL(tab->GetURL());
330 renderer_data.css_selectors.insert(matching_css_selectors_[tab].begin(), 322 css_condition_tracker_.GetMatchingCssSelectors(tab,
331 matching_css_selectors_[tab].end()); 323 &renderer_data.css_selectors);
332 std::set<const ContentRule*> matching_rules = 324 std::set<const ContentRule*> matching_rules =
333 GetMatches(renderer_data, tab->GetBrowserContext()->IsOffTheRecord()); 325 GetMatches(renderer_data, tab->GetBrowserContext()->IsOffTheRecord());
334 if (matching_rules.empty() && !ContainsKey(active_rules_, tab)) 326 if (matching_rules.empty() && !ContainsKey(active_rules_, tab))
335 return; 327 return;
336 328
337 std::set<const ContentRule*>& prev_matching_rules = active_rules_[tab]; 329 std::set<const ContentRule*>& prev_matching_rules = active_rules_[tab];
338 ContentAction::ApplyInfo apply_info = {browser_context(), tab}; 330 ContentAction::ApplyInfo apply_info = {browser_context(), tab};
339 for (const ContentRule* rule : matching_rules) { 331 for (const ContentRule* rule : matching_rules) {
340 apply_info.priority = rule->priority(); 332 apply_info.priority = rule->priority();
341 if (!ContainsKey(prev_matching_rules, rule)) { 333 if (!ContainsKey(prev_matching_rules, rule)) {
342 rule->actions().Apply(rule->extension_id(), base::Time(), &apply_info); 334 rule->actions().Apply(rule->extension_id(), base::Time(), &apply_info);
343 } else { 335 } else {
344 rule->actions().Reapply(rule->extension_id(), base::Time(), &apply_info); 336 rule->actions().Reapply(rule->extension_id(), base::Time(), &apply_info);
345 } 337 }
346 } 338 }
347 for (const ContentRule* rule : prev_matching_rules) { 339 for (const ContentRule* rule : prev_matching_rules) {
348 if (!ContainsKey(matching_rules, rule)) { 340 if (!ContainsKey(matching_rules, rule)) {
349 apply_info.priority = rule->priority(); 341 apply_info.priority = rule->priority();
350 rule->actions().Revert(rule->extension_id(), base::Time(), &apply_info); 342 rule->actions().Revert(rule->extension_id(), base::Time(), &apply_info);
351 } 343 }
352 } 344 }
353 345
354 if (matching_rules.empty()) 346 if (matching_rules.empty())
355 active_rules_.erase(tab); 347 active_rules_[tab].clear();
356 else 348 else
357 swap(matching_rules, prev_matching_rules); 349 swap(matching_rules, prev_matching_rules);
358 } 350 }
359 351
360 void ChromeContentRulesRegistry::EvaluateConditionsForAllTabs() { 352 void ChromeContentRulesRegistry::EvaluateConditionsForAllTabs() {
361 for (chrome::BrowserIterator it; !it.done(); it.Next()) { 353 ForEachWebContents([this](content::WebContents* contents) {
362 Browser* browser = *it; 354 EvaluateConditionsForTab(contents);
363 if (!ManagingRulesForBrowserContext(browser->profile())) 355 });
364 continue;
365
366 for (int i = 0, tab_count = browser->tab_strip_model()->count();
367 i < tab_count;
368 ++i)
369 EvaluateConditionsForTab(browser->tab_strip_model()->GetWebContentsAt(i));
370 }
371 } 356 }
372 357
373 bool ChromeContentRulesRegistry::IsEmpty() const { 358 bool ChromeContentRulesRegistry::IsEmpty() const {
374 return match_id_to_rule_.empty() && content_rules_.empty() && 359 return match_id_to_rule_.empty() && content_rules_.empty() &&
375 url_matcher_.IsEmpty(); 360 url_matcher_.IsEmpty();
376 } 361 }
377 362
363 void ChromeContentRulesRegistry::UpdateMatchingCssSelectorsForTesting(
364 content::WebContents* contents,
365 const std::vector<std::string>& matching_css_selectors) {
366 css_condition_tracker_.UpdateMatchingCssSelectorsForTesting(
367 contents,
368 matching_css_selectors);
369 }
370
371 size_t ChromeContentRulesRegistry::GetActiveRulesCountForTesting() {
372 size_t count = 0;
373 for (auto web_contents_rules_pair : active_rules_)
374 count += web_contents_rules_pair.second.size();
375 return count;
376 }
377
378 ChromeContentRulesRegistry::~ChromeContentRulesRegistry() { 378 ChromeContentRulesRegistry::~ChromeContentRulesRegistry() {
379 } 379 }
380 380
381 base::Time ChromeContentRulesRegistry::GetExtensionInstallationTime( 381 base::Time ChromeContentRulesRegistry::GetExtensionInstallationTime(
382 const std::string& extension_id) const { 382 const std::string& extension_id) const {
383 if (!extension_info_map_.get()) // May be NULL during testing. 383 if (!extension_info_map_.get()) // May be NULL during testing.
384 return base::Time(); 384 return base::Time();
385 385
386 return extension_info_map_->GetInstallTime(extension_id); 386 return extension_info_map_->GetInstallTime(extension_id);
387 } 387 }
388 388
389 } // namespace extensions 389 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698