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
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/declarative_content/content_condition.h"
7 #include "base/strings/stringprintf.h"
8 #include "base/values.h"
9 #include "chrome/browser/extensions/api/declarative_content/content_constants.h"
10 #include "components/url_matcher/url_matcher_factory.h"
11 #include "extensions/common/extension.h"
12 #include "extensions/common/permissions/permissions_data.h"
14 using url_matcher::URLMatcherConditionSet
;
16 namespace extensions
{
19 static URLMatcherConditionSet::ID g_next_id
= 0;
21 // TODO(jyasskin): improve error messaging to give more meaningful messages
22 // to the extension developer.
24 const char kExpectedDictionary
[] = "A condition has to be a dictionary.";
25 const char kConditionWithoutInstanceType
[] = "A condition had no instanceType";
26 const char kExpectedOtherConditionType
[] = "Expected a condition of type "
27 "declarativeContent.PageStateMatcher";
28 const char kUnknownConditionAttribute
[] = "Unknown condition attribute '%s'";
29 const char kInvalidTypeOfParameter
[] = "Attribute '%s' has an invalid type";
30 const char kIsBookmarkedRequiresBookmarkPermission
[] =
31 "Property 'isBookmarked' requires 'bookmarks' permission";
33 bool HasBookmarkAPIPermission(const Extension
* extension
) {
34 return extension
->permissions_data()->HasAPIPermission(
35 APIPermission::kBookmark
);
40 namespace keys
= declarative_content_constants
;
42 RendererContentMatchData::RendererContentMatchData() : is_bookmarked(false) {}
43 RendererContentMatchData::~RendererContentMatchData() {}
45 ContentCondition::ContentCondition(
46 scoped_refptr
<const Extension
> extension
,
47 scoped_refptr
<URLMatcherConditionSet
> url_matcher_condition_set
,
48 const std::vector
<std::string
>& css_selectors
,
49 BookmarkedStateMatch bookmarked_state
)
50 : extension_(extension
.Pass()),
51 url_matcher_condition_set_(url_matcher_condition_set
),
52 css_selectors_(css_selectors
),
53 bookmarked_state_(bookmarked_state
) {
54 DCHECK(url_matcher_condition_set_
.get());
57 ContentCondition::~ContentCondition() {}
59 bool ContentCondition::IsFulfilled(
60 const RendererContentMatchData
& renderer_data
) const {
61 if (!ContainsKey(renderer_data
.page_url_matches
,
62 url_matcher_condition_set_
->id()))
65 // All attributes must be fulfilled for a fulfilled condition.
66 for (const std::string
& css_selector
: css_selectors_
) {
67 if (!ContainsKey(renderer_data
.css_selectors
, css_selector
))
71 if (HasBookmarkAPIPermission(extension_
.get())) {
72 if ((bookmarked_state_
== BOOKMARKED
&& !renderer_data
.is_bookmarked
) ||
73 (bookmarked_state_
== NOT_BOOKMARKED
&& renderer_data
.is_bookmarked
)) {
82 scoped_ptr
<ContentCondition
> ContentCondition::Create(
83 scoped_refptr
<const Extension
> extension
,
84 url_matcher::URLMatcherConditionFactory
* url_matcher_condition_factory
,
85 const base::Value
& condition
,
87 const base::DictionaryValue
* condition_dict
= NULL
;
88 if (!condition
.GetAsDictionary(&condition_dict
)) {
89 *error
= kExpectedDictionary
;
90 return scoped_ptr
<ContentCondition
>();
93 // Verify that we are dealing with a Condition whose type we understand.
94 std::string instance_type
;
95 if (!condition_dict
->GetString(keys::kInstanceType
, &instance_type
)) {
96 *error
= kConditionWithoutInstanceType
;
97 return scoped_ptr
<ContentCondition
>();
99 if (instance_type
!= keys::kPageStateMatcherType
) {
100 *error
= kExpectedOtherConditionType
;
101 return scoped_ptr
<ContentCondition
>();
104 scoped_refptr
<URLMatcherConditionSet
> url_matcher_condition_set
;
105 std::vector
<std::string
> css_rules
;
106 BookmarkedStateMatch bookmarked_state
= DONT_CARE
;
108 for (base::DictionaryValue::Iterator
iter(*condition_dict
);
109 !iter
.IsAtEnd(); iter
.Advance()) {
110 const std::string
& condition_attribute_name
= iter
.key();
111 const base::Value
& condition_attribute_value
= iter
.value();
112 if (condition_attribute_name
== keys::kInstanceType
) {
114 } else if (condition_attribute_name
== keys::kPageUrl
) {
115 const base::DictionaryValue
* dict
= NULL
;
116 if (!condition_attribute_value
.GetAsDictionary(&dict
)) {
117 *error
= base::StringPrintf(kInvalidTypeOfParameter
,
118 condition_attribute_name
.c_str());
120 url_matcher_condition_set
=
121 url_matcher::URLMatcherFactory::CreateFromURLFilterDictionary(
122 url_matcher_condition_factory
, dict
, ++g_next_id
, error
);
124 } else if (condition_attribute_name
== keys::kCss
) {
125 const base::ListValue
* css_rules_value
= NULL
;
126 if (condition_attribute_value
.GetAsList(&css_rules_value
)) {
127 for (size_t i
= 0; i
< css_rules_value
->GetSize(); ++i
) {
128 std::string css_rule
;
129 if (!css_rules_value
->GetString(i
, &css_rule
)) {
130 *error
= base::StringPrintf(kInvalidTypeOfParameter
,
131 condition_attribute_name
.c_str());
134 css_rules
.push_back(css_rule
);
137 *error
= base::StringPrintf(kInvalidTypeOfParameter
,
138 condition_attribute_name
.c_str());
140 } else if (condition_attribute_name
== keys::kIsBookmarked
){
142 if (condition_attribute_value
.GetAsBoolean(&value
)) {
143 if (!HasBookmarkAPIPermission(extension
.get()))
144 *error
= kIsBookmarkedRequiresBookmarkPermission
;
146 bookmarked_state
= value
? BOOKMARKED
: NOT_BOOKMARKED
;
148 *error
= base::StringPrintf(kInvalidTypeOfParameter
,
149 condition_attribute_name
.c_str());
152 *error
= base::StringPrintf(kUnknownConditionAttribute
,
153 condition_attribute_name
.c_str());
156 return scoped_ptr
<ContentCondition
>();
159 if (!url_matcher_condition_set
.get()) {
160 URLMatcherConditionSet::Conditions url_matcher_conditions
;
161 url_matcher_conditions
.insert(
162 url_matcher_condition_factory
->CreateHostPrefixCondition(
164 url_matcher_condition_set
=
165 new URLMatcherConditionSet(++g_next_id
, url_matcher_conditions
);
167 return make_scoped_ptr(
168 new ContentCondition(extension
.Pass(), url_matcher_condition_set
,
169 css_rules
, bookmarked_state
));
172 } // namespace extensions