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.
4
5 #include "chrome/browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker.h"
6
7 #include <set>
8 #include <vector>
9
10 #include "base/macros.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/stl_util.h"
13 #include "base/test/values_test_util.h"
14 #include "chrome/browser/extensions/api/declarative_content/content_predicate_evaluator.h"
15 #include "chrome/browser/extensions/api/declarative_content/declarative_content_condition_tracker_test.h"
16 #include "components/url_matcher/url_matcher.h"
17 #include "content/public/browser/navigation_controller.h"
18 #include "content/public/browser/web_contents.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20
21 namespace extensions {
22
23 using testing::ElementsAre;
24 using testing::HasSubstr;
25 using testing::UnorderedElementsAre;
26 using testing::UnorderedElementsAreArray;
27
28 class DeclarativeContentPageUrlConditionTrackerTest
29 : public DeclarativeContentConditionTrackerTest {
30 protected:
31 class Delegate : public ContentPredicateEvaluator::Delegate {
32 public:
Delegate()33 Delegate() {}
34
evaluation_requests()35 std::set<content::WebContents*>& evaluation_requests() {
36 return evaluation_requests_;
37 }
38
39 // ContentPredicateEvaluator::Delegate:
RequestEvaluation(content::WebContents * contents)40 void RequestEvaluation(content::WebContents* contents) override {
41 EXPECT_FALSE(base::Contains(evaluation_requests_, contents));
42 evaluation_requests_.insert(contents);
43 }
44
ShouldManageConditionsForBrowserContext(content::BrowserContext * context)45 bool ShouldManageConditionsForBrowserContext(
46 content::BrowserContext* context) override {
47 return true;
48 }
49
50 private:
51 std::set<content::WebContents*> evaluation_requests_;
52
53 DISALLOW_COPY_AND_ASSIGN(Delegate);
54 };
55
DeclarativeContentPageUrlConditionTrackerTest()56 DeclarativeContentPageUrlConditionTrackerTest()
57 : tracker_(&delegate_) {
58 }
59
60 // Creates a predicate with appropriate expectations of success.
CreatePredicate(const std::string & value)61 std::unique_ptr<const ContentPredicate> CreatePredicate(
62 const std::string& value) {
63 std::unique_ptr<const ContentPredicate> predicate;
64 CreatePredicateImpl(value, &predicate);
65 return predicate;
66 }
67
LoadURL(content::WebContents * tab,const GURL & url)68 void LoadURL(content::WebContents* tab, const GURL& url) {
69 tab->GetController().LoadURL(url, content::Referrer(),
70 ui::PAGE_TRANSITION_LINK, std::string());
71 }
72
73 Delegate delegate_;
74 DeclarativeContentPageUrlConditionTracker tracker_;
75
76 private:
77 // This function exists to work around the gtest limitation that functions
78 // with fatal assertions must return void.
CreatePredicateImpl(const std::string & value,std::unique_ptr<const ContentPredicate> * predicate)79 void CreatePredicateImpl(const std::string& value,
80 std::unique_ptr<const ContentPredicate>* predicate) {
81 std::string error;
82 *predicate = tracker_.CreatePredicate(
83 nullptr, *base::test::ParseJsonDeprecated(value), &error);
84 EXPECT_EQ("", error);
85 ASSERT_TRUE(*predicate);
86 }
87
88 DISALLOW_COPY_AND_ASSIGN(DeclarativeContentPageUrlConditionTrackerTest);
89 };
90
TEST(DeclarativeContentPageUrlPredicateTest,WrongPageUrlDatatype)91 TEST(DeclarativeContentPageUrlPredicateTest, WrongPageUrlDatatype) {
92 url_matcher::URLMatcher matcher;
93 std::string error;
94 std::unique_ptr<DeclarativeContentPageUrlPredicate> predicate =
95 DeclarativeContentPageUrlPredicate::Create(
96 nullptr, matcher.condition_factory(),
97 *base::test::ParseJsonDeprecated("[]"), &error);
98 EXPECT_THAT(error, HasSubstr("invalid type"));
99 EXPECT_FALSE(predicate);
100
101 EXPECT_TRUE(matcher.IsEmpty()) << "Errors shouldn't add URL conditions";
102 }
103
TEST(DeclarativeContentPageUrlPredicateTest,PageUrlPredicate)104 TEST(DeclarativeContentPageUrlPredicateTest, PageUrlPredicate) {
105 url_matcher::URLMatcher matcher;
106 std::string error;
107 std::unique_ptr<DeclarativeContentPageUrlPredicate> predicate =
108 DeclarativeContentPageUrlPredicate::Create(
109 nullptr, matcher.condition_factory(),
110 *base::test::ParseJsonDeprecated("{\"hostSuffix\": \"example.com\"}"),
111 &error);
112 EXPECT_EQ("", error);
113 ASSERT_TRUE(predicate);
114
115 url_matcher::URLMatcherConditionSet::Vector all_new_condition_sets;
116 all_new_condition_sets.push_back(predicate->url_matcher_condition_set());
117 matcher.AddConditionSets(all_new_condition_sets);
118 EXPECT_FALSE(matcher.IsEmpty());
119
120 EXPECT_THAT(matcher.MatchURL(GURL("http://google.com/")),
121 ElementsAre(/*empty*/));
122 std::set<url_matcher::URLMatcherConditionSet::ID> page_url_matches =
123 matcher.MatchURL(GURL("http://www.example.com/foobar"));
124 EXPECT_THAT(
125 page_url_matches,
126 ElementsAre(predicate->url_matcher_condition_set()->id()));
127 }
128
129 // Tests that adding and removing condition sets trigger evaluation requests for
130 // the matching WebContents.
TEST_F(DeclarativeContentPageUrlConditionTrackerTest,AddAndRemovePredicates)131 TEST_F(DeclarativeContentPageUrlConditionTrackerTest, AddAndRemovePredicates) {
132 // Create four tabs.
133 std::vector<std::unique_ptr<content::WebContents>> tabs;
134 for (int i = 0; i < 4; ++i) {
135 tabs.push_back(MakeTab());
136 delegate_.evaluation_requests().clear();
137 tracker_.TrackForWebContents(tabs.back().get());
138 EXPECT_THAT(delegate_.evaluation_requests(),
139 UnorderedElementsAre(tabs.back().get()));
140 }
141
142 // Navigate three of them to URLs that will match with predicats we're about
143 // to add.
144 LoadURL(tabs[0].get(), GURL("http://test1/"));
145 LoadURL(tabs[1].get(), GURL("http://test2/"));
146 LoadURL(tabs[2].get(), GURL("http://test3/"));
147
148 std::vector<std::unique_ptr<const ContentPredicate>> predicates;
149 std::string error;
150 predicates.push_back(CreatePredicate("{\"hostPrefix\": \"test1\"}"));
151 predicates.push_back(CreatePredicate("{\"hostPrefix\": \"test2\"}"));
152 predicates.push_back(CreatePredicate("{\"hostPrefix\": \"test3\"}"));
153
154 // Add the predicates in two groups: (0, 1) and (2).
155 delegate_.evaluation_requests().clear();
156 std::map<const void*, std::vector<const ContentPredicate*>> predicate_groups;
157 const void* const group1 = GeneratePredicateGroupID();
158 predicate_groups[group1].push_back(predicates[0].get());
159 predicate_groups[group1].push_back(predicates[1].get());
160 const void* const group2 = GeneratePredicateGroupID();
161 predicate_groups[group2].push_back(predicates[2].get());
162 tracker_.TrackPredicates(predicate_groups);
163 EXPECT_THAT(
164 delegate_.evaluation_requests(),
165 UnorderedElementsAre(tabs[0].get(), tabs[1].get(), tabs[2].get()));
166
167 // Check that the predicates evaluate as expected for the tabs.
168 EXPECT_TRUE(tracker_.EvaluatePredicate(predicates[0].get(), tabs[0].get()));
169 EXPECT_FALSE(tracker_.EvaluatePredicate(predicates[0].get(), tabs[1].get()));
170 EXPECT_FALSE(tracker_.EvaluatePredicate(predicates[0].get(), tabs[2].get()));
171 EXPECT_FALSE(tracker_.EvaluatePredicate(predicates[0].get(), tabs[3].get()));
172
173 EXPECT_FALSE(tracker_.EvaluatePredicate(predicates[1].get(), tabs[0].get()));
174 EXPECT_TRUE(tracker_.EvaluatePredicate(predicates[1].get(), tabs[1].get()));
175 EXPECT_FALSE(tracker_.EvaluatePredicate(predicates[1].get(), tabs[2].get()));
176 EXPECT_FALSE(tracker_.EvaluatePredicate(predicates[1].get(), tabs[3].get()));
177
178 EXPECT_FALSE(tracker_.EvaluatePredicate(predicates[2].get(), tabs[0].get()));
179 EXPECT_FALSE(tracker_.EvaluatePredicate(predicates[2].get(), tabs[1].get()));
180 EXPECT_TRUE(tracker_.EvaluatePredicate(predicates[2].get(), tabs[2].get()));
181 EXPECT_FALSE(tracker_.EvaluatePredicate(predicates[2].get(), tabs[3].get()));
182
183 // Remove the first group of predicates.
184 delegate_.evaluation_requests().clear();
185 tracker_.StopTrackingPredicates(std::vector<const void*>(1, group1));
186 EXPECT_THAT(delegate_.evaluation_requests(),
187 UnorderedElementsAre(tabs[0].get(), tabs[1].get()));
188
189 // Remove the second group of predicates.
190 delegate_.evaluation_requests().clear();
191 tracker_.StopTrackingPredicates(std::vector<const void*>(1, group2));
192 EXPECT_THAT(delegate_.evaluation_requests(),
193 UnorderedElementsAre(tabs[2].get()));
194 }
195
196 // Tests that tracking WebContents triggers evaluation requests for matching
197 // rules.
TEST_F(DeclarativeContentPageUrlConditionTrackerTest,TrackWebContents)198 TEST_F(DeclarativeContentPageUrlConditionTrackerTest, TrackWebContents) {
199 std::string error;
200 std::unique_ptr<const ContentPredicate> predicate =
201 CreatePredicate("{\"hostPrefix\": \"test1\"}");
202
203 delegate_.evaluation_requests().clear();
204 std::map<const void*, std::vector<const ContentPredicate*>> predicates;
205 const void* const group = GeneratePredicateGroupID();
206 predicates[group].push_back(predicate.get());
207 tracker_.TrackPredicates(predicates);
208 EXPECT_TRUE(delegate_.evaluation_requests().empty());
209
210 const std::unique_ptr<content::WebContents> matching_tab = MakeTab();
211 LoadURL(matching_tab.get(), GURL("http://test1/"));
212
213 tracker_.TrackForWebContents(matching_tab.get());
214 EXPECT_THAT(delegate_.evaluation_requests(),
215 UnorderedElementsAre(matching_tab.get()));
216
217 delegate_.evaluation_requests().clear();
218 const std::unique_ptr<content::WebContents> non_matching_tab = MakeTab();
219 tracker_.TrackForWebContents(non_matching_tab.get());
220 EXPECT_THAT(delegate_.evaluation_requests(),
221 UnorderedElementsAre(non_matching_tab.get()));
222
223 delegate_.evaluation_requests().clear();
224 tracker_.StopTrackingPredicates(std::vector<const void*>(1, group));
225 EXPECT_THAT(delegate_.evaluation_requests(),
226 UnorderedElementsAre(matching_tab.get()));
227 }
228
229 // Tests that notifying WebContents navigation triggers evaluation requests for
230 // matching rules.
TEST_F(DeclarativeContentPageUrlConditionTrackerTest,NotifyWebContentsNavigation)231 TEST_F(DeclarativeContentPageUrlConditionTrackerTest,
232 NotifyWebContentsNavigation) {
233 std::string error;
234 std::unique_ptr<const ContentPredicate> predicate =
235 CreatePredicate("{\"hostPrefix\": \"test1\"}");
236
237 delegate_.evaluation_requests().clear();
238 std::map<const void*, std::vector<const ContentPredicate*>> predicates;
239 const void* const group = GeneratePredicateGroupID();
240 predicates[group].push_back(predicate.get());
241 tracker_.TrackPredicates(predicates);
242 EXPECT_TRUE(delegate_.evaluation_requests().empty());
243
244 const std::unique_ptr<content::WebContents> tab = MakeTab();
245 tracker_.TrackForWebContents(tab.get());
246 EXPECT_THAT(delegate_.evaluation_requests(),
247 UnorderedElementsAre(tab.get()));
248
249 // Check that navigation notification to a matching URL results in an
250 // evaluation request.
251 LoadURL(tab.get(), GURL("http://test1/"));
252 delegate_.evaluation_requests().clear();
253 tracker_.OnWebContentsNavigation(tab.get(), nullptr);
254 EXPECT_THAT(delegate_.evaluation_requests(),
255 UnorderedElementsAre(tab.get()));
256
257 // Check that navigation notification from a matching URL to another matching
258 // URL results in an evaluation request.
259 LoadURL(tab.get(), GURL("http://test1/a"));
260 delegate_.evaluation_requests().clear();
261 tracker_.OnWebContentsNavigation(tab.get(), nullptr);
262 EXPECT_THAT(delegate_.evaluation_requests(),
263 UnorderedElementsAre(tab.get()));
264
265 // Check that navigation notification from a matching URL to a non-matching
266 // URL results in an evaluation request.
267 delegate_.evaluation_requests().clear();
268 LoadURL(tab.get(), GURL("http://test2/"));
269 tracker_.OnWebContentsNavigation(tab.get(), nullptr);
270 EXPECT_THAT(delegate_.evaluation_requests(),
271 UnorderedElementsAre(tab.get()));
272
273 // Check that navigation notification from a non-matching URL to another
274 // non-matching URL results in an evaluation request.
275 delegate_.evaluation_requests().clear();
276 LoadURL(tab.get(), GURL("http://test2/a"));
277 tracker_.OnWebContentsNavigation(tab.get(), nullptr);
278 EXPECT_THAT(delegate_.evaluation_requests(),
279 UnorderedElementsAre(tab.get()));
280
281 delegate_.evaluation_requests().clear();
282 tracker_.StopTrackingPredicates(std::vector<const void*>(1, group));
283 EXPECT_THAT(delegate_.evaluation_requests(),
284 UnorderedElementsAre(/* empty */));
285 }
286
287 } // namespace extensions
288