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"
9 #include "base/stl_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/values.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/extensions/api/declarative_content/content_constants.h"
14 #include "chrome/browser/extensions/api/declarative_content/declarative_content_condition_tracker_delegate.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "content/public/browser/navigation_details.h"
17 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/notification_source.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "extensions/common/extension_messages.h"
21 #include "ipc/ipc_message.h"
22 #include "ipc/ipc_message_macros.h"
24 namespace extensions
{
28 const char kInvalidTypeOfParameter
[] = "Attribute '%s' has an invalid type";
33 // DeclarativeContentCssPredicate
36 DeclarativeContentCssPredicate::~DeclarativeContentCssPredicate() {
40 scoped_ptr
<DeclarativeContentCssPredicate
>
41 DeclarativeContentCssPredicate::Create(
42 const base::Value
& value
,
44 std::vector
<std::string
> css_rules
;
45 const base::ListValue
* css_rules_value
= nullptr;
46 if (value
.GetAsList(&css_rules_value
)) {
47 for (size_t i
= 0; i
< css_rules_value
->GetSize(); ++i
) {
49 if (!css_rules_value
->GetString(i
, &css_rule
)) {
50 *error
= base::StringPrintf(kInvalidTypeOfParameter
,
51 declarative_content_constants::kCss
);
52 return scoped_ptr
<DeclarativeContentCssPredicate
>();
54 css_rules
.push_back(css_rule
);
57 *error
= base::StringPrintf(kInvalidTypeOfParameter
,
58 declarative_content_constants::kCss
);
59 return scoped_ptr
<DeclarativeContentCssPredicate
>();
62 return !css_rules
.empty() ?
63 make_scoped_ptr(new DeclarativeContentCssPredicate(css_rules
)) :
64 scoped_ptr
<DeclarativeContentCssPredicate
>();
67 DeclarativeContentCssPredicate::DeclarativeContentCssPredicate(
68 const std::vector
<std::string
>& css_selectors
)
69 : css_selectors_(css_selectors
) {
70 DCHECK(!css_selectors
.empty());
74 // PerWebContentsTracker
77 DeclarativeContentCssConditionTracker::PerWebContentsTracker::
78 PerWebContentsTracker(
79 content::WebContents
* contents
,
80 const RequestEvaluationCallback
& request_evaluation
,
81 const WebContentsDestroyedCallback
& web_contents_destroyed
)
82 : WebContentsObserver(contents
),
83 request_evaluation_(request_evaluation
),
84 web_contents_destroyed_(web_contents_destroyed
) {
87 DeclarativeContentCssConditionTracker::PerWebContentsTracker::
88 ~PerWebContentsTracker() {
91 scoped_ptr
<DeclarativeContentCssPredicate
>
92 DeclarativeContentCssConditionTracker::CreatePredicate(
93 const Extension
* extension
,
94 const base::Value
& value
,
96 return DeclarativeContentCssPredicate::Create(value
, error
);
99 void DeclarativeContentCssConditionTracker::PerWebContentsTracker::
100 OnWebContentsNavigation(const content::LoadCommittedDetails
& details
,
101 const content::FrameNavigateParams
& params
) {
102 if (details
.is_in_page
) {
103 // Within-page navigations don't change the set of elements that
104 // exist, and we only support filtering on the top-level URL, so
105 // this can't change which rules match.
109 // Top-level navigation produces a new document. Initially, the
110 // document's empty, so no CSS rules match. The renderer will send
111 // an ExtensionHostMsg_OnWatchedPageChange later if any CSS rules
113 matching_css_selectors_
.clear();
114 request_evaluation_
.Run(web_contents());
117 void DeclarativeContentCssConditionTracker::PerWebContentsTracker::
118 UpdateMatchingCssSelectorsForTesting(
119 const std::vector
<std::string
>& matching_css_selectors
) {
120 matching_css_selectors_
.clear();
121 matching_css_selectors_
.insert(matching_css_selectors
.begin(),
122 matching_css_selectors
.end());
123 request_evaluation_
.Run(web_contents());
127 DeclarativeContentCssConditionTracker::PerWebContentsTracker::
129 const IPC::Message
& message
) {
131 IPC_BEGIN_MESSAGE_MAP(PerWebContentsTracker
, message
)
132 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OnWatchedPageChange
,
134 IPC_MESSAGE_UNHANDLED(handled
= false)
135 IPC_END_MESSAGE_MAP()
139 void DeclarativeContentCssConditionTracker::PerWebContentsTracker::
140 WebContentsDestroyed() {
141 web_contents_destroyed_
.Run(web_contents());
145 DeclarativeContentCssConditionTracker::PerWebContentsTracker::
147 const std::vector
<std::string
>& css_selectors
) {
148 matching_css_selectors_
.clear();
149 matching_css_selectors_
.insert(css_selectors
.begin(), css_selectors
.end());
150 request_evaluation_
.Run(web_contents());
154 // DeclarativeContentCssConditionTracker
157 DeclarativeContentCssConditionTracker::DeclarativeContentCssConditionTracker(
158 content::BrowserContext
* context
,
159 DeclarativeContentConditionTrackerDelegate
* delegate
)
161 delegate_(delegate
) {
163 content::NOTIFICATION_RENDERER_PROCESS_CREATED
,
164 content::NotificationService::AllBrowserContextsAndSources());
167 DeclarativeContentCssConditionTracker::
168 ~DeclarativeContentCssConditionTracker() {}
170 // We use the sorted propery of the set for equality checks with
171 // watched_css_selectors_, which is guaranteed to be sorted because it's set
172 // from the set contents.
173 void DeclarativeContentCssConditionTracker::SetWatchedCssSelectors(
174 const std::set
<std::string
>& new_watched_css_selectors
) {
175 if (new_watched_css_selectors
.size() != watched_css_selectors_
.size() ||
176 !std::equal(new_watched_css_selectors
.begin(),
177 new_watched_css_selectors
.end(),
178 watched_css_selectors_
.begin())) {
179 watched_css_selectors_
.assign(new_watched_css_selectors
.begin(),
180 new_watched_css_selectors
.end());
182 for (content::RenderProcessHost::iterator
it(
183 content::RenderProcessHost::AllHostsIterator());
186 InstructRenderProcessIfManagingBrowserContext(it
.GetCurrentValue());
191 void DeclarativeContentCssConditionTracker::TrackForWebContents(
192 content::WebContents
* contents
) {
193 per_web_contents_tracker_
[contents
] =
194 make_linked_ptr(new PerWebContentsTracker(
196 base::Bind(&DeclarativeContentConditionTrackerDelegate::
198 base::Unretained(delegate_
)),
199 base::Bind(&DeclarativeContentCssConditionTracker::
200 DeletePerWebContentsTracker
,
201 base::Unretained(this))));
202 // Note: the condition is always false until we receive OnWatchedPageChange,
203 // so there's no need to evaluate it here.
206 void DeclarativeContentCssConditionTracker::OnWebContentsNavigation(
207 content::WebContents
* contents
,
208 const content::LoadCommittedDetails
& details
,
209 const content::FrameNavigateParams
& params
) {
210 DCHECK(ContainsKey(per_web_contents_tracker_
, contents
));
211 per_web_contents_tracker_
[contents
]->OnWebContentsNavigation(details
, params
);
214 bool DeclarativeContentCssConditionTracker::EvaluatePredicate(
215 const DeclarativeContentCssPredicate
* predicate
,
216 content::WebContents
* contents
) const {
217 auto loc
= per_web_contents_tracker_
.find(contents
);
218 DCHECK(loc
!= per_web_contents_tracker_
.end());
219 const base::hash_set
<std::string
>& matching_css_selectors
=
220 loc
->second
->matching_css_selectors();
221 for (const std::string
& predicate_css_selector
: predicate
->css_selectors()) {
222 if (!ContainsKey(matching_css_selectors
, predicate_css_selector
))
229 void DeclarativeContentCssConditionTracker::
230 UpdateMatchingCssSelectorsForTesting(
231 content::WebContents
* contents
,
232 const std::vector
<std::string
>& matching_css_selectors
) {
233 DCHECK(ContainsKey(per_web_contents_tracker_
, contents
));
234 per_web_contents_tracker_
[contents
]->
235 UpdateMatchingCssSelectorsForTesting(matching_css_selectors
);
238 void DeclarativeContentCssConditionTracker::Observe(
240 const content::NotificationSource
& source
,
241 const content::NotificationDetails
& details
) {
243 case content::NOTIFICATION_RENDERER_PROCESS_CREATED
: {
244 content::RenderProcessHost
* process
=
245 content::Source
<content::RenderProcessHost
>(source
).ptr();
246 InstructRenderProcessIfManagingBrowserContext(process
);
252 void DeclarativeContentCssConditionTracker::
253 InstructRenderProcessIfManagingBrowserContext(
254 content::RenderProcessHost
* process
) {
255 if (delegate_
->ShouldManageConditionsForBrowserContext(
256 process
->GetBrowserContext())) {
257 process
->Send(new ExtensionMsg_WatchPages(watched_css_selectors_
));
261 void DeclarativeContentCssConditionTracker::DeletePerWebContentsTracker(
262 content::WebContents
* contents
) {
263 DCHECK(ContainsKey(per_web_contents_tracker_
, contents
));
264 per_web_contents_tracker_
.erase(contents
);
267 } // namespace extensions