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() {
39 bool DeclarativeContentCssPredicate::Evaluate(
40 const base::hash_set
<std::string
>& matched_css_selectors
) const {
41 // All attributes must be fulfilled for a fulfilled condition.
42 for (const std::string
& css_selector
: css_selectors_
) {
43 if (!ContainsKey(matched_css_selectors
, css_selector
))
51 scoped_ptr
<DeclarativeContentCssPredicate
>
52 DeclarativeContentCssPredicate::Create(
53 const base::Value
& value
,
55 std::vector
<std::string
> css_rules
;
56 const base::ListValue
* css_rules_value
= nullptr;
57 if (value
.GetAsList(&css_rules_value
)) {
58 for (size_t i
= 0; i
< css_rules_value
->GetSize(); ++i
) {
60 if (!css_rules_value
->GetString(i
, &css_rule
)) {
61 *error
= base::StringPrintf(kInvalidTypeOfParameter
,
62 declarative_content_constants::kCss
);
63 return scoped_ptr
<DeclarativeContentCssPredicate
>();
65 css_rules
.push_back(css_rule
);
68 *error
= base::StringPrintf(kInvalidTypeOfParameter
,
69 declarative_content_constants::kCss
);
70 return scoped_ptr
<DeclarativeContentCssPredicate
>();
73 return !css_rules
.empty() ?
74 make_scoped_ptr(new DeclarativeContentCssPredicate(css_rules
)) :
75 scoped_ptr
<DeclarativeContentCssPredicate
>();
78 DeclarativeContentCssPredicate::DeclarativeContentCssPredicate(
79 const std::vector
<std::string
>& css_selectors
)
80 : css_selectors_(css_selectors
) {
81 DCHECK(!css_selectors
.empty());
85 // PerWebContentsTracker
88 DeclarativeContentCssConditionTracker::PerWebContentsTracker::
89 PerWebContentsTracker(
90 content::WebContents
* contents
,
91 const RequestEvaluationCallback
& request_evaluation
,
92 const WebContentsDestroyedCallback
& web_contents_destroyed
)
93 : WebContentsObserver(contents
),
94 request_evaluation_(request_evaluation
),
95 web_contents_destroyed_(web_contents_destroyed
) {
98 DeclarativeContentCssConditionTracker::PerWebContentsTracker::
99 ~PerWebContentsTracker() {
102 scoped_ptr
<DeclarativeContentCssPredicate
>
103 DeclarativeContentCssConditionTracker::CreatePredicate(
104 const Extension
* extension
,
105 const base::Value
& value
,
106 std::string
* error
) {
107 return DeclarativeContentCssPredicate::Create(value
, error
);
110 void DeclarativeContentCssConditionTracker::PerWebContentsTracker::
111 OnWebContentsNavigation(const content::LoadCommittedDetails
& details
,
112 const content::FrameNavigateParams
& params
) {
113 if (details
.is_in_page
) {
114 // Within-page navigations don't change the set of elements that
115 // exist, and we only support filtering on the top-level URL, so
116 // this can't change which rules match.
120 // Top-level navigation produces a new document. Initially, the
121 // document's empty, so no CSS rules match. The renderer will send
122 // an ExtensionHostMsg_OnWatchedPageChange later if any CSS rules
124 matching_css_selectors_
.clear();
125 request_evaluation_
.Run(web_contents());
128 void DeclarativeContentCssConditionTracker::PerWebContentsTracker::
129 UpdateMatchingCssSelectorsForTesting(
130 const std::vector
<std::string
>& matching_css_selectors
) {
131 matching_css_selectors_
= matching_css_selectors
;
132 request_evaluation_
.Run(web_contents());
136 DeclarativeContentCssConditionTracker::PerWebContentsTracker::
138 const IPC::Message
& message
) {
140 IPC_BEGIN_MESSAGE_MAP(PerWebContentsTracker
, message
)
141 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OnWatchedPageChange
,
143 IPC_MESSAGE_UNHANDLED(handled
= false)
144 IPC_END_MESSAGE_MAP()
148 void DeclarativeContentCssConditionTracker::PerWebContentsTracker::
149 WebContentsDestroyed() {
150 web_contents_destroyed_
.Run(web_contents());
154 DeclarativeContentCssConditionTracker::PerWebContentsTracker::
156 const std::vector
<std::string
>& css_selectors
) {
157 matching_css_selectors_
= css_selectors
;
158 request_evaluation_
.Run(web_contents());
162 // DeclarativeContentCssConditionTracker
165 DeclarativeContentCssConditionTracker::DeclarativeContentCssConditionTracker(
166 content::BrowserContext
* context
,
167 DeclarativeContentConditionTrackerDelegate
* delegate
)
169 delegate_(delegate
) {
171 content::NOTIFICATION_RENDERER_PROCESS_CREATED
,
172 content::NotificationService::AllBrowserContextsAndSources());
175 DeclarativeContentCssConditionTracker::
176 ~DeclarativeContentCssConditionTracker() {}
178 // We use the sorted propery of the set for equality checks with
179 // watched_css_selectors_, which is guaranteed to be sorted because it's set
180 // from the set contents.
181 void DeclarativeContentCssConditionTracker::SetWatchedCssSelectors(
182 const std::set
<std::string
>& new_watched_css_selectors
) {
183 if (new_watched_css_selectors
.size() != watched_css_selectors_
.size() ||
184 !std::equal(new_watched_css_selectors
.begin(),
185 new_watched_css_selectors
.end(),
186 watched_css_selectors_
.begin())) {
187 watched_css_selectors_
.assign(new_watched_css_selectors
.begin(),
188 new_watched_css_selectors
.end());
190 for (content::RenderProcessHost::iterator
it(
191 content::RenderProcessHost::AllHostsIterator());
194 InstructRenderProcessIfManagingBrowserContext(it
.GetCurrentValue());
199 void DeclarativeContentCssConditionTracker::TrackForWebContents(
200 content::WebContents
* contents
) {
201 per_web_contents_tracker_
[contents
] =
202 make_linked_ptr(new PerWebContentsTracker(
204 base::Bind(&DeclarativeContentConditionTrackerDelegate::
206 base::Unretained(delegate_
)),
207 base::Bind(&DeclarativeContentCssConditionTracker::
208 DeletePerWebContentsTracker
,
209 base::Unretained(this))));
210 // Note: the condition is always false until we receive OnWatchedPageChange,
211 // so there's no need to evaluate it here.
214 void DeclarativeContentCssConditionTracker::OnWebContentsNavigation(
215 content::WebContents
* contents
,
216 const content::LoadCommittedDetails
& details
,
217 const content::FrameNavigateParams
& params
) {
218 DCHECK(ContainsKey(per_web_contents_tracker_
, contents
));
219 per_web_contents_tracker_
[contents
]->OnWebContentsNavigation(details
, params
);
222 void DeclarativeContentCssConditionTracker::GetMatchingCssSelectors(
223 content::WebContents
* contents
,
224 base::hash_set
<std::string
>* css_selectors
) {
225 DCHECK(ContainsKey(per_web_contents_tracker_
, contents
));
226 const std::vector
<std::string
>& matching_css_selectors
=
227 per_web_contents_tracker_
[contents
]->matching_css_selectors();
228 css_selectors
->insert(matching_css_selectors
.begin(),
229 matching_css_selectors
.end());
232 void DeclarativeContentCssConditionTracker::
233 UpdateMatchingCssSelectorsForTesting(
234 content::WebContents
* contents
,
235 const std::vector
<std::string
>& matching_css_selectors
) {
236 DCHECK(ContainsKey(per_web_contents_tracker_
, contents
));
237 per_web_contents_tracker_
[contents
]->
238 UpdateMatchingCssSelectorsForTesting(matching_css_selectors
);
241 void DeclarativeContentCssConditionTracker::Observe(
243 const content::NotificationSource
& source
,
244 const content::NotificationDetails
& details
) {
246 case content::NOTIFICATION_RENDERER_PROCESS_CREATED
: {
247 content::RenderProcessHost
* process
=
248 content::Source
<content::RenderProcessHost
>(source
).ptr();
249 InstructRenderProcessIfManagingBrowserContext(process
);
255 void DeclarativeContentCssConditionTracker::
256 InstructRenderProcessIfManagingBrowserContext(
257 content::RenderProcessHost
* process
) {
258 if (delegate_
->ShouldManageConditionsForBrowserContext(
259 process
->GetBrowserContext())) {
260 process
->Send(new ExtensionMsg_WatchPages(watched_css_selectors_
));
264 void DeclarativeContentCssConditionTracker::DeletePerWebContentsTracker(
265 content::WebContents
* contents
) {
266 DCHECK(ContainsKey(per_web_contents_tracker_
, contents
));
267 per_web_contents_tracker_
.erase(contents
);
270 } // namespace extensions