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.
4
5#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h"
6
7#include <set>
8
9#include "base/message_loop/message_loop.h"
10#include "base/test/values_test_util.h"
11#include "base/values.h"
12#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h"
13#include "components/url_matcher/url_matcher_constants.h"
14#include "content/public/browser/resource_request_info.h"
15#include "net/base/request_priority.h"
16#include "net/url_request/url_request_test_util.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19using url_matcher::URLMatcher;
20using url_matcher::URLMatcherConditionSet;
21
22namespace extensions {
23
24TEST(WebRequestConditionTest, CreateCondition) {
25  // Necessary for TestURLRequest.
26  base::MessageLoop message_loop(base::MessageLoop::TYPE_IO);
27  URLMatcher matcher;
28
29  std::string error;
30  scoped_ptr<WebRequestCondition> result;
31
32  // Test wrong condition name passed.
33  error.clear();
34  result = WebRequestCondition::Create(
35      NULL,
36      matcher.condition_factory(),
37      *base::test::ParseJson(
38           "{ \"invalid\": \"foobar\", \n"
39           "  \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
40           "}"),
41      &error);
42  EXPECT_FALSE(error.empty());
43  EXPECT_FALSE(result.get());
44
45  // Test wrong datatype in host_suffix.
46  error.clear();
47  result = WebRequestCondition::Create(
48      NULL,
49      matcher.condition_factory(),
50      *base::test::ParseJson(
51           "{ \n"
52           "  \"url\": [], \n"
53           "  \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
54           "}"),
55      &error);
56  EXPECT_FALSE(error.empty());
57  EXPECT_FALSE(result.get());
58
59  // Test success (can we support multiple criteria?)
60  error.clear();
61  result = WebRequestCondition::Create(
62      NULL,
63      matcher.condition_factory(),
64      *base::test::ParseJson(
65           "{ \n"
66           "  \"resourceType\": [\"main_frame\"], \n"
67           "  \"url\": { \"hostSuffix\": \"example.com\" }, \n"
68           "  \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
69           "}"),
70      &error);
71  EXPECT_EQ("", error);
72  ASSERT_TRUE(result.get());
73
74  URLMatcherConditionSet::Vector url_matcher_condition_set;
75  result->GetURLMatcherConditionSets(&url_matcher_condition_set);
76  matcher.AddConditionSets(url_matcher_condition_set);
77
78  net::TestURLRequestContext context;
79  const GURL http_url("http://www.example.com");
80  net::TestURLRequest match_request(
81      http_url, net::DEFAULT_PRIORITY, NULL, &context);
82  WebRequestData data(&match_request, ON_BEFORE_REQUEST);
83  WebRequestDataWithMatchIds request_data(&data);
84  request_data.url_match_ids = matcher.MatchURL(http_url);
85  EXPECT_EQ(1u, request_data.url_match_ids.size());
86  content::ResourceRequestInfo::AllocateForTesting(
87      &match_request, ResourceType::MAIN_FRAME, NULL, -1, -1, false);
88  EXPECT_TRUE(result->IsFulfilled(request_data));
89
90  const GURL https_url("https://www.example.com");
91  net::TestURLRequest wrong_resource_type(
92      https_url, net::DEFAULT_PRIORITY, NULL, &context);
93  data.request = &wrong_resource_type;
94  request_data.url_match_ids = matcher.MatchURL(http_url);
95  // Make sure IsFulfilled does not fail because of URL matching.
96  EXPECT_EQ(1u, request_data.url_match_ids.size());
97  content::ResourceRequestInfo::AllocateForTesting(
98      &wrong_resource_type, ResourceType::SUB_FRAME, NULL, -1, -1, false);
99  EXPECT_FALSE(result->IsFulfilled(request_data));
100}
101
102TEST(WebRequestConditionTest, CreateConditionFirstPartyForCookies) {
103  // Necessary for TestURLRequest.
104  base::MessageLoop message_loop(base::MessageLoop::TYPE_IO);
105  URLMatcher matcher;
106
107  std::string error;
108  scoped_ptr<WebRequestCondition> result;
109
110  result = WebRequestCondition::Create(
111      NULL,
112      matcher.condition_factory(),
113      *base::test::ParseJson(
114           "{ \n"
115           "  \"firstPartyForCookiesUrl\": { \"hostPrefix\": \"fpfc\"}, \n"
116           "  \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
117           "}"),
118      &error);
119  EXPECT_EQ("", error);
120  ASSERT_TRUE(result.get());
121
122  URLMatcherConditionSet::Vector url_matcher_condition_set;
123  result->GetURLMatcherConditionSets(&url_matcher_condition_set);
124  matcher.AddConditionSets(url_matcher_condition_set);
125
126  net::TestURLRequestContext context;
127  const GURL http_url("http://www.example.com");
128  const GURL first_party_url("http://fpfc.example.com");
129  net::TestURLRequest match_request(
130      http_url, net::DEFAULT_PRIORITY, NULL, &context);
131  WebRequestData data(&match_request, ON_BEFORE_REQUEST);
132  WebRequestDataWithMatchIds request_data(&data);
133  request_data.url_match_ids = matcher.MatchURL(http_url);
134  EXPECT_EQ(0u, request_data.url_match_ids.size());
135  request_data.first_party_url_match_ids = matcher.MatchURL(first_party_url);
136  EXPECT_EQ(1u, request_data.first_party_url_match_ids.size());
137  content::ResourceRequestInfo::AllocateForTesting(
138      &match_request, ResourceType::MAIN_FRAME, NULL, -1, -1, false);
139  EXPECT_TRUE(result->IsFulfilled(request_data));
140}
141
142// Conditions without UrlFilter attributes need to be independent of URL
143// matching results. We test here that:
144//   1. A non-empty condition without UrlFilter attributes is fulfilled iff its
145//      attributes are fulfilled.
146//   2. An empty condition (in particular, without UrlFilter attributes) is
147//      always fulfilled.
148TEST(WebRequestConditionTest, NoUrlAttributes) {
149  // Necessary for TestURLRequest.
150  base::MessageLoop message_loop(base::MessageLoop::TYPE_IO);
151  URLMatcher matcher;
152  std::string error;
153
154  // The empty condition.
155  error.clear();
156  scoped_ptr<WebRequestCondition> condition_empty = WebRequestCondition::Create(
157      NULL,
158      matcher.condition_factory(),
159      *base::test::ParseJson(
160           "{ \n"
161           "  \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
162           "}"),
163      &error);
164  EXPECT_EQ("", error);
165  ASSERT_TRUE(condition_empty.get());
166
167  // A condition without a UrlFilter attribute, which is always true.
168  error.clear();
169  scoped_ptr<WebRequestCondition> condition_no_url_true =
170      WebRequestCondition::Create(
171          NULL,
172          matcher.condition_factory(),
173          *base::test::ParseJson(
174               "{ \n"
175               "  \"instanceType\": \"declarativeWebRequest.RequestMatcher\", "
176               "\n"
177               // There is no "1st party for cookies" URL in the requests below,
178               // therefore all requests are considered first party for cookies.
179               "  \"thirdPartyForCookies\": false, \n"
180               "}"),
181          &error);
182  EXPECT_EQ("", error);
183  ASSERT_TRUE(condition_no_url_true.get());
184
185  // A condition without a UrlFilter attribute, which is always false.
186  error.clear();
187  scoped_ptr<WebRequestCondition> condition_no_url_false =
188      WebRequestCondition::Create(
189          NULL,
190          matcher.condition_factory(),
191          *base::test::ParseJson(
192               "{ \n"
193               "  \"instanceType\": \"declarativeWebRequest.RequestMatcher\", "
194               "\n"
195               "  \"thirdPartyForCookies\": true, \n"
196               "}"),
197          &error);
198  EXPECT_EQ("", error);
199  ASSERT_TRUE(condition_no_url_false.get());
200
201  net::TestURLRequestContext context;
202  net::TestURLRequest https_request(
203      GURL("https://www.example.com"), net::DEFAULT_PRIORITY, NULL, &context);
204
205  // 1. A non-empty condition without UrlFilter attributes is fulfilled iff its
206  //    attributes are fulfilled.
207  WebRequestData data(&https_request, ON_BEFORE_REQUEST);
208  EXPECT_FALSE(
209      condition_no_url_false->IsFulfilled(WebRequestDataWithMatchIds(&data)));
210
211  data = WebRequestData(&https_request, ON_BEFORE_REQUEST);
212  EXPECT_TRUE(
213      condition_no_url_true->IsFulfilled(WebRequestDataWithMatchIds(&data)));
214
215  // 2. An empty condition (in particular, without UrlFilter attributes) is
216  //    always fulfilled.
217  data = WebRequestData(&https_request, ON_BEFORE_REQUEST);
218  EXPECT_TRUE(condition_empty->IsFulfilled(WebRequestDataWithMatchIds(&data)));
219}
220
221TEST(WebRequestConditionTest, CreateConditionSet) {
222  // Necessary for TestURLRequest.
223  base::MessageLoop message_loop(base::MessageLoop::TYPE_IO);
224  URLMatcher matcher;
225
226  WebRequestConditionSet::AnyVector conditions;
227  conditions.push_back(linked_ptr<base::Value>(base::test::ParseJson(
228      "{ \n"
229      "  \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
230      "  \"url\": { \n"
231      "    \"hostSuffix\": \"example.com\", \n"
232      "    \"schemes\": [\"http\"], \n"
233      "  }, \n"
234      "}").release()));
235  conditions.push_back(linked_ptr<base::Value>(base::test::ParseJson(
236      "{ \n"
237      "  \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
238      "  \"url\": { \n"
239      "    \"hostSuffix\": \"example.com\", \n"
240      "    \"hostPrefix\": \"www\", \n"
241      "    \"schemes\": [\"https\"], \n"
242      "  }, \n"
243      "}").release()));
244
245  // Test insertion
246  std::string error;
247  scoped_ptr<WebRequestConditionSet> result = WebRequestConditionSet::Create(
248      NULL, matcher.condition_factory(), conditions, &error);
249  EXPECT_EQ("", error);
250  ASSERT_TRUE(result.get());
251  EXPECT_EQ(2u, result->conditions().size());
252
253  // Tell the URLMatcher about our shiny new patterns.
254  URLMatcherConditionSet::Vector url_matcher_condition_set;
255  result->GetURLMatcherConditionSets(&url_matcher_condition_set);
256  matcher.AddConditionSets(url_matcher_condition_set);
257
258  // Test that the result is correct and matches http://www.example.com and
259  // https://www.example.com
260  GURL http_url("http://www.example.com");
261  net::TestURLRequestContext context;
262  net::TestURLRequest http_request(
263      http_url, net::DEFAULT_PRIORITY, NULL, &context);
264  WebRequestData data(&http_request, ON_BEFORE_REQUEST);
265  WebRequestDataWithMatchIds request_data(&data);
266  request_data.url_match_ids = matcher.MatchURL(http_url);
267  EXPECT_EQ(1u, request_data.url_match_ids.size());
268  EXPECT_TRUE(result->IsFulfilled(*(request_data.url_match_ids.begin()),
269                                  request_data));
270
271  GURL https_url("https://www.example.com");
272  request_data.url_match_ids = matcher.MatchURL(https_url);
273  EXPECT_EQ(1u, request_data.url_match_ids.size());
274  net::TestURLRequest https_request(
275      https_url, net::DEFAULT_PRIORITY, NULL, &context);
276  data.request = &https_request;
277  EXPECT_TRUE(result->IsFulfilled(*(request_data.url_match_ids.begin()),
278                                  request_data));
279
280  // Check that both, hostPrefix and hostSuffix are evaluated.
281  GURL https_foo_url("https://foo.example.com");
282  request_data.url_match_ids = matcher.MatchURL(https_foo_url);
283  EXPECT_EQ(0u, request_data.url_match_ids.size());
284  net::TestURLRequest https_foo_request(
285      https_foo_url, net::DEFAULT_PRIORITY, NULL, &context);
286  data.request = &https_foo_request;
287  EXPECT_FALSE(result->IsFulfilled(-1, request_data));
288}
289
290TEST(WebRequestConditionTest, TestPortFilter) {
291  // Necessary for TestURLRequest.
292  base::MessageLoop message_loop(base::MessageLoop::TYPE_IO);
293  URLMatcher matcher;
294
295  WebRequestConditionSet::AnyVector conditions;
296  conditions.push_back(linked_ptr<base::Value>(base::test::ParseJson(
297      "{ \n"
298      "  \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
299      "  \"url\": { \n"
300      "    \"ports\": [80, [1000, 1010]], \n"  // Allow 80;1000-1010.
301      "    \"hostSuffix\": \"example.com\", \n"
302      "  }, \n"
303      "}").release()));
304
305  // Test insertion
306  std::string error;
307  scoped_ptr<WebRequestConditionSet> result = WebRequestConditionSet::Create(
308      NULL, matcher.condition_factory(), conditions, &error);
309  EXPECT_EQ("", error);
310  ASSERT_TRUE(result.get());
311  EXPECT_EQ(1u, result->conditions().size());
312
313  // Tell the URLMatcher about our shiny new patterns.
314  URLMatcherConditionSet::Vector url_matcher_condition_set;
315  result->GetURLMatcherConditionSets(&url_matcher_condition_set);
316  matcher.AddConditionSets(url_matcher_condition_set);
317
318  std::set<URLMatcherConditionSet::ID> url_match_ids;
319
320  // Test various URLs.
321  GURL http_url("http://www.example.com");
322  net::TestURLRequestContext context;
323  net::TestURLRequest http_request(
324      http_url, net::DEFAULT_PRIORITY, NULL, &context);
325  url_match_ids = matcher.MatchURL(http_url);
326  ASSERT_EQ(1u, url_match_ids.size());
327
328  GURL http_url_80("http://www.example.com:80");
329  net::TestURLRequest http_request_80(
330      http_url_80, net::DEFAULT_PRIORITY, NULL, &context);
331  url_match_ids = matcher.MatchURL(http_url_80);
332  ASSERT_EQ(1u, url_match_ids.size());
333
334  GURL http_url_1000("http://www.example.com:1000");
335  net::TestURLRequest http_request_1000(
336      http_url_1000, net::DEFAULT_PRIORITY, NULL, &context);
337  url_match_ids = matcher.MatchURL(http_url_1000);
338  ASSERT_EQ(1u, url_match_ids.size());
339
340  GURL http_url_2000("http://www.example.com:2000");
341  net::TestURLRequest http_request_2000(
342      http_url_2000, net::DEFAULT_PRIORITY, NULL, &context);
343  url_match_ids = matcher.MatchURL(http_url_2000);
344  ASSERT_EQ(0u, url_match_ids.size());
345}
346
347// Create a condition with two attributes: one on the request header and one on
348// the response header. The Create() method should fail and complain that it is
349// impossible that both conditions are fulfilled at the same time.
350TEST(WebRequestConditionTest, ConditionsWithConflictingStages) {
351  // Necessary for TestURLRequest.
352  base::MessageLoop message_loop(base::MessageLoop::TYPE_IO);
353  URLMatcher matcher;
354
355  std::string error;
356  scoped_ptr<WebRequestCondition> result;
357
358  // Test error on incompatible application stages for involved attributes.
359  error.clear();
360  result = WebRequestCondition::Create(
361      NULL,
362      matcher.condition_factory(),
363      *base::test::ParseJson(
364           "{ \n"
365           "  \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
366           // Pass a JS array with one empty object to each of the header
367           // filters.
368           "  \"requestHeaders\": [{}], \n"
369           "  \"responseHeaders\": [{}], \n"
370           "}"),
371      &error);
372  EXPECT_FALSE(error.empty());
373  EXPECT_FALSE(result.get());
374}
375
376}  // namespace extensions
377