Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / api / declarative_content / declarative_content_css_condition_tracker.cc
blobbda1d589d65384ac9641c24b9a32169bf3698f76
1 // Copyright 2015 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.
5 #include "chrome/browser/extensions/api/declarative_content/declarative_content_css_condition_tracker.h"
7 #include "base/stl_util.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/values.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/api/declarative_content/content_constants.h"
12 #include "chrome/browser/extensions/api/declarative_content/content_predicate_evaluator.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "content/public/browser/navigation_details.h"
15 #include "content/public/browser/notification_service.h"
16 #include "content/public/browser/notification_source.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "extensions/common/extension_messages.h"
19 #include "ipc/ipc_message.h"
20 #include "ipc/ipc_message_macros.h"
22 namespace extensions {
24 namespace {
26 const char kInvalidTypeOfParameter[] = "Attribute '%s' has an invalid type";
28 } // namespace
31 // DeclarativeContentCssPredicate
34 DeclarativeContentCssPredicate::~DeclarativeContentCssPredicate() {
37 // static
38 scoped_ptr<DeclarativeContentCssPredicate>
39 DeclarativeContentCssPredicate::Create(
40 ContentPredicateEvaluator* evaluator,
41 const base::Value& value,
42 std::string* error) {
43 std::vector<std::string> css_rules;
44 const base::ListValue* css_rules_value = nullptr;
45 if (value.GetAsList(&css_rules_value)) {
46 for (size_t i = 0; i < css_rules_value->GetSize(); ++i) {
47 std::string css_rule;
48 if (!css_rules_value->GetString(i, &css_rule)) {
49 *error = base::StringPrintf(kInvalidTypeOfParameter,
50 declarative_content_constants::kCss);
51 return scoped_ptr<DeclarativeContentCssPredicate>();
53 css_rules.push_back(css_rule);
55 } else {
56 *error = base::StringPrintf(kInvalidTypeOfParameter,
57 declarative_content_constants::kCss);
58 return scoped_ptr<DeclarativeContentCssPredicate>();
61 return !css_rules.empty() ?
62 make_scoped_ptr(
63 new DeclarativeContentCssPredicate(evaluator, css_rules)) :
64 scoped_ptr<DeclarativeContentCssPredicate>();
67 ContentPredicateEvaluator*
68 DeclarativeContentCssPredicate::GetEvaluator() const {
69 return evaluator_;
72 DeclarativeContentCssPredicate::DeclarativeContentCssPredicate(
73 ContentPredicateEvaluator* evaluator,
74 const std::vector<std::string>& css_selectors)
75 : evaluator_(evaluator),
76 css_selectors_(css_selectors) {
77 DCHECK(!css_selectors.empty());
81 // PerWebContentsTracker
84 DeclarativeContentCssConditionTracker::PerWebContentsTracker::
85 PerWebContentsTracker(
86 content::WebContents* contents,
87 const RequestEvaluationCallback& request_evaluation,
88 const WebContentsDestroyedCallback& web_contents_destroyed)
89 : WebContentsObserver(contents),
90 request_evaluation_(request_evaluation),
91 web_contents_destroyed_(web_contents_destroyed) {
94 DeclarativeContentCssConditionTracker::PerWebContentsTracker::
95 ~PerWebContentsTracker() {
98 void DeclarativeContentCssConditionTracker::PerWebContentsTracker::
99 OnWebContentsNavigation(const content::LoadCommittedDetails& details,
100 const content::FrameNavigateParams& params) {
101 if (details.is_in_page) {
102 // Within-page navigations don't change the set of elements that
103 // exist, and we only support filtering on the top-level URL, so
104 // this can't change which rules match.
105 return;
108 // Top-level navigation produces a new document. Initially, the
109 // document's empty, so no CSS rules match. The renderer will send
110 // an ExtensionHostMsg_OnWatchedPageChange later if any CSS rules
111 // match.
112 matching_css_selectors_.clear();
113 request_evaluation_.Run(web_contents());
116 bool
117 DeclarativeContentCssConditionTracker::PerWebContentsTracker::
118 OnMessageReceived(
119 const IPC::Message& message) {
120 bool handled = true;
121 IPC_BEGIN_MESSAGE_MAP(PerWebContentsTracker, message)
122 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OnWatchedPageChange,
123 OnWatchedPageChange)
124 IPC_MESSAGE_UNHANDLED(handled = false)
125 IPC_END_MESSAGE_MAP()
126 return handled;
129 void DeclarativeContentCssConditionTracker::PerWebContentsTracker::
130 WebContentsDestroyed() {
131 web_contents_destroyed_.Run(web_contents());
134 void
135 DeclarativeContentCssConditionTracker::PerWebContentsTracker::
136 OnWatchedPageChange(
137 const std::vector<std::string>& css_selectors) {
138 matching_css_selectors_.clear();
139 matching_css_selectors_.insert(css_selectors.begin(), css_selectors.end());
140 request_evaluation_.Run(web_contents());
144 // DeclarativeContentCssConditionTracker
147 DeclarativeContentCssConditionTracker::DeclarativeContentCssConditionTracker(
148 Delegate* delegate)
149 : delegate_(delegate) {
150 registrar_.Add(this,
151 content::NOTIFICATION_RENDERER_PROCESS_CREATED,
152 content::NotificationService::AllBrowserContextsAndSources());
155 DeclarativeContentCssConditionTracker::
156 ~DeclarativeContentCssConditionTracker() {}
158 std::string DeclarativeContentCssConditionTracker::
159 GetPredicateApiAttributeName() const {
160 return declarative_content_constants::kCss;
163 scoped_ptr<const ContentPredicate> DeclarativeContentCssConditionTracker::
164 CreatePredicate(const Extension* extension,
165 const base::Value& value,
166 std::string* error) {
167 return DeclarativeContentCssPredicate::Create(this, value, error);
170 void DeclarativeContentCssConditionTracker::TrackPredicates(
171 const std::map<const void*, std::vector<const ContentPredicate*>>&
172 predicates) {
173 bool watched_selectors_updated = false;
174 for (const auto& group_predicates_pair : predicates) {
175 for (const ContentPredicate* predicate : group_predicates_pair.second) {
176 DCHECK_EQ(this, predicate->GetEvaluator());
177 const DeclarativeContentCssPredicate* typed_predicate =
178 static_cast<const DeclarativeContentCssPredicate*>(predicate);
179 tracked_predicates_[group_predicates_pair.first].push_back(
180 typed_predicate);
181 for (const std::string& selector : typed_predicate->css_selectors()) {
182 if (watched_css_selector_predicate_count_[selector]++ == 0)
183 watched_selectors_updated = true;
188 if (watched_selectors_updated)
189 UpdateRenderersWatchedCssSelectors(GetWatchedCssSelectors());
192 void DeclarativeContentCssConditionTracker::StopTrackingPredicates(
193 const std::vector<const void*>& predicate_groups) {
194 bool watched_selectors_updated = false;
195 for (const void* group : predicate_groups) {
196 auto loc = tracked_predicates_.find(group);
197 if (loc == tracked_predicates_.end())
198 continue;
199 for (const DeclarativeContentCssPredicate* predicate : loc->second) {
200 for (const std::string& selector : predicate->css_selectors()) {
201 std::map<std::string, int>::iterator loc =
202 watched_css_selector_predicate_count_.find(selector);
203 DCHECK(loc != watched_css_selector_predicate_count_.end());
204 if (--loc->second == 0) {
205 watched_css_selector_predicate_count_.erase(loc);
206 watched_selectors_updated = true;
210 tracked_predicates_.erase(group);
213 if (watched_selectors_updated)
214 UpdateRenderersWatchedCssSelectors(GetWatchedCssSelectors());
217 void DeclarativeContentCssConditionTracker::TrackForWebContents(
218 content::WebContents* contents) {
219 per_web_contents_tracker_[contents] =
220 make_linked_ptr(new PerWebContentsTracker(
221 contents,
222 base::Bind(&Delegate::RequestEvaluation, base::Unretained(delegate_)),
223 base::Bind(&DeclarativeContentCssConditionTracker::
224 DeletePerWebContentsTracker,
225 base::Unretained(this))));
226 // Note: the condition is always false until we receive OnWatchedPageChange,
227 // so there's no need to evaluate it here.
230 void DeclarativeContentCssConditionTracker::OnWebContentsNavigation(
231 content::WebContents* contents,
232 const content::LoadCommittedDetails& details,
233 const content::FrameNavigateParams& params) {
234 DCHECK(ContainsKey(per_web_contents_tracker_, contents));
235 per_web_contents_tracker_[contents]->OnWebContentsNavigation(details, params);
238 bool DeclarativeContentCssConditionTracker::EvaluatePredicate(
239 const ContentPredicate* predicate,
240 content::WebContents* tab) const {
241 DCHECK_EQ(this, predicate->GetEvaluator());
242 const DeclarativeContentCssPredicate* typed_predicate =
243 static_cast<const DeclarativeContentCssPredicate*>(predicate);
244 auto loc = per_web_contents_tracker_.find(tab);
245 DCHECK(loc != per_web_contents_tracker_.end());
246 const base::hash_set<std::string>& matching_css_selectors =
247 loc->second->matching_css_selectors();
248 for (const std::string& predicate_css_selector :
249 typed_predicate->css_selectors()) {
250 if (!ContainsKey(matching_css_selectors, predicate_css_selector))
251 return false;
254 return true;
257 void DeclarativeContentCssConditionTracker::Observe(
258 int type,
259 const content::NotificationSource& source,
260 const content::NotificationDetails& details) {
261 switch (type) {
262 case content::NOTIFICATION_RENDERER_PROCESS_CREATED: {
263 content::RenderProcessHost* process =
264 content::Source<content::RenderProcessHost>(source).ptr();
265 InstructRenderProcessIfManagingBrowserContext(process,
266 GetWatchedCssSelectors());
267 break;
272 void DeclarativeContentCssConditionTracker::
273 UpdateRenderersWatchedCssSelectors(
274 const std::vector<std::string>& watched_css_selectors) {
275 for (content::RenderProcessHost::iterator it(
276 content::RenderProcessHost::AllHostsIterator());
277 !it.IsAtEnd();
278 it.Advance()) {
279 InstructRenderProcessIfManagingBrowserContext(it.GetCurrentValue(),
280 watched_css_selectors);
284 std::vector<std::string> DeclarativeContentCssConditionTracker::
285 GetWatchedCssSelectors() const {
286 std::vector<std::string> selectors;
287 selectors.reserve(watched_css_selector_predicate_count_.size());
288 for (const std::pair<std::string, int>& selector_pair :
289 watched_css_selector_predicate_count_) {
290 selectors.push_back(selector_pair.first);
292 return selectors;
295 void DeclarativeContentCssConditionTracker::
296 InstructRenderProcessIfManagingBrowserContext(
297 content::RenderProcessHost* process,
298 std::vector<std::string> watched_css_selectors) {
299 if (delegate_->ShouldManageConditionsForBrowserContext(
300 process->GetBrowserContext())) {
301 process->Send(new ExtensionMsg_WatchPages(watched_css_selectors));
305 void DeclarativeContentCssConditionTracker::DeletePerWebContentsTracker(
306 content::WebContents* contents) {
307 DCHECK(ContainsKey(per_web_contents_tracker_, contents));
308 per_web_contents_tracker_.erase(contents);
311 } // namespace extensions