0

Revert of Encapsulate CSS selector declarative content condition tracking (patchset id:140001 of https://codereview.chromium.org/1159733004/)

Reason for revert:
Stability sheriff here; I'm speculatively rolling this out because I suspect it caused Issue 497586.

Original issue's description:
> Encapsulate CSS selector declarative content condition tracking
>
> This is the first step in a refactoring of the declarativeContent API
> implementation. The goal is to facilitate profile state matching by
> providing abstractions for conditions and condition state tracking
> and evaluation.
>
> This CL consists of steps 1 and 2 described in the associated bug:
> by-hand instantiation of DeclarativeRule as DeclarativeContentRule to
> decouple changes from the web request declarative API implementation,
> and encapsulation of CSS selector condition tracking logic. No
> functional change is intended.
>
> BUG=492946
>
> Committed: https://crrev.com/fe76220cf0a1cdcfe74ca7daeae9f1da2b91cc6b
> Cr-Commit-Position: refs/heads/master@{#333144}

TBR=kalman@chromium.org,jyasskin@chromium.org,wittman@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=492946

Review URL: https://codereview.chromium.org/1159623014

Cr-Commit-Position: refs/heads/master@{#333238}
This commit is contained in:
dominicc
2015-06-07 21:15:29 -07:00
committed by Commit bot
parent 0b7513ad94
commit 4fcf9f62d9
13 changed files with 159 additions and 737 deletions

@ -8,6 +8,7 @@
#include "chrome/browser/extensions/api/declarative_content/content_action.h"
#include "chrome/browser/extensions/api/declarative_content/content_condition.h"
#include "chrome/browser/extensions/api/declarative_content/content_constants.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
@ -16,29 +17,17 @@
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/api/declarative/rules_registry_service.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension_messages.h"
using url_matcher::URLMatcherConditionSet;
namespace extensions {
template <class Func>
void ChromeContentRulesRegistry::ForEachWebContents(const Func& func) {
for (chrome::BrowserIterator it; !it.done(); it.Next()) {
Browser* browser = *it;
if (!ManagingRulesForBrowserContext(browser->profile()))
continue;
for (int i = 0, tab_count = browser->tab_strip_model()->count();
i < tab_count; ++i) {
func(browser->tab_strip_model()->GetWebContentsAt(i));
}
}
}
ChromeContentRulesRegistry::ChromeContentRulesRegistry(
content::BrowserContext* browser_context,
RulesCacheDelegate* cache_delegate)
@ -46,18 +35,12 @@ ChromeContentRulesRegistry::ChromeContentRulesRegistry(
declarative_content_constants::kOnPageChanged,
content::BrowserThread::UI,
cache_delegate,
RulesRegistryService::kDefaultRulesRegistryID),
css_condition_tracker_(browser_context, this) {
RulesRegistryService::kDefaultRulesRegistryID) {
extension_info_map_ = ExtensionSystem::Get(browser_context)->info_map();
// Set up rule tracking for any existing WebContents.
ForEachWebContents([this](content::WebContents* contents) {
TrackRulesForWebContents(contents);
});
registrar_.Add(this,
chrome::NOTIFICATION_TAB_ADDED,
content::NotificationService::AllSources());
content::NOTIFICATION_RENDERER_PROCESS_CREATED,
content::NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this,
content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
content::NotificationService::AllBrowserContextsAndSources());
@ -68,10 +51,10 @@ void ChromeContentRulesRegistry::Observe(
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case chrome::NOTIFICATION_TAB_ADDED: {
content::WebContents* contents =
content::Details<content::WebContents>(details).ptr();
TrackRulesForWebContents(contents);
case content::NOTIFICATION_RENDERER_PROCESS_CREATED: {
content::RenderProcessHost* process =
content::Source<content::RenderProcessHost>(source).ptr();
InstructRenderProcessIfSameBrowserContext(process);
break;
}
case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
@ -80,27 +63,25 @@ void ChromeContentRulesRegistry::Observe(
// Note that neither non-tab WebContents nor tabs from other browser
// contexts will be in the map.
active_rules_.erase(tab);
matching_css_selectors_.erase(tab);
break;
}
}
}
void ChromeContentRulesRegistry::RequestEvaluation(
content::WebContents* contents) {
EvaluateConditionsForTab(contents);
}
void ChromeContentRulesRegistry::Apply(
content::WebContents* contents,
const std::vector<std::string>& matching_css_selectors) {
matching_css_selectors_[contents] = matching_css_selectors;
bool ChromeContentRulesRegistry::ShouldManageConditionsForBrowserContext(
content::BrowserContext* context) {
return ManagingRulesForBrowserContext(context);
EvaluateConditionsForTab(contents);
}
void ChromeContentRulesRegistry::DidNavigateMainFrame(
content::WebContents* contents,
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) {
if (ContainsKey(active_rules_, contents))
css_condition_tracker_.OnWebContentsNavigation(contents, details, params);
OnTabNavigation(contents, details.is_in_page);
}
void ChromeContentRulesRegistry::DidNavigateMainFrameOfOriginalContext(
@ -108,16 +89,24 @@ void ChromeContentRulesRegistry::DidNavigateMainFrameOfOriginalContext(
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) {
DCHECK(browser_context()->IsOffTheRecord());
if (ContainsKey(active_rules_, contents))
css_condition_tracker_.OnWebContentsNavigation(contents, details, params);
OnTabNavigation(contents, details.is_in_page);
}
void ChromeContentRulesRegistry::TrackRulesForWebContents(
content::WebContents* contents) {
// We rely on the_rules_ to have a key-value pair for |contents| to know which
// WebContents we are working with.
active_rules_[contents] = std::set<const ContentRule*>();
css_condition_tracker_.TrackForWebContents(contents);
void ChromeContentRulesRegistry::OnTabNavigation(content::WebContents* tab,
bool is_in_page_navigation) {
if (is_in_page_navigation) {
// Within-page navigations don't change the set of elements that
// exist, and we only support filtering on the top-level URL, so
// this can't change which rules match.
return;
}
// Top-level navigation produces a new document. Initially, the
// document's empty, so no CSS rules match. The renderer will send
// an ExtensionHostMsg_OnWatchedPageChange later if any CSS rules
// match.
matching_css_selectors_[tab].clear();
EvaluateConditionsForTab(tab);
}
bool ChromeContentRulesRegistry::ManagingRulesForBrowserContext(
@ -230,7 +219,7 @@ std::string ChromeContentRulesRegistry::AddRulesImpl(
}
url_matcher_.AddConditionSets(all_new_condition_sets);
UpdateCssSelectorsFromRules();
UpdateConditionCache();
EvaluateConditionsForAllTabs();
return std::string();
@ -277,7 +266,7 @@ std::string ChromeContentRulesRegistry::RemoveRulesImpl(
// Clear URLMatcher based on condition_set_ids that are not needed any more.
url_matcher_.RemoveConditionSets(remove_from_url_matcher);
UpdateCssSelectorsFromRules();
UpdateConditionCache();
return std::string();
}
@ -297,7 +286,7 @@ std::string ChromeContentRulesRegistry::RemoveAllRulesImpl(
return RemoveRulesImpl(extension_id, rule_identifiers);
}
void ChromeContentRulesRegistry::UpdateCssSelectorsFromRules() {
void ChromeContentRulesRegistry::UpdateConditionCache() {
std::set<std::string> css_selectors; // We rely on this being sorted.
for (const std::pair<ContentRule::GlobalRuleId,
linked_ptr<const ContentRule>>& rule_id_rule_pair :
@ -312,15 +301,34 @@ void ChromeContentRulesRegistry::UpdateCssSelectorsFromRules() {
}
}
css_condition_tracker_.SetWatchedCssSelectors(css_selectors);
if (css_selectors.size() != watched_css_selectors_.size() ||
!std::equal(css_selectors.begin(),
css_selectors.end(),
watched_css_selectors_.begin())) {
watched_css_selectors_.assign(css_selectors.begin(), css_selectors.end());
for (content::RenderProcessHost::iterator it(
content::RenderProcessHost::AllHostsIterator());
!it.IsAtEnd();
it.Advance()) {
content::RenderProcessHost* process = it.GetCurrentValue();
InstructRenderProcessIfSameBrowserContext(process);
}
}
}
void ChromeContentRulesRegistry::InstructRenderProcessIfSameBrowserContext(
content::RenderProcessHost* process) {
if (ManagingRulesForBrowserContext(process->GetBrowserContext()))
process->Send(new ExtensionMsg_WatchPages(watched_css_selectors_));
}
void ChromeContentRulesRegistry::EvaluateConditionsForTab(
content::WebContents* tab) {
extensions::RendererContentMatchData renderer_data;
renderer_data.page_url_matches = url_matcher_.MatchURL(tab->GetURL());
css_condition_tracker_.GetMatchingCssSelectors(tab,
&renderer_data.css_selectors);
renderer_data.css_selectors.insert(matching_css_selectors_[tab].begin(),
matching_css_selectors_[tab].end());
std::set<const ContentRule*> matching_rules =
GetMatches(renderer_data, tab->GetBrowserContext()->IsOffTheRecord());
if (matching_rules.empty() && !ContainsKey(active_rules_, tab))
@ -344,15 +352,22 @@ void ChromeContentRulesRegistry::EvaluateConditionsForTab(
}
if (matching_rules.empty())
active_rules_[tab].clear();
active_rules_.erase(tab);
else
swap(matching_rules, prev_matching_rules);
}
void ChromeContentRulesRegistry::EvaluateConditionsForAllTabs() {
ForEachWebContents([this](content::WebContents* contents) {
EvaluateConditionsForTab(contents);
});
for (chrome::BrowserIterator it; !it.done(); it.Next()) {
Browser* browser = *it;
if (!ManagingRulesForBrowserContext(browser->profile()))
continue;
for (int i = 0, tab_count = browser->tab_strip_model()->count();
i < tab_count;
++i)
EvaluateConditionsForTab(browser->tab_strip_model()->GetWebContentsAt(i));
}
}
bool ChromeContentRulesRegistry::IsEmpty() const {
@ -360,21 +375,6 @@ bool ChromeContentRulesRegistry::IsEmpty() const {
url_matcher_.IsEmpty();
}
void ChromeContentRulesRegistry::UpdateMatchingCssSelectorsForTesting(
content::WebContents* contents,
const std::vector<std::string>& matching_css_selectors) {
css_condition_tracker_.UpdateMatchingCssSelectorsForTesting(
contents,
matching_css_selectors);
}
size_t ChromeContentRulesRegistry::GetActiveRulesCountForTesting() {
size_t count = 0;
for (auto web_contents_rules_pair : active_rules_)
count += web_contents_rules_pair.second.size();
return count;
}
ChromeContentRulesRegistry::~ChromeContentRulesRegistry() {
}

@ -16,7 +16,6 @@
#include "base/time/time.h"
#include "chrome/browser/extensions/api/declarative_content/content_action.h"
#include "chrome/browser/extensions/api/declarative_content/content_condition.h"
#include "chrome/browser/extensions/api/declarative_content/declarative_content_css_condition_tracker.h"
#include "components/url_matcher/url_matcher.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
@ -67,17 +66,20 @@ typedef DeclarativeRule<ContentCondition, ContentAction> ContentRule;
// side of split-mode extensions to incognito tabs. The non-incognito instance
// handles incognito tabs for spanning-mode extensions, plus all non-incognito
// tabs.
class ChromeContentRulesRegistry
: public ContentRulesRegistry,
public content::NotificationObserver,
public DeclarativeContentCssConditionTrackerDelegate {
class ChromeContentRulesRegistry : public ContentRulesRegistry,
public content::NotificationObserver {
public:
// For testing, |ui_part| can be NULL. In that case it constructs the
// registry with storage functionality suspended.
ChromeContentRulesRegistry(content::BrowserContext* browser_context,
RulesCacheDelegate* cache_delegate);
// ContentRulesRegistry:
// ChromeContentRulesRegistry implementation:
// Applies all content rules given an update (CSS match change or
// page navigation, for now) from the renderer.
void Apply(content::WebContents* contents,
const std::vector<std::string>& matching_css_selectors) override;
// Applies all content rules given that a tab was just navigated.
void DidNavigateMainFrame(
content::WebContents* tab,
@ -92,7 +94,7 @@ class ChromeContentRulesRegistry
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) override;
// RulesRegistry:
// Implementation of RulesRegistry:
std::string AddRulesImpl(
const std::string& extension_id,
const std::vector<linked_ptr<RulesRegistry::Rule>>& rules) override;
@ -101,31 +103,14 @@ class ChromeContentRulesRegistry
const std::vector<std::string>& rule_identifiers) override;
std::string RemoveAllRulesImpl(const std::string& extension_id) override;
// content::NotificationObserver:
// content::NotificationObserver implementation.
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override;
// DeclarativeContentCssConditionTrackerDelegate:
void RequestEvaluation(content::WebContents* contents) override;
bool ShouldManageConditionsForBrowserContext(
content::BrowserContext* context) override;
// Returns true if this object retains no allocated data. Only for debugging.
bool IsEmpty() const;
// TODO(wittman): Remove once DeclarativeChromeContentRulesRegistry no longer
// depends on concrete condition implementations. At that point
// DeclarativeChromeContentRulesRegistryTest.ActiveRulesDoesntGrow will be
// able to use a test condition object and not need to depend on force setting
// matching CSS seleectors.
void UpdateMatchingCssSelectorsForTesting(
content::WebContents* contents,
const std::vector<std::string>& matching_css_selectors);
// Returns the number of active rules.
size_t GetActiveRulesCountForTesting();
protected:
~ChromeContentRulesRegistry() override;
@ -134,22 +119,27 @@ class ChromeContentRulesRegistry
const std::string& extension_id) const;
private:
// Utility function for iterating a lambda over the relevant WebContents.
template <class Func>
void ForEachWebContents(const Func& func);
// Set up the state to track rules for |contents|.
void TrackRulesForWebContents(content::WebContents* contents);
friend class DeclarativeChromeContentRulesRegistryTest;
// True if this object is managing the rules for |context|.
bool ManagingRulesForBrowserContext(content::BrowserContext* context);
// Applies all content rules given that a tab was just navigated.
void OnTabNavigation(content::WebContents* tab, bool is_in_page_navigation);
std::set<const ContentRule*> GetMatches(
const RendererContentMatchData& renderer_data,
bool is_incognito_renderer) const;
// Updates the condition evaluator with the current watched CSS selectors.
void UpdateCssSelectorsFromRules();
// Scans the rules for the set of conditions they're watching. If the set has
// changed, calls InstructRenderProcess() for each RenderProcessHost in the
// current browser_context.
void UpdateConditionCache();
// If the renderer process is associated with our browser context, tells it
// what page attributes to watch for using an ExtensionMsg_WatchPages.
void InstructRenderProcessIfSameBrowserContext(
content::RenderProcessHost* process);
// Evaluates the conditions for |tab| based on the tab state and matching CSS
// selectors.
@ -162,6 +152,8 @@ class ChromeContentRulesRegistry
URLMatcherIdToRule;
typedef std::map<ContentRule::GlobalRuleId, linked_ptr<const ContentRule>>
RulesMap;
typedef std::map<content::WebContents*, std::vector<std::string>>
CssSelectors;
// Map that tells us which ContentRules may match under the condition that
// the URLMatcherConditionSet::ID was returned by the |url_matcher_|.
@ -170,22 +162,23 @@ class ChromeContentRulesRegistry
RulesMap content_rules_;
// Maps a WebContents to the set of rules that match on that WebContents.
// This lets us call Revert as appropriate. Note that this is expected to have
// a key-value pair for every WebContents the registry is tracking, even if
// the value is the empty set.
// This lets us call Revert as appropriate.
std::map<content::WebContents*, std::set<const ContentRule*>> active_rules_;
// Matches URLs for the page_url condition.
url_matcher::URLMatcher url_matcher_;
// Responsible for evaluating the declarative content conditions.
DeclarativeContentCssConditionTracker css_condition_tracker_;
// All CSS selectors any rule's conditions watch for.
std::vector<std::string> watched_css_selectors_;
// Manages our notification registrations.
content::NotificationRegistrar registrar_;
scoped_refptr<InfoMap> extension_info_map_;
// Maps tab_id to the matching CSS selectors for the tab.
CssSelectors matching_css_selectors_;
DISALLOW_COPY_AND_ASSIGN(ChromeContentRulesRegistry);
};

@ -7,12 +7,10 @@
#include <string>
#include "base/test/values_test_util.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/test_extension_environment.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/frame_navigate_params.h"
#include "testing/gmock/include/gmock/gmock.h"
@ -20,27 +18,42 @@
namespace extensions {
TEST(DeclarativeChromeContentRulesRegistryTest, ActiveRulesDoesntGrow) {
using base::test::ParseJson;
using testing::HasSubstr;
using content::WebContents;
// Must be outside the anonymous namespace to be a friend of
// ContentRulesRegistry.
class DeclarativeChromeContentRulesRegistryTest : public testing::Test {
protected:
using RulesMap =
std::map<content::WebContents*, std::set<const ContentRule*>>;
static const RulesMap& active_rules(
const ChromeContentRulesRegistry& registry) {
return registry.active_rules_;
}
};
namespace {
TEST_F(DeclarativeChromeContentRulesRegistryTest, ActiveRulesDoesntGrow) {
TestExtensionEnvironment env;
scoped_refptr<ChromeContentRulesRegistry> registry(
new ChromeContentRulesRegistry(env.profile(), NULL));
EXPECT_EQ(0u, registry->GetActiveRulesCountForTesting());
EXPECT_EQ(0u, active_rules(*registry.get()).size());
scoped_ptr<content::WebContents> tab = env.MakeTab();
// Let the registry know about the tab.
registry->Observe(chrome::NOTIFICATION_TAB_ADDED,
content::Source<content::WebContentsDelegate>(nullptr),
content::Details<content::WebContents>(tab.get()));
registry->DidNavigateMainFrame(tab.get(), content::LoadCommittedDetails(),
content::FrameNavigateParams());
EXPECT_EQ(0u, registry->GetActiveRulesCountForTesting());
content::LoadCommittedDetails load_details;
content::FrameNavigateParams navigate_params;
scoped_ptr<WebContents> tab = env.MakeTab();
registry->DidNavigateMainFrame(tab.get(), load_details, navigate_params);
EXPECT_EQ(0u, active_rules(*registry.get()).size());
// Add a rule.
linked_ptr<RulesRegistry::Rule> rule(new RulesRegistry::Rule);
RulesRegistry::Rule::Populate(
*base::test::ParseJson(
*ParseJson(
"{\n"
" \"id\": \"rule1\",\n"
" \"priority\": 100,\n"
@ -57,35 +70,30 @@ TEST(DeclarativeChromeContentRulesRegistryTest, ActiveRulesDoesntGrow) {
std::vector<linked_ptr<RulesRegistry::Rule> > rules;
rules.push_back(rule);
const Extension* extension = env.MakeExtension(*base::test::ParseJson(
const Extension* extension = env.MakeExtension(*ParseJson(
"{\"page_action\": {}}"));
registry->AddRulesImpl(extension->id(), rules);
registry->DidNavigateMainFrame(tab.get(), content::LoadCommittedDetails(),
content::FrameNavigateParams());
EXPECT_EQ(0u, registry->GetActiveRulesCountForTesting());
registry->DidNavigateMainFrame(tab.get(), load_details, navigate_params);
EXPECT_EQ(0u, active_rules(*registry.get()).size());
std::vector<std::string> css_selectors;
css_selectors.push_back("input");
registry->UpdateMatchingCssSelectorsForTesting(tab.get(), css_selectors);
EXPECT_EQ(1u, registry->GetActiveRulesCountForTesting());
registry->Apply(tab.get(), css_selectors);
EXPECT_EQ(1u, active_rules(*registry.get()).size());
// Closing the tab should erase its entry from active_rules_.
tab.reset();
EXPECT_EQ(0u, registry->GetActiveRulesCountForTesting());
EXPECT_EQ(0u, active_rules(*registry.get()).size());
tab = env.MakeTab();
// Let the registry know about the tab.
registry->Observe(chrome::NOTIFICATION_TAB_ADDED,
content::Source<content::WebContentsDelegate>(nullptr),
content::Details<content::WebContents>(tab.get()));
registry->UpdateMatchingCssSelectorsForTesting(tab.get(), css_selectors);
EXPECT_EQ(1u, registry->GetActiveRulesCountForTesting());
registry->Apply(tab.get(), css_selectors);
EXPECT_EQ(1u, active_rules(*registry.get()).size());
// Navigating the tab should erase its entry from active_rules_ if
// it no longer matches.
registry->DidNavigateMainFrame(tab.get(), content::LoadCommittedDetails(),
content::FrameNavigateParams());
EXPECT_EQ(0u, registry->GetActiveRulesCountForTesting());
registry->DidNavigateMainFrame(tab.get(), load_details, navigate_params);
EXPECT_EQ(0u, active_rules(*registry.get()).size());
}
} // namespace
} // namespace extensions

@ -16,7 +16,6 @@
namespace base {
class Time;
class Value;
class DictionaryValue;
}
namespace content {

@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_CONTENT_CONTENT_CONDITION_H_
#define CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_CONTENT_CONTENT_CONDITION_H_
#include <map>
#include <set>
#include <string>
#include <vector>
@ -12,17 +13,12 @@
#include "base/containers/hash_tables.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/values.h"
#include "components/url_matcher/url_matcher.h"
#include "extensions/browser/api/declarative/declarative_rule.h"
namespace base {
class Value;
}
namespace extensions {
class Extension;
struct RendererContentMatchData {
RendererContentMatchData();
~RendererContentMatchData();

@ -1,254 +0,0 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/api/declarative_content/declarative_content_css_condition_tracker.h"
#include <algorithm>
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/render_process_host.h"
#include "extensions/common/extension_messages.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_message_macros.h"
namespace extensions {
//
// PerWebContentsTracker
//
// Monitors CSS selector matching state on one WebContents.
class DeclarativeContentCssConditionTracker::
PerWebContentsTracker : public content::WebContentsObserver {
public:
using RequestEvaluationCallback =
DeclarativeContentCssConditionTrackerDelegate::WebContentsCallback;
using DestroyingCallback =
base::Callback<void(PerWebContentsTracker*)>;
PerWebContentsTracker(
content::WebContents* contents,
const RequestEvaluationCallback& request_evaluation,
const DestroyingCallback& destroying);
~PerWebContentsTracker() override;
void OnWebContentsNavigation(const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params);
// See comment on similar function above.
void UpdateMatchingCssSelectorsForTesting(
const std::vector<std::string>& matching_css_selectors);
const std::vector<std::string>& matching_css_selectors() const {
return matching_css_selectors_;
}
private:
// content::WebContentsObserver overrides.
bool OnMessageReceived(const IPC::Message& message) override;
void WebContentsDestroyed() override;
void OnWatchedPageChange(const std::vector<std::string>& css_selectors);
const RequestEvaluationCallback request_evaluation_;
const DestroyingCallback destroying_;
std::vector<std::string> matching_css_selectors_;
DISALLOW_COPY_AND_ASSIGN(PerWebContentsTracker);
};
DeclarativeContentCssConditionTracker::PerWebContentsTracker::
PerWebContentsTracker(
content::WebContents* contents,
const RequestEvaluationCallback& request_evaluation,
const DestroyingCallback& destroying)
: WebContentsObserver(contents),
request_evaluation_(request_evaluation),
destroying_(destroying) {
}
DeclarativeContentCssConditionTracker::PerWebContentsTracker::
~PerWebContentsTracker() {
}
void DeclarativeContentCssConditionTracker::PerWebContentsTracker::
OnWebContentsNavigation(const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) {
if (details.is_in_page) {
// Within-page navigations don't change the set of elements that
// exist, and we only support filtering on the top-level URL, so
// this can't change which rules match.
return;
}
// Top-level navigation produces a new document. Initially, the
// document's empty, so no CSS rules match. The renderer will send
// an ExtensionHostMsg_OnWatchedPageChange later if any CSS rules
// match.
matching_css_selectors_.clear();
request_evaluation_.Run(web_contents());
}
void DeclarativeContentCssConditionTracker::PerWebContentsTracker::
UpdateMatchingCssSelectorsForTesting(
const std::vector<std::string>& matching_css_selectors) {
matching_css_selectors_ = matching_css_selectors;
request_evaluation_.Run(web_contents());
}
bool
DeclarativeContentCssConditionTracker::PerWebContentsTracker::
OnMessageReceived(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PerWebContentsTracker, message)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_OnWatchedPageChange,
OnWatchedPageChange)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void DeclarativeContentCssConditionTracker::PerWebContentsTracker::
WebContentsDestroyed() {
destroying_.Run(this);
delete this;
}
void
DeclarativeContentCssConditionTracker::PerWebContentsTracker::
OnWatchedPageChange(
const std::vector<std::string>& css_selectors) {
matching_css_selectors_ = css_selectors;
request_evaluation_.Run(web_contents());
}
//
// DeclarativeContentCssConditionTrackerDelegate
//
DeclarativeContentCssConditionTrackerDelegate::
DeclarativeContentCssConditionTrackerDelegate() {}
DeclarativeContentCssConditionTrackerDelegate::
~DeclarativeContentCssConditionTrackerDelegate() {}
//
// DeclarativeContentCssConditionTracker
//
DeclarativeContentCssConditionTracker::DeclarativeContentCssConditionTracker(
content::BrowserContext* context,
DeclarativeContentCssConditionTrackerDelegate* delegate)
: context_(context),
delegate_(delegate) {
registrar_.Add(this,
content::NOTIFICATION_RENDERER_PROCESS_CREATED,
content::NotificationService::AllBrowserContextsAndSources());
}
DeclarativeContentCssConditionTracker::
~DeclarativeContentCssConditionTracker() {
// All monitored WebContents are expected to be destroyed before this object.
CHECK(per_web_contents_tracker_.empty());
}
// We use the sorted propery of the set for equality checks with
// watched_css_selectors_, which is guaranteed to be sorted because it's set
// from the set contents.
void DeclarativeContentCssConditionTracker::SetWatchedCssSelectors(
const std::set<std::string>& new_watched_css_selectors) {
if (new_watched_css_selectors.size() != watched_css_selectors_.size() ||
!std::equal(new_watched_css_selectors.begin(),
new_watched_css_selectors.end(),
watched_css_selectors_.begin())) {
watched_css_selectors_.assign(new_watched_css_selectors.begin(),
new_watched_css_selectors.end());
for (content::RenderProcessHost::iterator it(
content::RenderProcessHost::AllHostsIterator());
!it.IsAtEnd();
it.Advance()) {
InstructRenderProcessIfManagingBrowserContext(it.GetCurrentValue());
}
}
}
void DeclarativeContentCssConditionTracker::TrackForWebContents(
content::WebContents* contents) {
CreatePerWebContentsTracker(contents);
}
void DeclarativeContentCssConditionTracker::OnWebContentsNavigation(
content::WebContents* contents,
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) {
per_web_contents_tracker_[contents]->OnWebContentsNavigation(details, params);
}
void DeclarativeContentCssConditionTracker::GetMatchingCssSelectors(
content::WebContents* contents,
base::hash_set<std::string>* css_selectors) {
const std::vector<std::string>& matching_css_selectors =
per_web_contents_tracker_[contents]->matching_css_selectors();
css_selectors->insert(matching_css_selectors.begin(),
matching_css_selectors.end());
}
void DeclarativeContentCssConditionTracker::
UpdateMatchingCssSelectorsForTesting(
content::WebContents* contents,
const std::vector<std::string>& matching_css_selectors) {
per_web_contents_tracker_[contents]->
UpdateMatchingCssSelectorsForTesting(matching_css_selectors);
}
void DeclarativeContentCssConditionTracker::CreatePerWebContentsTracker(
content::WebContents* contents) {
// This is a weak pointer; PerWebContentsTracker owns itself.
per_web_contents_tracker_[contents] =
new PerWebContentsTracker(
contents,
base::Bind(&DeclarativeContentCssConditionTrackerDelegate::
RequestEvaluation,
base::Unretained(delegate_)),
base::Bind(&DeclarativeContentCssConditionTracker::
RemovePerWebContentsTracker,
base::Unretained(this)));
}
void DeclarativeContentCssConditionTracker::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case content::NOTIFICATION_RENDERER_PROCESS_CREATED: {
content::RenderProcessHost* process =
content::Source<content::RenderProcessHost>(source).ptr();
InstructRenderProcessIfManagingBrowserContext(process);
break;
}
}
}
void DeclarativeContentCssConditionTracker::
InstructRenderProcessIfManagingBrowserContext(
content::RenderProcessHost* process) {
if (delegate_->ShouldManageConditionsForBrowserContext(
process->GetBrowserContext())) {
process->Send(new ExtensionMsg_WatchPages(watched_css_selectors_));
}
}
void DeclarativeContentCssConditionTracker::RemovePerWebContentsTracker(
PerWebContentsTracker* tracker) {
per_web_contents_tracker_.erase(tracker->web_contents());
}
} // namespace extensions

@ -1,137 +0,0 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_CONTENT_DECLARATIVE_CONTENT_CSS_CONDITION_TRACKER_H_
#define CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_CONTENT_DECLARATIVE_CONTENT_CSS_CONDITION_TRACKER_H_
#include <map>
#include <set>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/containers/hash_tables.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/web_contents_observer.h"
namespace content {
class BrowserContext;
struct FrameNavigateParams;
struct LoadCommittedDetails;
class RenderProcessHost;
class WebContents;
}
namespace extensions {
// Interface that allows the DeclarativeContentCssConditionTracker to request
// condition evaluation, and shields it from having to know about Browsers.
class DeclarativeContentCssConditionTrackerDelegate {
public:
// Callback for per-tab invocations.
using WebContentsCallback =
base::Callback<void(content::WebContents* content)>;
// Requests re-evaluation of conditions for |contents|.
virtual void RequestEvaluation(content::WebContents* contents) = 0;
// Returns true if the evaluator should manage condition state for |context|.
virtual bool ShouldManageConditionsForBrowserContext(
content::BrowserContext* context) = 0;
protected:
DeclarativeContentCssConditionTrackerDelegate();
virtual ~DeclarativeContentCssConditionTrackerDelegate();
private:
DISALLOW_COPY_AND_ASSIGN(DeclarativeContentCssConditionTrackerDelegate);
};
// Supports watching of CSS selectors to across tab contents in a browser
// context, and querying for the matching CSS selectors for a context.
class DeclarativeContentCssConditionTracker
: public content::NotificationObserver {
public:
DeclarativeContentCssConditionTracker(
content::BrowserContext* context,
DeclarativeContentCssConditionTrackerDelegate* delegate);
~DeclarativeContentCssConditionTracker() override;
// Sets the set of CSS selectors to watch for CSS condition evaluation.
void SetWatchedCssSelectors(
const std::set<std::string>& watched_css_selectors);
// Requests that CSS conditions be tracked for |contents|.
void TrackForWebContents(content::WebContents* contents);
// Handles navigation of |contents|. We depend on the caller to notify us of
// this event rather than listening for it ourselves, so that the caller can
// coordinate evaluation with all the trackers that respond to it. If we
// listened ourselves and requested rule evaluation before another tracker
// received the notification, our conditions would be evaluated based on the
// new URL while the other tracker's conditions would still be evaluated based
// on the previous URL.
void OnWebContentsNavigation(content::WebContents* contents,
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params);
// Inserts the currently-matching CSS selectors into |css_selectors|. We use
// hash_set for maximally efficient lookup.
void GetMatchingCssSelectors(content::WebContents* contents,
base::hash_set<std::string>* css_selectors);
// TODO(wittman): Remove once DeclarativeChromeContentRulesRegistry no longer
// depends on concrete condition implementations. At that point
// DeclarativeChromeContentRulesRegistryTest.ActiveRulesDoesntGrow will be
// able to use a test condition object and not need to depend on force setting
// matching CSS seleectors.
void UpdateMatchingCssSelectorsForTesting(
content::WebContents* contents,
const std::vector<std::string>& matching_css_selectors);
private:
class PerWebContentsTracker;
// Instantiate a PerWebContentsTracker watching |contents|.
void CreatePerWebContentsTracker(content::WebContents* contents);
// content::NotificationObserver implementation.
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override;
// If the renderer process is associated with our browser context, tells it
// what page attributes to watch for using an ExtensionMsg_WatchPages.
void InstructRenderProcessIfManagingBrowserContext(
content::RenderProcessHost* process);
// Called by PerWebContentsTracker on destruction.
void RemovePerWebContentsTracker(PerWebContentsTracker* tracker);
// The context whose state we're monitoring for evaluation.
content::BrowserContext* context_;
// All CSS selectors that are watched for by any rule's conditions. This
// vector is sorted by construction.
std::vector<std::string> watched_css_selectors_;
// Maps WebContents to the tracker for that WebContents
// state. PerWebContentsTracker owns itself, so this pointer is weak.
std::map<content::WebContents*, PerWebContentsTracker*>
per_web_contents_tracker_;
// Weak.
DeclarativeContentCssConditionTrackerDelegate* delegate_;
// Manages our notification registrations.
content::NotificationRegistrar registrar_;
DISALLOW_COPY_AND_ASSIGN(DeclarativeContentCssConditionTracker);
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_CONTENT_DECLARATIVE_CONTENT_CSS_CONDITION_TRACKER_H_

@ -1,199 +0,0 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/api/declarative_content/declarative_content_css_condition_tracker.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/run_loop.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "extensions/common/extension_messages.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
using testing::UnorderedElementsAreArray;
class DeclarativeContentCssConditionTrackerTest : public testing::Test {
public:
DeclarativeContentCssConditionTrackerTest()
: profile_(new TestingProfile),
delegate_(web_contents_) {
}
~DeclarativeContentCssConditionTrackerTest() override {
web_contents_.clear();
// MockRenderProcessHosts are deleted from the message loop, and their
// deletion must complete before RenderViewHostTestEnabler's destructor is
// run.
base::RunLoop().RunUntilIdle();
}
protected:
class Delegate : public DeclarativeContentCssConditionTrackerDelegate {
public:
Delegate(const ScopedVector<content::WebContents>& web_contents)
: web_contents_(web_contents),
evaluation_requests_(0) {
}
int evaluation_requests() { return evaluation_requests_; }
// DeclarativeContentCssConditionTrackerDelegate:
void RequestEvaluation(content::WebContents* contents) override {
++evaluation_requests_;
}
bool ShouldManageConditionsForBrowserContext(
content::BrowserContext* context) override {
return true;
}
private:
// Reference to the test's WebContents vector.
const ScopedVector<content::WebContents>& web_contents_;
int evaluation_requests_;
DISALLOW_COPY_AND_ASSIGN(Delegate);
};
// Creates a new DeclarativeContentCssConditionTracker and retains ownership.
DeclarativeContentCssConditionTracker* MakeTracker() {
trackers_.push_back(
new DeclarativeContentCssConditionTracker(profile(), delegate()));
return trackers_.back();
}
// Creates a new WebContents and retains ownership.
content::WebContents* MakeTab() {
web_contents_.push_back(
content::WebContentsTester::CreateTestWebContents(profile_.get(),
nullptr));
return web_contents_.back();
}
// Expect an ExtensionMsg_WatchPages message in |sink| with |selectors| as the
// param, after invoking |func|.
template <class Func>
void ExpectWatchPagesMessage(IPC::TestSink& sink,
const std::set<std::string>& selectors,
const Func& func) {
sink.ClearMessages();
func();
EXPECT_EQ(1u, sink.message_count());
const IPC::Message* message =
sink.GetUniqueMessageMatching(ExtensionMsg_WatchPages::ID);
ASSERT_TRUE(message);
ExtensionMsg_WatchPages::Param params;
ExtensionMsg_WatchPages::Read(message, &params);
EXPECT_THAT(base::get<0>(params), UnorderedElementsAreArray(selectors));
}
// Sends an OnWatchedPageChange message to the tab.
void SendOnWatchedPageChangeMessage(
content::WebContents* tab,
const std::vector<std::string>& selectors) {
ExtensionHostMsg_OnWatchedPageChange page_change(tab->GetRoutingID(),
selectors);
content::MockRenderProcessHost* process =
static_cast<content::MockRenderProcessHost*>(
tab->GetRenderViewHost()->GetProcess());
EXPECT_TRUE(process->OnMessageReceived(page_change));
}
Profile* profile() { return profile_.get(); }
Delegate* delegate() { return &delegate_; }
private:
content::TestBrowserThreadBundle thread_bundle_;
// Enables MockRenderProcessHosts.
content::RenderViewHostTestEnabler render_view_host_test_enabler_;
const scoped_ptr<TestingProfile> profile_;
ScopedVector<DeclarativeContentCssConditionTracker> trackers_;
ScopedVector<content::WebContents> web_contents_;
Delegate delegate_;
DISALLOW_COPY_AND_ASSIGN(DeclarativeContentCssConditionTrackerTest);
};
// Tests the basic flow of operations on the
// DeclarativeContentCssConditionTracker.
TEST_F(DeclarativeContentCssConditionTrackerTest, Basic) {
DeclarativeContentCssConditionTracker *tracker = MakeTracker();
int expected_evaluation_requests = 0;
content::WebContents* const tab = MakeTab();
tracker->TrackForWebContents(tab);
EXPECT_EQ(expected_evaluation_requests, delegate()->evaluation_requests());
content::MockRenderProcessHost* process =
static_cast<content::MockRenderProcessHost*>(
tab->GetRenderViewHost()->GetProcess());
// Check that calling SetWatchedCssSelectors sends a WatchPages message with
// the selectors to the tab's RenderProcessHost.
std::set<std::string> watched_selectors;
watched_selectors.insert("a");
watched_selectors.insert("div");
ExpectWatchPagesMessage(process->sink(), watched_selectors,
[&tracker, &watched_selectors]() {
tracker->SetWatchedCssSelectors(watched_selectors);
});
EXPECT_EQ(expected_evaluation_requests, delegate()->evaluation_requests());
// Check that receiving an OnWatchedPageChange message from the tab results in
// a request for condition evaluation.
const std::vector<std::string> matched_selectors(1, "div");
SendOnWatchedPageChangeMessage(tab, matched_selectors);
EXPECT_EQ(++expected_evaluation_requests, delegate()->evaluation_requests());
// Check that GetMatchingCssSelectors produces the same matched selectors as
// were sent by the OnWatchedPageChange message.
base::hash_set<std::string> matching_selectors;
tracker->GetMatchingCssSelectors(tab, &matching_selectors);
EXPECT_THAT(matching_selectors,
UnorderedElementsAreArray(matched_selectors));
EXPECT_EQ(expected_evaluation_requests, delegate()->evaluation_requests());
// Check that an in-page navigation has no effect on the matching selectors.
{
content::LoadCommittedDetails details;
details.is_in_page = true;
content::FrameNavigateParams params;
tracker->OnWebContentsNavigation(tab, details, params);
matching_selectors.clear();
tracker->GetMatchingCssSelectors(tab, &matching_selectors);
EXPECT_THAT(matching_selectors,
UnorderedElementsAreArray(matched_selectors));
EXPECT_EQ(expected_evaluation_requests, delegate()->evaluation_requests());
}
// Check that a non in-page navigation clears the matching selectors and
// requests condition evaluation.
{
content::LoadCommittedDetails details;
details.is_in_page = false;
content::FrameNavigateParams params;
tracker->OnWebContentsNavigation(tab, details, params);
matching_selectors.clear();
tracker->GetMatchingCssSelectors(tab, &matching_selectors);
EXPECT_TRUE(matching_selectors.empty());
EXPECT_EQ(++expected_evaluation_requests,
delegate()->evaluation_requests());
}
}
} // namespace extensions

@ -264,6 +264,8 @@ bool TabHelper::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_ContentScriptsExecuting,
OnContentScriptsExecuting)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_OnWatchedPageChange,
OnWatchedPageChange)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
@ -431,6 +433,15 @@ void TabHelper::OnContentScriptsExecuting(
OnScriptsExecuted(web_contents(), executing_scripts_map, on_url));
}
void TabHelper::OnWatchedPageChange(
const std::vector<std::string>& css_selectors) {
if (ExtensionSystem::Get(profile_)->extension_service() &&
RulesRegistryService::Get(profile_)) {
RulesRegistryService::Get(profile_)->content_rules_registry()->Apply(
web_contents(), css_selectors);
}
}
void TabHelper::OnDetailedConsoleMessageAdded(
const base::string16& message,
const base::string16& source,

@ -7,6 +7,7 @@
#include <set>
#include <string>
#include <vector>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
@ -170,6 +171,7 @@ class TabHelper : public content::WebContentsObserver,
void OnContentScriptsExecuting(
const ScriptExecutionObserver::ExecutingScriptsMap& extension_ids,
const GURL& on_url);
void OnWatchedPageChange(const std::vector<std::string>& css_selectors);
void OnDetailedConsoleMessageAdded(const base::string16& message,
const base::string16& source,
const StackTrace& stack_trace,

@ -213,8 +213,6 @@
'browser/extensions/api/declarative_content/content_condition.h',
'browser/extensions/api/declarative_content/content_constants.cc',
'browser/extensions/api/declarative_content/content_constants.h',
'browser/extensions/api/declarative_content/declarative_content_css_condition_tracker.cc',
'browser/extensions/api/declarative_content/declarative_content_css_condition_tracker.h',
'browser/extensions/api/desktop_capture/desktop_capture_api.cc',
'browser/extensions/api/desktop_capture/desktop_capture_api.h',
'browser/extensions/api/desktop_capture/desktop_capture_base.cc',

@ -656,7 +656,6 @@
'browser/extensions/api/declarative_content/chrome_content_rules_registry_unittest.cc',
'browser/extensions/api/declarative_content/content_action_unittest.cc',
'browser/extensions/api/declarative_content/content_condition_unittest.cc',
'browser/extensions/api/declarative_content/declarative_content_css_condition_tracker_unittest.cc',
'browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc',
'browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc',
'browser/extensions/api/developer_private/developer_private_api_unittest.cc',

@ -36,6 +36,12 @@ class ContentRulesRegistry : public RulesRegistry {
cache_delegate,
rules_registry_id) {}
// Applies all content rules given an update (CSS match change or
// page navigation, for now) from the renderer.
virtual void Apply(
content::WebContents* contents,
const std::vector<std::string>& matching_css_selectors) = 0;
// Applies all content rules given that a tab was just navigated.
virtual void DidNavigateMainFrame(
content::WebContents* tab,