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