1 // Copyright 2017 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 <stddef.h>
6
7 #include <algorithm>
8 #include <memory>
9 #include <set>
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include "base/bind.h"
15 #include "base/files/file_util.h"
16 #include "base/files/scoped_temp_dir.h"
17 #include "base/json/json_string_value_serializer.h"
18 #include "base/macros.h"
19 #include "base/memory/scoped_refptr.h"
20 #include "base/optional.h"
21 #include "base/path_service.h"
22 #include "base/rand_util.h"
23 #include "base/run_loop.h"
24 #include "base/sequence_checker.h"
25 #include "base/strings/string_number_conversions.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/strings/utf_string_conversions.h"
28 #include "base/synchronization/lock.h"
29 #include "base/task/post_task.h"
30 #include "base/test/bind.h"
31 #include "base/test/metrics/histogram_tester.h"
32 #include "base/test/scoped_feature_list.h"
33 #include "base/test/simple_test_clock.h"
34 #include "base/threading/thread_restrictions.h"
35 #include "base/time/time.h"
36 #include "base/values.h"
37 #include "build/build_config.h"
38 #include "chrome/browser/extensions/active_tab_permission_granter.h"
39 #include "chrome/browser/extensions/api/extension_action/test_extension_action_api_observer.h"
40 #include "chrome/browser/extensions/extension_action_runner.h"
41 #include "chrome/browser/extensions/extension_browsertest.h"
42 #include "chrome/browser/extensions/extension_service.h"
43 #include "chrome/browser/extensions/extension_tab_util.h"
44 #include "chrome/browser/extensions/extension_util.h"
45 #include "chrome/browser/extensions/identifiability_metrics_test_util.h"
46 #include "chrome/browser/extensions/load_error_reporter.h"
47 #include "chrome/browser/extensions/scripting_permissions_modifier.h"
48 #include "chrome/browser/extensions/tab_helper.h"
49 #include "chrome/browser/net/profile_network_context_service.h"
50 #include "chrome/browser/net/profile_network_context_service_factory.h"
51 #include "chrome/browser/profiles/profile.h"
52 #include "chrome/browser/ui/browser_tabstrip.h"
53 #include "chrome/browser/ui/tabs/tab_strip_model.h"
54 #include "chrome/common/chrome_paths.h"
55 #include "chrome/common/webui_url_constants.h"
56 #include "chrome/test/base/ui_test_utils.h"
57 #include "components/prefs/pref_service.h"
58 #include "components/proxy_config/proxy_config_dictionary.h"
59 #include "components/proxy_config/proxy_config_pref_names.h"
60 #include "components/web_package/test_support/web_bundle_builder.h"
61 #include "content/public/browser/browser_context.h"
62 #include "content/public/browser/browser_task_traits.h"
63 #include "content/public/browser/browser_thread.h"
64 #include "content/public/browser/navigation_entry.h"
65 #include "content/public/browser/notification_service.h"
66 #include "content/public/browser/render_frame_host.h"
67 #include "content/public/browser/storage_partition.h"
68 #include "content/public/common/content_features.h"
69 #include "content/public/common/url_constants.h"
70 #include "content/public/test/browser_test.h"
71 #include "content/public/test/browser_test_utils.h"
72 #include "content/public/test/simple_url_loader_test_helper.h"
73 #include "content/public/test/test_navigation_observer.h"
74 #include "content/public/test/test_utils.h"
75 #include "extensions/browser/api/declarative_net_request/action_tracker.h"
76 #include "extensions/browser/api/declarative_net_request/composite_matcher.h"
77 #include "extensions/browser/api/declarative_net_request/constants.h"
78 #include "extensions/browser/api/declarative_net_request/declarative_net_request_api.h"
79 #include "extensions/browser/api/declarative_net_request/rules_monitor_service.h"
80 #include "extensions/browser/api/declarative_net_request/ruleset_manager.h"
81 #include "extensions/browser/api/declarative_net_request/ruleset_matcher.h"
82 #include "extensions/browser/api/declarative_net_request/ruleset_source.h"
83 #include "extensions/browser/api/declarative_net_request/test_utils.h"
84 #include "extensions/browser/api/declarative_net_request/utils.h"
85 #include "extensions/browser/api/web_request/web_request_api.h"
86 #include "extensions/browser/api/web_request/web_request_info.h"
87 #include "extensions/browser/blocked_action_type.h"
88 #include "extensions/browser/extension_action.h"
89 #include "extensions/browser/extension_action_manager.h"
90 #include "extensions/browser/extension_prefs.h"
91 #include "extensions/browser/extension_registry.h"
92 #include "extensions/browser/extension_system.h"
93 #include "extensions/browser/extension_util.h"
94 #include "extensions/browser/test_extension_registry_observer.h"
95 #include "extensions/browser/warning_service.h"
96 #include "extensions/browser/warning_set.h"
97 #include "extensions/common/api/declarative_net_request/constants.h"
98 #include "extensions/common/api/declarative_net_request/test_utils.h"
99 #include "extensions/common/api/extension_action/action_info.h"
100 #include "extensions/common/constants.h"
101 #include "extensions/common/extension_features.h"
102 #include "extensions/common/extension_id.h"
103 #include "extensions/common/file_util.h"
104 #include "extensions/common/permissions/permissions_data.h"
105 #include "extensions/common/url_pattern.h"
106 #include "extensions/common/url_pattern_set.h"
107 #include "extensions/common/value_builder.h"
108 #include "extensions/test/extension_test_message_listener.h"
109 #include "ipc/ipc_message.h"
110 #include "net/base/net_errors.h"
111 #include "net/dns/mock_host_resolver.h"
112 #include "net/http/http_status_code.h"
113 #include "net/test/embedded_test_server/default_handlers.h"
114 #include "net/test/embedded_test_server/http_request.h"
115 #include "net/test/embedded_test_server/http_response.h"
116 #include "net/test/spawned_test_server/spawned_test_server.h"
117 #include "net/test/test_data_directory.h"
118 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
119 #include "services/network/public/cpp/features.h"
120 #include "services/network/public/cpp/resource_request.h"
121 #include "services/network/public/cpp/simple_url_loader.h"
122 #include "testing/gmock/include/gmock/gmock.h"
123
124 namespace extensions {
125 namespace declarative_net_request {
126 namespace {
127
128 namespace dnr_api = api::declarative_net_request;
129
130 using ::testing::UnorderedElementsAre;
131 using ::testing::UnorderedElementsAreArray;
132
133 constexpr char kDefaultRulesetID[] = "id";
134
135 // Returns true if |window.scriptExecuted| is true for the given frame.
WasFrameWithScriptLoaded(content::RenderFrameHost * rfh)136 bool WasFrameWithScriptLoaded(content::RenderFrameHost* rfh) {
137 if (!rfh)
138 return false;
139 bool script_resource_was_loaded = false;
140 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
141 rfh, "domAutomationController.send(!!window.scriptExecuted)",
142 &script_resource_was_loaded));
143 return script_resource_was_loaded;
144 }
145
146 // Helper to wait for ruleset load in response to extension load.
147 class RulesetLoadObserver : public RulesMonitorService::TestObserver {
148 public:
RulesetLoadObserver(RulesMonitorService * service,const ExtensionId & extension_id)149 RulesetLoadObserver(RulesMonitorService* service,
150 const ExtensionId& extension_id)
151 : service_(service), extension_id_(extension_id) {
152 service_->SetObserverForTest(this);
153 }
154
~RulesetLoadObserver()155 ~RulesetLoadObserver() override { service_->SetObserverForTest(nullptr); }
156
Wait()157 void Wait() { run_loop_.Run(); }
158
159 private:
160 // RulesMonitorService::TestObserver override:
OnRulesetLoadComplete(const ExtensionId & extension_id)161 void OnRulesetLoadComplete(const ExtensionId& extension_id) override {
162 if (extension_id_ == extension_id)
163 run_loop_.Quit();
164 }
165
166 RulesMonitorService* const service_;
167 const ExtensionId extension_id_;
168 base::RunLoop run_loop_;
169 };
170
171 class DeclarativeNetRequestBrowserTest
172 : public ExtensionBrowserTest,
173 public ::testing::WithParamInterface<ExtensionLoadType> {
174 public:
DeclarativeNetRequestBrowserTest()175 DeclarativeNetRequestBrowserTest() {
176 net::test_server::RegisterDefaultHandlers(embedded_test_server());
177 }
178
179 // ExtensionBrowserTest overrides:
SetUpOnMainThread()180 void SetUpOnMainThread() override {
181 ExtensionBrowserTest::SetUpOnMainThread();
182
183 base::FilePath test_root_path;
184 base::PathService::Get(chrome::DIR_TEST_DATA, &test_root_path);
185 test_root_path = test_root_path.AppendASCII("extensions")
186 .AppendASCII("declarative_net_request");
187 embedded_test_server()->ServeFilesFromDirectory(test_root_path);
188
189 embedded_test_server()->RegisterRequestMonitor(
190 base::BindRepeating(&DeclarativeNetRequestBrowserTest::MonitorRequest,
191 base::Unretained(this)));
192
193 content::SetupCrossSiteRedirector(embedded_test_server());
194
195 ASSERT_TRUE(embedded_test_server()->Start());
196
197 // Map all hosts to localhost.
198 host_resolver()->AddRule("*", "127.0.0.1");
199
200 CreateTempDir();
201 InitializeRulesetManagerObserver();
202 }
203
TearDownOnMainThread()204 void TearDownOnMainThread() override {
205 // Ensure |ruleset_manager_observer_| gets destructed on the UI thread.
206 ruleset_manager_observer_.reset();
207
208 ExtensionBrowserTest::TearDownOnMainThread();
209 }
210
211 protected:
212 // Returns the number of extensions with active rulesets.
extensions_with_rulesets_count()213 size_t extensions_with_rulesets_count() {
214 return ruleset_manager()->GetMatcherCountForTest();
215 }
216
ruleset_manager_observer()217 RulesetManagerObserver* ruleset_manager_observer() {
218 return ruleset_manager_observer_.get();
219 }
220
221 // Waits till the number of extensions with active rulesets is |count|.
WaitForExtensionsWithRulesetsCount(size_t count)222 void WaitForExtensionsWithRulesetsCount(size_t count) {
223 ruleset_manager_observer()->WaitForExtensionsWithRulesetsCount(count);
224 }
225
web_contents(Browser * browser) const226 content::WebContents* web_contents(Browser* browser) const {
227 return browser->tab_strip_model()->GetActiveWebContents();
228 }
229
web_contents() const230 content::WebContents* web_contents() const { return web_contents(browser()); }
231
GetMainFrame(Browser * browser) const232 content::RenderFrameHost* GetMainFrame(Browser* browser) const {
233 return web_contents(browser)->GetMainFrame();
234 }
235
GetMainFrame() const236 content::RenderFrameHost* GetMainFrame() const {
237 return GetMainFrame(browser());
238 }
239
GetFrameByName(const std::string & name) const240 content::RenderFrameHost* GetFrameByName(const std::string& name) const {
241 return content::FrameMatchingPredicate(
242 web_contents(), base::BindRepeating(&content::FrameMatchesName, name));
243 }
244
GetPageType(Browser * browser) const245 content::PageType GetPageType(Browser* browser) const {
246 return web_contents(browser)
247 ->GetController()
248 .GetLastCommittedEntry()
249 ->GetPageType();
250 }
251
rules_monitor_service()252 RulesMonitorService* rules_monitor_service() {
253 return RulesMonitorService::Get(profile());
254 }
255
ruleset_manager()256 RulesetManager* ruleset_manager() {
257 return rules_monitor_service()->ruleset_manager();
258 }
259
last_loaded_extension()260 const Extension* last_loaded_extension() {
261 return extension_registry()->GetExtensionById(last_loaded_extension_id(),
262 ExtensionRegistry::ENABLED);
263 }
264
GetPageType() const265 content::PageType GetPageType() const { return GetPageType(browser()); }
266
GetPageBody() const267 std::string GetPageBody() const {
268 std::string result;
269 const char* script =
270 "domAutomationController.send(document.body.innerText.trim())";
271 EXPECT_TRUE(content::ExecuteScriptAndExtractString(web_contents(), script,
272 &result));
273 return result;
274 }
275
set_config_flags(unsigned flags)276 void set_config_flags(unsigned flags) { flags_ = flags; }
277
278 // Loads an extension with the given |rulesets| in the given |directory|.
279 // Generates a fatal failure if the extension failed to load. |hosts|
280 // specifies the host permissions the extensions should have. Waits till the
281 // ruleset is loaded.
LoadExtensionWithRulesets(const std::vector<TestRulesetInfo> & rulesets,const std::string & directory,const std::vector<std::string> & hosts)282 void LoadExtensionWithRulesets(const std::vector<TestRulesetInfo>& rulesets,
283 const std::string& directory,
284 const std::vector<std::string>& hosts) {
285 size_t expected_extension_ruleset_count_change = rulesets.empty() ? 0 : 1;
286 LoadExtensionInternal(
287 rulesets, directory, hosts, expected_extension_ruleset_count_change,
288 false /* has_dynamic_ruleset */, false /* is_extension_update */);
289 }
290
291 // Similar to LoadExtensionWithRulesets above but updates the last loaded
292 // extension instead. |expected_extension_ruleset_count_change| corresponds to
293 // the expected change in the number of extensions with rulesets after
294 // extension update. |has_dynamic_ruleset| should be true if the installed
295 // extension has a dynamic ruleset.
UpdateLastLoadedExtension(const std::vector<TestRulesetInfo> & new_rulesets,const std::string & new_directory,const std::vector<std::string> & new_hosts,int expected_extension_ruleset_count_change,bool has_dynamic_ruleset)296 void UpdateLastLoadedExtension(
297 const std::vector<TestRulesetInfo>& new_rulesets,
298 const std::string& new_directory,
299 const std::vector<std::string>& new_hosts,
300 int expected_extension_ruleset_count_change,
301 bool has_dynamic_ruleset) {
302 LoadExtensionInternal(new_rulesets, new_directory, new_hosts,
303 expected_extension_ruleset_count_change,
304 has_dynamic_ruleset, true /* is_extension_update */);
305 }
306
307 // Specialization of LoadExtensionWithRulesets above for an extension with a
308 // single static ruleset.
LoadExtensionWithRules(const std::vector<TestRule> & rules,const std::string & directory,const std::vector<std::string> & hosts)309 void LoadExtensionWithRules(const std::vector<TestRule>& rules,
310 const std::string& directory,
311 const std::vector<std::string>& hosts) {
312 constexpr char kJSONRulesFilename[] = "rules_file.json";
313 LoadExtensionWithRulesets(
314 {TestRulesetInfo(kDefaultRulesetID, kJSONRulesFilename,
315 *ToListValue(rules))},
316 directory, hosts);
317 }
318
319 // Returns a url with |filter| as a substring.
GetURLForFilter(const std::string & filter) const320 GURL GetURLForFilter(const std::string& filter) const {
321 return embedded_test_server()->GetURL(
322 "abc.com", "/pages_with_script/index.html?" + filter);
323 }
324
LoadExtensionWithRules(const std::vector<TestRule> & rules)325 void LoadExtensionWithRules(const std::vector<TestRule>& rules) {
326 LoadExtensionWithRules(rules, "test_extension", {} /* hosts */);
327 }
328
329 // Returns true if the navigation to given |url| is blocked.
IsNavigationBlocked(const GURL & url)330 bool IsNavigationBlocked(const GURL& url) {
331 ui_test_utils::NavigateToURL(browser(), url);
332 return !WasFrameWithScriptLoaded(GetMainFrame());
333 }
334
VerifyNavigations(const std::vector<GURL> & expected_blocked_urls,const std::vector<GURL> & expected_allowed_urls)335 void VerifyNavigations(const std::vector<GURL>& expected_blocked_urls,
336 const std::vector<GURL>& expected_allowed_urls) {
337 for (const GURL& url : expected_blocked_urls)
338 EXPECT_TRUE(IsNavigationBlocked(url)) << url;
339
340 for (const GURL& url : expected_allowed_urls)
341 EXPECT_FALSE(IsNavigationBlocked(url)) << url;
342 }
343
CreateMainFrameBlockRule(const std::string & filter)344 TestRule CreateMainFrameBlockRule(const std::string& filter) {
345 TestRule rule = CreateGenericRule();
346 rule.condition->url_filter = filter;
347 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
348 return rule;
349 }
350
AddDynamicRules(const ExtensionId & extension_id,const std::vector<TestRule> & rules)351 void AddDynamicRules(const ExtensionId& extension_id,
352 const std::vector<TestRule>& rules) {
353 static constexpr char kScript[] = R"(
354 chrome.declarativeNetRequest.updateDynamicRules({addRules: $1},
355 function () {
356 window.domAutomationController.send(chrome.runtime.lastError ?
357 chrome.runtime.lastError.message : 'success');
358 });
359 )";
360
361 // Serialize |rules|.
362 ListBuilder builder;
363 for (const auto& rule : rules)
364 builder.Append(rule.ToValue());
365
366 // A cast is necessary from ListValue to Value, else this fails to compile.
367 const std::string script = content::JsReplace(
368 kScript, static_cast<const base::Value&>(*builder.Build()));
369 ASSERT_EQ("success", ExecuteScriptInBackgroundPage(extension_id, script));
370 }
371
RemoveDynamicRules(const ExtensionId & extension_id,const std::vector<int> rule_ids)372 void RemoveDynamicRules(const ExtensionId& extension_id,
373 const std::vector<int> rule_ids) {
374 static constexpr char kScript[] = R"(
375 chrome.declarativeNetRequest.updateDynamicRules({removeRuleIds: $1},
376 function () {
377 window.domAutomationController.send(chrome.runtime.lastError ?
378 chrome.runtime.lastError.message : 'success');
379 });
380 )";
381
382 // Serialize |rule_ids|.
383 std::unique_ptr<base::Value> rule_ids_value =
384 ListBuilder().Append(rule_ids.begin(), rule_ids.end()).Build();
385
386 // A cast is necessary from ListValue to Value, else this fails to compile.
387 const std::string script = content::JsReplace(
388 kScript, static_cast<const base::Value&>(*rule_ids_value));
389 ASSERT_EQ("success", ExecuteScriptInBackgroundPage(extension_id, script));
390 }
391
UpdateEnabledRulesets(const ExtensionId & extension_id,const std::vector<std::string> & ruleset_ids_to_remove,const std::vector<std::string> & ruleset_ids_to_add)392 void UpdateEnabledRulesets(
393 const ExtensionId& extension_id,
394 const std::vector<std::string>& ruleset_ids_to_remove,
395 const std::vector<std::string>& ruleset_ids_to_add) {
396 std::string result = UpdateEnabledRulesetsInternal(
397 extension_id, ruleset_ids_to_remove, ruleset_ids_to_add);
398 ASSERT_EQ("success", result);
399 }
400
UpdateEnabledRulesetsAndFail(const ExtensionId & extension_id,const std::vector<std::string> & ruleset_ids_to_remove,const std::vector<std::string> & ruleset_ids_to_add,std::string expected_error)401 void UpdateEnabledRulesetsAndFail(
402 const ExtensionId& extension_id,
403 const std::vector<std::string>& ruleset_ids_to_remove,
404 const std::vector<std::string>& ruleset_ids_to_add,
405 std::string expected_error) {
406 std::string result = UpdateEnabledRulesetsInternal(
407 extension_id, ruleset_ids_to_remove, ruleset_ids_to_add);
408 ASSERT_NE("success", result);
409 EXPECT_EQ(expected_error, result);
410 }
411
VerifyPublicRulesetIds(const Extension * extension,const std::vector<std::string> & expected_ruleset_ids)412 void VerifyPublicRulesetIds(
413 const Extension* extension,
414 const std::vector<std::string>& expected_ruleset_ids) {
415 ASSERT_TRUE(extension);
416
417 CompositeMatcher* composite_matcher =
418 ruleset_manager()->GetMatcherForExtension(extension->id());
419 ASSERT_TRUE(composite_matcher);
420 EXPECT_THAT(GetPublicRulesetIDs(*extension, *composite_matcher),
421 UnorderedElementsAreArray(expected_ruleset_ids));
422 }
423
SetActionsAsBadgeText(const ExtensionId & extension_id,bool pref)424 void SetActionsAsBadgeText(const ExtensionId& extension_id, bool pref) {
425 const char* pref_string = pref ? "true" : "false";
426 static constexpr char kSetExtensionActionOptionsScript[] = R"(
427 chrome.declarativeNetRequest.setExtensionActionOptions(
428 {displayActionCountAsBadgeText: %s});
429 window.domAutomationController.send("done");
430 )";
431
432 ExecuteScriptInBackgroundPage(
433 extension_id,
434 base::StringPrintf(kSetExtensionActionOptionsScript, pref_string));
435 }
436
437 // Navigates frame with name |frame_name| to |url|.
NavigateFrame(const std::string & frame_name,const GURL & url,bool use_frame_referrer=true)438 void NavigateFrame(const std::string& frame_name,
439 const GURL& url,
440 bool use_frame_referrer = true) {
441 content::TestNavigationObserver navigation_observer(
442 web_contents(), 1 /*number_of_navigations*/);
443
444 const char* referrer_policy = use_frame_referrer ? "origin" : "no-referrer";
445
446 ASSERT_TRUE(content::ExecuteScript(
447 GetMainFrame(),
448 base::StringPrintf(R"(
449 document.getElementsByName('%s')[0].referrerPolicy = '%s';
450 document.getElementsByName('%s')[0].src = '%s';)",
451 frame_name.c_str(), referrer_policy,
452 frame_name.c_str(), url.spec().c_str())));
453 navigation_observer.Wait();
454 }
455
456 // Calls getMatchedRules for |extension_id| and optionally, the |tab_id| and
457 // returns comma separated pairs of rule_id and tab_id, with each pair
458 // delimited by '|'. Matched Rules are sorted in ascending order by ruleId,
459 // and ties are resolved by the tabId (in ascending order.)
460 // E.g. "<rule_1>,<tab_1>|<rule_2>,<tab_2>|<rule_3>,<tab_3>"
GetRuleAndTabIdsMatched(const ExtensionId & extension_id,base::Optional<int> tab_id)461 std::string GetRuleAndTabIdsMatched(const ExtensionId& extension_id,
462 base::Optional<int> tab_id) {
463 const char kGetMatchedRulesScript[] = R"(
464 chrome.declarativeNetRequest.getMatchedRules({%s}, (rules) => {
465 // |ruleAndTabIds| is a list of `${ruleId},${tabId}`
466 var ruleAndTabIds = rules.rulesMatchedInfo.map(rule => {
467 return [rule.rule.ruleId, rule.tabId];
468 }).sort((a, b) => {
469 // Sort ascending by rule ID, and resolve ties by tab ID.
470 const idDiff = a.ruleId - b.ruleId;
471 return (idDiff != 0) ? idDiff : a.tabId - b.tabId;
472 }).map(ruleAndTabId => ruleAndTabId.join());
473
474 // Join the comma separated (ruleId,tabId) pairs with '|'.
475 window.domAutomationController.send(ruleAndTabIds.join('|'));
476 });
477 )";
478
479 std::string tab_id_param =
480 tab_id ? base::StringPrintf("tabId: %d", *tab_id) : "";
481 return ExecuteScriptInBackgroundPage(
482 extension_id,
483 base::StringPrintf(kGetMatchedRulesScript, tab_id_param.c_str()),
484 browsertest_util::ScriptUserActivation::kDontActivate);
485 }
486
487 // Calls getMatchedRules for |extension_id| and optionally, |tab_id| and
488 // |timestamp|. Returns the matched rule count for rules more recent than
489 // |timestamp| if specified, and are associated with the tab specified by
490 // |tab_id| or all tabs if |tab_id| is not specified. |script_user_activation|
491 // specifies if the call should be treated as a user gesture. Returns any API
492 // error if the call fails.
GetMatchedRuleCount(const ExtensionId & extension_id,base::Optional<int> tab_id,base::Optional<base::Time> timestamp,browsertest_util::ScriptUserActivation script_user_activation=browsertest_util::ScriptUserActivation::kDontActivate)493 std::string GetMatchedRuleCount(
494 const ExtensionId& extension_id,
495 base::Optional<int> tab_id,
496 base::Optional<base::Time> timestamp,
497 browsertest_util::ScriptUserActivation script_user_activation =
498 browsertest_util::ScriptUserActivation::kDontActivate) {
499 const char kGetMatchedRulesScript[] = R"(
500 chrome.declarativeNetRequest.getMatchedRules(%s, (rules) => {
501 if (chrome.runtime.lastError) {
502 window.domAutomationController.send(chrome.runtime.lastError.message);
503 return;
504 }
505
506 var rule_count = rules.rulesMatchedInfo.length;
507 window.domAutomationController.send(rule_count.toString());
508 });
509 )";
510
511 double timestamp_in_js =
512 timestamp.has_value() ? timestamp->ToJsTimeIgnoringNull() : 0;
513
514 std::string param_string =
515 tab_id.has_value()
516 ? base::StringPrintf("{tabId: %d, minTimeStamp: %f}", *tab_id,
517 timestamp_in_js)
518 : base::StringPrintf("{minTimeStamp: %f}", timestamp_in_js);
519
520 return ExecuteScriptInBackgroundPage(
521 extension_id,
522 base::StringPrintf(kGetMatchedRulesScript, param_string.c_str()),
523 script_user_activation);
524 }
525
GetAvailableStaticRuleCount(const ExtensionId & extension_id)526 std::string GetAvailableStaticRuleCount(const ExtensionId& extension_id) {
527 const char kGetAvailableStaticRuleCountScript[] = R"(
528 chrome.declarativeNetRequest.getAvailableStaticRuleCount((rule_count) => {
529 if (chrome.runtime.lastError) {
530 window.domAutomationController.send(chrome.runtime.lastError.message);
531 return;
532 }
533
534 window.domAutomationController.send(rule_count.toString());
535 });
536 )";
537
538 return ExecuteScriptInBackgroundPage(
539 extension_id, kGetAvailableStaticRuleCountScript,
540 browsertest_util::ScriptUserActivation::kDontActivate);
541 }
542
GetAndResetRequestsToServer()543 std::set<GURL> GetAndResetRequestsToServer() {
544 base::AutoLock lock(requests_to_server_lock_);
545 std::set<GURL> results = requests_to_server_;
546 requests_to_server_.clear();
547 return results;
548 }
549
CreateModifyHeadersRule(int id,int priority,const std::string & url_filter,base::Optional<std::vector<TestHeaderInfo>> request_headers,base::Optional<std::vector<TestHeaderInfo>> response_headers)550 TestRule CreateModifyHeadersRule(
551 int id,
552 int priority,
553 const std::string& url_filter,
554 base::Optional<std::vector<TestHeaderInfo>> request_headers,
555 base::Optional<std::vector<TestHeaderInfo>> response_headers) {
556 TestRule rule = CreateGenericRule();
557 rule.id = id;
558 rule.priority = priority;
559 rule.condition->url_filter = url_filter;
560 rule.condition->resource_types = std::vector<std::string>({"sub_frame"});
561 rule.action->type = "modifyHeaders";
562 rule.action->request_headers = std::move(request_headers);
563 rule.action->response_headers = std::move(response_headers);
564
565 return rule;
566 }
567
CreateTempDir()568 void CreateTempDir() { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
569
InitializeRulesetManagerObserver()570 void InitializeRulesetManagerObserver() {
571 ruleset_manager_observer_ =
572 std::make_unique<RulesetManagerObserver>(ruleset_manager());
573 }
574
575 private:
576 // Handler to monitor the requests which reach the EmbeddedTestServer. This
577 // will be run on the EmbeddedTestServer's IO thread.
MonitorRequest(const net::test_server::HttpRequest & request)578 void MonitorRequest(const net::test_server::HttpRequest& request) {
579 base::AutoLock lock(requests_to_server_lock_);
580 requests_to_server_.insert(request.GetURL());
581 }
582
583 // Helper to load an extension. |has_dynamic_ruleset| should be true if the
584 // extension has a dynamic ruleset on load. If |is_extension_update|, the last
585 // loaded extension is updated.
LoadExtensionInternal(const std::vector<TestRulesetInfo> & rulesets,const std::string & directory,const std::vector<std::string> & hosts,int expected_extension_ruleset_count_change,bool has_dynamic_ruleset,bool is_extension_update)586 void LoadExtensionInternal(const std::vector<TestRulesetInfo>& rulesets,
587 const std::string& directory,
588 const std::vector<std::string>& hosts,
589 int expected_extension_ruleset_count_change,
590 bool has_dynamic_ruleset,
591 bool is_extension_update) {
592 CHECK(!is_extension_update || GetParam() == ExtensionLoadType::PACKED);
593
594 // The "crx" directory is reserved for use by this test fixture.
595 CHECK_NE("crx", directory);
596
597 base::ScopedAllowBlockingForTesting scoped_allow_blocking;
598 base::HistogramTester tester;
599
600 base::FilePath extension_dir = temp_dir_.GetPath().AppendASCII(directory);
601 ASSERT_FALSE(base::PathExists(extension_dir));
602 EXPECT_TRUE(base::CreateDirectory(extension_dir));
603
604 WriteManifestAndRulesets(extension_dir, rulesets, hosts, flags_,
605 directory /* extension_name */);
606
607 ExtensionTestMessageListener background_page_ready_listener(
608 "ready", false /*will_reply*/);
609 size_t current_ruleset_count = extensions_with_rulesets_count();
610
611 const Extension* extension = nullptr;
612 switch (GetParam()) {
613 case ExtensionLoadType::PACKED: {
614 base::FilePath crx_dir =
615 temp_dir_.GetPath().AppendASCII("crx").AppendASCII(directory);
616 base::FilePath crx_path = crx_dir.AppendASCII("temp.crx");
617
618 base::FilePath pem_path;
619 if (is_extension_update)
620 pem_path = last_pem_path_;
621 else
622 last_pem_path_ = crx_dir.AppendASCII("temp.pem");
623
624 ASSERT_FALSE(base::PathExists(crx_dir));
625 ASSERT_TRUE(base::CreateDirectory(crx_dir));
626 ASSERT_EQ(crx_path,
627 PackExtensionWithOptions(extension_dir, crx_path, pem_path,
628 last_pem_path_ /* pem_out_path */
629 ));
630
631 if (is_extension_update) {
632 std::string extension_id = last_loaded_extension_id();
633 extension =
634 UpdateExtension(extension_id, crx_path, 0 /* expected_change */);
635 } else {
636 extension = InstallExtensionWithPermissionsGranted(
637 crx_path, 1 /* expected_change */);
638 }
639 break;
640 }
641 case ExtensionLoadType::UNPACKED:
642 extension = LoadExtension(extension_dir);
643 break;
644 }
645
646 ASSERT_TRUE(extension);
647
648 WaitForExtensionsWithRulesetsCount(current_ruleset_count +
649 expected_extension_ruleset_count_change);
650
651 size_t expected_enabled_rulesets_count = has_dynamic_ruleset ? 1 : 0;
652 size_t expected_manifest_rules_count = 0;
653 size_t expected_manifest_enabled_rules_count = 0;
654 for (const TestRulesetInfo& info : rulesets) {
655 size_t rules_count = info.rules_value.GetList().size();
656 expected_manifest_rules_count += rules_count;
657
658 if (info.enabled) {
659 expected_enabled_rulesets_count++;
660 expected_manifest_enabled_rules_count += rules_count;
661 }
662 }
663
664 // The histograms below are not logged for unpacked extensions.
665 if (GetParam() == ExtensionLoadType::PACKED) {
666 size_t expected_histogram_counts = rulesets.empty() ? 0 : 1;
667
668 tester.ExpectTotalCount(kIndexAndPersistRulesTimeHistogram,
669 expected_histogram_counts);
670 tester.ExpectBucketCount(kManifestRulesCountHistogram,
671 expected_manifest_rules_count /*sample*/,
672 expected_histogram_counts);
673 tester.ExpectBucketCount(kManifestEnabledRulesCountHistogram,
674 expected_manifest_enabled_rules_count /*sample*/,
675 expected_histogram_counts);
676 }
677 tester.ExpectTotalCount(
678 "Extensions.DeclarativeNetRequest.CreateVerifiedMatcherTime",
679 expected_enabled_rulesets_count);
680 tester.ExpectUniqueSample(kLoadRulesetResultHistogram,
681 LoadRulesetResult::kSuccess /*sample*/,
682 expected_enabled_rulesets_count);
683
684 EXPECT_TRUE(AreAllIndexedStaticRulesetsValid(*extension, profile()));
685
686 // Wait for the background page to load if needed.
687 if (flags_ & kConfig_HasBackgroundScript) {
688 ASSERT_TRUE(background_page_ready_listener.WaitUntilSatisfied());
689 ASSERT_EQ(extension->id(),
690 background_page_ready_listener.extension_id_for_message());
691 }
692
693 // Ensure no load errors were reported.
694 EXPECT_TRUE(LoadErrorReporter::GetInstance()->GetErrors()->empty());
695 }
696
UpdateEnabledRulesetsInternal(const ExtensionId & extension_id,const std::vector<std::string> & ruleset_ids_to_remove,const std::vector<std::string> & ruleset_ids_to_add)697 std::string UpdateEnabledRulesetsInternal(
698 const ExtensionId& extension_id,
699 const std::vector<std::string>& ruleset_ids_to_remove,
700 const std::vector<std::string>& ruleset_ids_to_add) {
701 static constexpr char kScript[] = R"(
702 chrome.declarativeNetRequest.updateEnabledRulesets({
703 disableRulesetIds: $1,
704 enableRulesetIds: $2
705 }, () => {
706 window.domAutomationController.send(chrome.runtime.lastError ?
707 chrome.runtime.lastError.message : 'success');
708 });
709 )";
710
711 std::unique_ptr<base::Value> ids_to_remove =
712 ListBuilder()
713 .Append(ruleset_ids_to_remove.begin(), ruleset_ids_to_remove.end())
714 .Build();
715 std::unique_ptr<base::Value> ids_to_add =
716 ListBuilder()
717 .Append(ruleset_ids_to_add.begin(), ruleset_ids_to_add.end())
718 .Build();
719
720 // A cast is necessary from ListValue to Value, else this fails to compile.
721 const std::string script = content::JsReplace(
722 kScript, static_cast<const base::Value&>(*ids_to_remove),
723 static_cast<const base::Value&>(*ids_to_add));
724 return ExecuteScriptInBackgroundPage(extension_id, script);
725 }
726
727 base::ScopedTempDir temp_dir_;
728
729 unsigned flags_ = ConfigFlag::kConfig_None;
730
731 // Requests observed by the EmbeddedTestServer. This is accessed on both the
732 // UI and the EmbeddedTestServer's IO thread. Access is protected by
733 // |requests_to_server_lock_|.
734 std::set<GURL> requests_to_server_;
735
736 base::Lock requests_to_server_lock_;
737
738 std::unique_ptr<RulesetManagerObserver> ruleset_manager_observer_;
739
740 // Path to the PEM file for the last installed packed extension.
741 base::FilePath last_pem_path_;
742
743 DISALLOW_COPY_AND_ASSIGN(DeclarativeNetRequestBrowserTest);
744 };
745
746 using DeclarativeNetRequestBrowserTest_Packed =
747 DeclarativeNetRequestBrowserTest;
748 using DeclarativeNetRequestBrowserTest_Unpacked =
749 DeclarativeNetRequestBrowserTest;
750
751 #if (defined(OS_WIN) || defined(OS_MAC)) && !defined(NDEBUG)
752 // TODO: test times out on win7-debug. http://crbug.com/900447.
753 // Also times out on mac-debug: https://crbug.com/900447
754 #define MAYBE_BlockRequests_UrlFilter DISABLED_BlockRequests_UrlFilter
755 #else
756 #define MAYBE_BlockRequests_UrlFilter BlockRequests_UrlFilter
757 #endif
758 // Tests the "urlFilter" and "regexFilter" property of a declarative rule
759 // condition.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,MAYBE_BlockRequests_UrlFilter)760 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
761 MAYBE_BlockRequests_UrlFilter) {
762 struct {
763 std::string filter;
764 int id;
765 bool is_regex_rule;
766 } rules_data[] = {
767 {"pages_with_script/*ex", 1, false},
768 {"||a.b.com", 2, false},
769 {"|http://*.us", 3, false},
770 {"pages_with_script/page2.html|", 4, false},
771 {"|http://msn*/pages_with_script/page.html|", 5, false},
772 {"%20", 6, false}, // Block any urls with space.
773 {"%C3%A9", 7, false}, // Percent-encoded non-ascii character é.
774 // Internationalized domain "ⱴase.com" in punycode.
775 {"|http://xn--ase-7z0b.com", 8, false},
776 {R"((http|https)://(\w+\.){1,2}com.*reg$)", 9, true},
777 {R"(\d+\.google\.com)", 10, true},
778 };
779
780 // Rule |i| is the rule with id |i|.
781 struct {
782 std::string hostname;
783 std::string path;
784 bool expect_main_frame_loaded;
785 } test_cases[] = {
786 {"example.com", "/pages_with_script/index.html", false}, // Rule 1
787 {"example.com", "/pages_with_script/page.html", true},
788 {"a.b.com", "/pages_with_script/page.html", false}, // Rule 2
789 {"c.a.b.com", "/pages_with_script/page.html", false}, // Rule 2
790 {"b.com", "/pages_with_script/page.html", true},
791 {"example.us", "/pages_with_script/page.html", false}, // Rule 3
792 {"example.jp", "/pages_with_script/page.html", true},
793 {"example.jp", "/pages_with_script/page2.html", false}, // Rule 4
794 {"example.jp", "/pages_with_script/page2.html?q=hello", true},
795 {"msn.com", "/pages_with_script/page.html", false}, // Rule 5
796 {"msn.com", "/pages_with_script/page.html?q=hello", true},
797 {"a.msn.com", "/pages_with_script/page.html", true},
798 {"abc.com", "/pages_with_script/page.html?q=hi bye", false}, // Rule 6
799 {"abc.com", "/pages_with_script/page.html?q=hi%20bye", false}, // Rule 6
800 {"abc.com", "/pages_with_script/page.html?q=hibye", true},
801 {"abc.com",
802 "/pages_with_script/page.html?q=" + base::WideToUTF8(L"\u00E9"),
803 false}, // Rule 7
804 {base::WideToUTF8(L"\x2c74"
805 L"ase.com"),
806 "/pages_with_script/page.html", false}, // Rule 8
807 {"abc.com", "/pages_with_script/page2.html?reg", false}, // Rule 9
808 {"abc.com", "/pages_with_script/page2.html?reg1", true},
809 {"w1.w2.com", "/pages_with_script/page2.html?reg", false}, // Rule 9
810 {"w1.w2.w3.com", "/pages_with_script/page2.html?reg", true},
811 {"24.google.com", "/pages_with_script/page.html", false}, // Rule 10
812 {"xyz.google.com", "/pages_with_script/page.html", true},
813 };
814
815 // Load the extension.
816 std::vector<TestRule> rules;
817 for (const auto& rule_data : rules_data) {
818 TestRule rule = CreateGenericRule();
819 rule.condition->url_filter.reset();
820
821 if (rule_data.is_regex_rule)
822 rule.condition->regex_filter = rule_data.filter;
823 else
824 rule.condition->url_filter = rule_data.filter;
825
826 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
827 rule.id = rule_data.id;
828 rules.push_back(rule);
829 }
830 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules));
831
832 // Verify that the extension correctly intercepts network requests.
833 for (const auto& test_case : test_cases) {
834 GURL url =
835 embedded_test_server()->GetURL(test_case.hostname, test_case.path);
836 SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str()));
837
838 ui_test_utils::NavigateToURL(browser(), url);
839 EXPECT_EQ(test_case.expect_main_frame_loaded,
840 WasFrameWithScriptLoaded(GetMainFrame()));
841
842 content::PageType expected_page_type = test_case.expect_main_frame_loaded
843 ? content::PAGE_TYPE_NORMAL
844 : content::PAGE_TYPE_ERROR;
845 EXPECT_EQ(expected_page_type, GetPageType());
846 }
847 }
848
849 // Tests the matching behavior of the separator ('^') character.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,BlockRequests_Separator)850 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
851 BlockRequests_Separator) {
852 TestRule rule = CreateGenericRule();
853 rule.condition->url_filter =
854 std::string("pages_with_script/page2.html?q=bye^");
855 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
856 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
857
858 // '^' (Separator character) matches anything except a letter, a digit or
859 // one of the following: _ - . %.
860 struct {
861 std::string url_path;
862 bool expect_main_frame_loaded;
863 } test_cases[] = {
864 {"/pages_with_script/page2.html?q=bye&x=1", false},
865 {"/pages_with_script/page2.html?q=bye#x=1", false},
866 {"/pages_with_script/page2.html?q=bye:", false},
867 {"/pages_with_script/page2.html?q=bye3", true},
868 {"/pages_with_script/page2.html?q=byea", true},
869 {"/pages_with_script/page2.html?q=bye_", true},
870 {"/pages_with_script/page2.html?q=bye-", true},
871 {"/pages_with_script/page2.html?q=bye%", true},
872 {"/pages_with_script/page2.html?q=bye.", true},
873 };
874
875 for (const auto& test_case : test_cases) {
876 GURL url = embedded_test_server()->GetURL("google.com", test_case.url_path);
877 SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str()));
878
879 ui_test_utils::NavigateToURL(browser(), url);
880 EXPECT_EQ(test_case.expect_main_frame_loaded,
881 WasFrameWithScriptLoaded(GetMainFrame()));
882
883 content::PageType expected_page_type = test_case.expect_main_frame_loaded
884 ? content::PAGE_TYPE_NORMAL
885 : content::PAGE_TYPE_ERROR;
886 EXPECT_EQ(expected_page_type, GetPageType());
887 }
888 }
889
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,BlockRequests_SeparatorMatchesEndOfURL)890 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
891 BlockRequests_SeparatorMatchesEndOfURL) {
892 TestRule rule = CreateGenericRule();
893 rule.condition->url_filter = std::string("page2.html^");
894 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
895
896 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
897
898 GURL url = embedded_test_server()->GetURL("google.com",
899 "/pages_with_script/page2.html");
900 ui_test_utils::NavigateToURL(browser(), url);
901 EXPECT_FALSE(WasFrameWithScriptLoaded(GetMainFrame()));
902 EXPECT_EQ(content::PAGE_TYPE_ERROR, GetPageType());
903 }
904
905 // Tests case sensitive url filters.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,BlockRequests_IsUrlFilterCaseSensitive)906 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
907 BlockRequests_IsUrlFilterCaseSensitive) {
908 struct {
909 std::string url_filter;
910 size_t id;
911 bool is_url_filter_case_sensitive;
912 } rules_data[] = {{"pages_with_script/index.html?q=hello", 1, false},
913 {"pages_with_script/page.html?q=hello", 2, true}};
914
915 std::vector<TestRule> rules;
916 for (const auto& rule_data : rules_data) {
917 TestRule rule = CreateGenericRule();
918 rule.condition->url_filter = rule_data.url_filter;
919 rule.id = rule_data.id;
920 rule.condition->is_url_filter_case_sensitive =
921 rule_data.is_url_filter_case_sensitive;
922 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
923 rules.push_back(rule);
924 }
925 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules));
926
927 // Rule |i| is the rule with id |i|.
928 struct {
929 std::string hostname;
930 std::string path;
931 bool expect_main_frame_loaded;
932 } test_cases[] = {
933 {"example.com", "/pages_with_script/index.html?q=hello",
934 false}, // Rule 1
935 {"example.com", "/pages_with_script/index.html?q=HELLO",
936 false}, // Rule 1
937 {"example.com", "/pages_with_script/page.html?q=hello", false}, // Rule 2
938 {"example.com", "/pages_with_script/page.html?q=HELLO", true},
939 };
940
941 for (const auto& test_case : test_cases) {
942 GURL url =
943 embedded_test_server()->GetURL(test_case.hostname, test_case.path);
944 SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str()));
945
946 ui_test_utils::NavigateToURL(browser(), url);
947 EXPECT_EQ(test_case.expect_main_frame_loaded,
948 WasFrameWithScriptLoaded(GetMainFrame()));
949
950 content::PageType expected_page_type = test_case.expect_main_frame_loaded
951 ? content::PAGE_TYPE_NORMAL
952 : content::PAGE_TYPE_ERROR;
953 EXPECT_EQ(expected_page_type, GetPageType());
954 }
955 }
956
957 // Tests the "domains" and "excludedDomains" property of a declarative rule
958 // condition.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,BlockRequests_Domains)959 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
960 BlockRequests_Domains) {
961 struct {
962 std::string url_filter;
963 size_t id;
964 std::vector<std::string> domains;
965 std::vector<std::string> excluded_domains;
966 } rules_data[] = {{"child_frame.html?frame=1",
967 1,
968 {"x.com", "xn--tst-bma.com" /* punycode for tést.com */},
969 {"a.x.com"}},
970 {"child_frame.html?frame=2", 2, {}, {"a.y.com"}}};
971
972 std::vector<TestRule> rules;
973 for (const auto& rule_data : rules_data) {
974 TestRule rule = CreateGenericRule();
975 rule.condition->url_filter = rule_data.url_filter;
976 rule.id = rule_data.id;
977
978 // An empty list is not allowed for the "domains" property.
979 if (!rule_data.domains.empty())
980 rule.condition->domains = rule_data.domains;
981
982 rule.condition->excluded_domains = rule_data.excluded_domains;
983 rule.condition->resource_types = std::vector<std::string>({"sub_frame"});
984 rules.push_back(rule);
985 }
986 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules));
987
988 // Rule |i| is the rule with id |i|.
989 struct {
990 std::string main_frame_hostname;
991 bool expect_frame_1_loaded;
992 bool expect_frame_2_loaded;
993 } test_cases[] = {
994 {"x.com", false /* Rule 1 */, false /* Rule 2 */},
995 {base::WideToUTF8(L"tést.com"), false /*Rule 1*/, false /*Rule 2*/},
996 {"b.x.com", false /* Rule 1 */, false /* Rule 2 */},
997 {"a.x.com", true, false /* Rule 2 */},
998 {"b.a.x.com", true, false /* Rule 2 */},
999 {"y.com", true, false /* Rule 2*/},
1000 {"a.y.com", true, true},
1001 {"b.a.y.com", true, true},
1002 {"google.com", true, false /* Rule 2 */},
1003 };
1004
1005 for (const auto& test_case : test_cases) {
1006 GURL url = embedded_test_server()->GetURL(test_case.main_frame_hostname,
1007 "/page_with_two_frames.html");
1008 SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str()));
1009
1010 ui_test_utils::NavigateToURL(browser(), url);
1011 content::RenderFrameHost* main_frame = GetMainFrame();
1012 EXPECT_TRUE(WasFrameWithScriptLoaded(main_frame));
1013 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1014
1015 content::RenderFrameHost* child_1 = content::ChildFrameAt(main_frame, 0);
1016 content::RenderFrameHost* child_2 = content::ChildFrameAt(main_frame, 1);
1017
1018 EXPECT_EQ(test_case.expect_frame_1_loaded,
1019 WasFrameWithScriptLoaded(child_1));
1020 EXPECT_EQ(test_case.expect_frame_2_loaded,
1021 WasFrameWithScriptLoaded(child_2));
1022 }
1023 }
1024
1025 // Tests the "domainType" property of a declarative rule condition.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,BlockRequests_DomainType)1026 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
1027 BlockRequests_DomainType) {
1028 struct {
1029 std::string url_filter;
1030 size_t id;
1031 base::Optional<std::string> domain_type;
1032 } rules_data[] = {
1033 {"child_frame.html?case=1", 1, std::string("firstParty")},
1034 {"child_frame.html?case=2", 2, std::string("thirdParty")},
1035 {"child_frame.html?case=3", 3, base::nullopt},
1036 };
1037
1038 std::vector<TestRule> rules;
1039 for (const auto& rule_data : rules_data) {
1040 TestRule rule = CreateGenericRule();
1041 rule.condition->url_filter = rule_data.url_filter;
1042 rule.id = rule_data.id;
1043 rule.condition->domain_type = rule_data.domain_type;
1044 rule.condition->resource_types = std::vector<std::string>({"sub_frame"});
1045 rules.push_back(rule);
1046 }
1047 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules));
1048
1049 GURL url =
1050 embedded_test_server()->GetURL("example.com", "/domain_type_test.html");
1051 ui_test_utils::NavigateToURL(browser(), url);
1052 ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
1053 ASSERT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1054
1055 // The loaded page will consist of four pairs of iframes named
1056 // first_party_[1..4] and third_party_[1..4], with url path
1057 // child_frame.html?case=[1..4] respectively.
1058 struct {
1059 std::string child_frame_name;
1060 bool expect_frame_loaded;
1061 } cases[] = {
1062 // Url matched by rule 1. Only the first party frame is blocked.
1063 {"first_party_1", false},
1064 {"third_party_1", true},
1065 // Url matched by rule 2. Only the third party frame is blocked.
1066 {"first_party_2", true},
1067 {"third_party_2", false},
1068 // Url matched by rule 3. Both the first and third party frames are
1069 // blocked.
1070 {"first_party_3", false},
1071 {"third_party_3", false},
1072 // No matching rule.
1073 {"first_party_4", true},
1074 {"third_party_4", true},
1075 };
1076
1077 for (const auto& test_case : cases) {
1078 SCOPED_TRACE(base::StringPrintf("Testing child frame named %s",
1079 test_case.child_frame_name.c_str()));
1080
1081 content::RenderFrameHost* child =
1082 GetFrameByName(test_case.child_frame_name);
1083 EXPECT_TRUE(child);
1084 EXPECT_EQ(test_case.expect_frame_loaded, WasFrameWithScriptLoaded(child));
1085 }
1086 }
1087
1088 // Tests allowing rules for blocks.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,AllowBlock)1089 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, AllowBlock) {
1090 const int kNumRequests = 6;
1091
1092 TestRule rule = CreateGenericRule();
1093 int id = kMinValidID;
1094
1095 // Block all main-frame requests ending with numbers 1 to |kNumRequests|.
1096 std::vector<TestRule> rules;
1097 for (int i = 1; i <= kNumRequests; ++i) {
1098 rule.id = id++;
1099 rule.condition->url_filter = base::StringPrintf("num=%d|", i);
1100 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
1101 rule.priority = 1;
1102 rules.push_back(rule);
1103 }
1104
1105 // Allow all main-frame requests ending with even numbers from 1 to
1106 // |kNumRequests|.
1107 for (int i = 2; i <= kNumRequests; i += 2) {
1108 rule.id = id++;
1109 rule.condition->url_filter = base::StringPrintf("num=%d|", i);
1110 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
1111 rule.action->type = std::string("allow");
1112 rule.priority = 2;
1113 rules.push_back(rule);
1114 }
1115
1116 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules));
1117
1118 for (int i = 1; i <= kNumRequests; ++i) {
1119 GURL url = embedded_test_server()->GetURL(
1120 "example.com",
1121 base::StringPrintf("/pages_with_script/page.html?num=%d", i));
1122 SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str()));
1123
1124 ui_test_utils::NavigateToURL(browser(), url);
1125
1126 // All requests ending with odd numbers should be blocked.
1127 const bool page_should_load = (i % 2 == 0);
1128 EXPECT_EQ(page_should_load, WasFrameWithScriptLoaded(GetMainFrame()));
1129 content::PageType expected_page_type =
1130 page_should_load ? content::PAGE_TYPE_NORMAL : content::PAGE_TYPE_ERROR;
1131 EXPECT_EQ(expected_page_type, GetPageType());
1132 }
1133 }
1134
1135 // Tests allowing rules for redirects.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,AllowRedirect)1136 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, AllowRedirect) {
1137 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
1138
1139 const GURL static_redirect_url = embedded_test_server()->GetURL(
1140 "example.com", base::StringPrintf("/pages_with_script/page2.html"));
1141
1142 const GURL dynamic_redirect_url = embedded_test_server()->GetURL(
1143 "abc.com", base::StringPrintf("/pages_with_script/page.html"));
1144
1145 // Create 2 static and 2 dynamic rules.
1146 struct {
1147 std::string url_filter;
1148 int id;
1149 int priority;
1150 std::string action_type;
1151 base::Optional<std::string> redirect_url;
1152 } rules_data[] = {
1153 {"google.com", 1, 1, "redirect", static_redirect_url.spec()},
1154 {"num=1|", 2, 3, "allow", base::nullopt},
1155 {"1|", 3, 4, "redirect", dynamic_redirect_url.spec()},
1156 {"num=3|", 4, 2, "allow", base::nullopt},
1157 };
1158
1159 std::vector<TestRule> rules;
1160 for (const auto& rule_data : rules_data) {
1161 TestRule rule = CreateGenericRule();
1162 rule.id = rule_data.id;
1163 rule.priority = rule_data.priority;
1164 rule.condition->url_filter = rule_data.url_filter;
1165 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
1166 rule.action->type = rule_data.action_type;
1167 rule.action->redirect.emplace();
1168 rule.action->redirect->url = rule_data.redirect_url;
1169 rules.push_back(rule);
1170 }
1171
1172 std::vector<TestRule> static_rules;
1173 static_rules.push_back(rules[0]);
1174 static_rules.push_back(rules[1]);
1175
1176 std::vector<TestRule> dynamic_rules;
1177 dynamic_rules.push_back(rules[2]);
1178 dynamic_rules.push_back(rules[3]);
1179
1180 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
1181 static_rules, "test_extension", {URLPattern::kAllUrlsPattern}));
1182
1183 auto get_url = [this](int i) {
1184 return embedded_test_server()->GetURL(
1185 "google.com",
1186 base::StringPrintf("/pages_with_script/page.html?num=%d", i));
1187 };
1188
1189 struct {
1190 GURL initial_url;
1191 GURL expected_final_url;
1192 } static_test_cases[] = {
1193 {get_url(0), static_redirect_url},
1194 {get_url(1), get_url(1)},
1195 {get_url(3), static_redirect_url},
1196 };
1197
1198 for (const auto& test_case : static_test_cases) {
1199 GURL url = test_case.initial_url;
1200 SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str()));
1201
1202 ui_test_utils::NavigateToURL(browser(), url);
1203 EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
1204
1205 GURL final_url = web_contents()->GetLastCommittedURL();
1206 EXPECT_EQ(test_case.expected_final_url, final_url);
1207 }
1208
1209 // Now add dynamic rules. These should share the priority space with static
1210 // rules.
1211 const ExtensionId& extension_id = last_loaded_extension_id();
1212 ASSERT_NO_FATAL_FAILURE(AddDynamicRules(extension_id, dynamic_rules));
1213
1214 // Test that dynamic and static rules are in the same priority space.
1215 struct {
1216 GURL initial_url;
1217 GURL expected_final_url;
1218 } dynamic_test_cases[] = {
1219 {get_url(1), dynamic_redirect_url},
1220 {get_url(3), get_url(3)},
1221 };
1222
1223 for (const auto& test_case : dynamic_test_cases) {
1224 GURL url = test_case.initial_url;
1225 SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str()));
1226
1227 ui_test_utils::NavigateToURL(browser(), url);
1228 EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
1229
1230 GURL final_url = web_contents()->GetLastCommittedURL();
1231 EXPECT_EQ(test_case.expected_final_url, final_url);
1232 }
1233 }
1234
1235 // Tests that the extension ruleset is active only when the extension is
1236 // enabled.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,Enable_Disable_Reload_Uninstall)1237 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
1238 Enable_Disable_Reload_Uninstall) {
1239 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
1240
1241 // Block all main frame requests to "index.html".
1242 TestRule rule = CreateGenericRule();
1243 rule.condition->url_filter = std::string("index.html");
1244 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
1245 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
1246 const ExtensionId extension_id = last_loaded_extension_id();
1247
1248 // Add dynamic rule to block requests to "page.html".
1249 rule.condition->url_filter = std::string("page.html");
1250 ASSERT_NO_FATAL_FAILURE(AddDynamicRules(extension_id, {rule}));
1251
1252 GURL static_rule_url = embedded_test_server()->GetURL(
1253 "example.com", "/pages_with_script/index.html");
1254 GURL dynamic_rule_url = embedded_test_server()->GetURL(
1255 "example.com", "/pages_with_script/page.html");
1256
1257 auto test_extension_enabled = [&](bool expected_enabled) {
1258 EXPECT_EQ(expected_enabled,
1259 ExtensionRegistry::Get(profile())->enabled_extensions().Contains(
1260 extension_id));
1261
1262 // If the extension is enabled, both the |static_rule_url| and
1263 // |dynamic_rule_url| should be blocked.
1264 EXPECT_EQ(expected_enabled, IsNavigationBlocked(static_rule_url));
1265 EXPECT_EQ(expected_enabled, IsNavigationBlocked(dynamic_rule_url));
1266 };
1267
1268 {
1269 SCOPED_TRACE("Testing extension after load");
1270 EXPECT_EQ(1u, extensions_with_rulesets_count());
1271 test_extension_enabled(true);
1272 }
1273
1274 {
1275 SCOPED_TRACE("Testing DisableExtension");
1276 DisableExtension(extension_id);
1277 WaitForExtensionsWithRulesetsCount(0);
1278 test_extension_enabled(false);
1279 }
1280
1281 {
1282 SCOPED_TRACE("Testing EnableExtension");
1283 EnableExtension(extension_id);
1284 WaitForExtensionsWithRulesetsCount(1);
1285 test_extension_enabled(true);
1286 }
1287
1288 {
1289 SCOPED_TRACE("Testing ReloadExtension");
1290 // Don't use ExtensionBrowserTest::ReloadExtension since it waits for the
1291 // extension to be loaded again. But we need to use our custom waiting logic
1292 // below.
1293 extension_service()->ReloadExtension(extension_id);
1294 WaitForExtensionsWithRulesetsCount(0);
1295 WaitForExtensionsWithRulesetsCount(1);
1296 test_extension_enabled(true);
1297 }
1298
1299 {
1300 SCOPED_TRACE("Testing UninstallExtension");
1301 UninstallExtension(extension_id);
1302 WaitForExtensionsWithRulesetsCount(0);
1303 test_extension_enabled(false);
1304 }
1305 }
1306
1307 // Tests that multiple enabled extensions with declarative rulesets having
1308 // blocking rules behave correctly.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,BlockRequests_MultipleExtensions)1309 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
1310 BlockRequests_MultipleExtensions) {
1311 struct {
1312 std::string url_filter;
1313 int id;
1314 bool add_to_first_extension;
1315 bool add_to_second_extension;
1316 } rules_data[] = {
1317 {"block_both.com", 1, true, true},
1318 {"block_first.com", 2, true, false},
1319 {"block_second.com", 3, false, true},
1320 };
1321
1322 std::vector<TestRule> rules_1, rules_2;
1323 for (const auto& rule_data : rules_data) {
1324 TestRule rule = CreateGenericRule();
1325 rule.condition->url_filter = rule_data.url_filter;
1326 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
1327 rule.id = rule_data.id;
1328 if (rule_data.add_to_first_extension)
1329 rules_1.push_back(rule);
1330 if (rule_data.add_to_second_extension)
1331 rules_2.push_back(rule);
1332 }
1333
1334 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
1335 rules_1, "extension_1", {URLPattern::kAllUrlsPattern}));
1336 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
1337 rules_2, "extension_2", {URLPattern::kAllUrlsPattern}));
1338
1339 struct {
1340 std::string host;
1341 bool expect_main_frame_loaded;
1342 } test_cases[] = {
1343 {"block_both.com", false},
1344 {"block_first.com", false},
1345 {"block_second.com", false},
1346 {"block_none.com", true},
1347 };
1348
1349 for (const auto& test_case : test_cases) {
1350 GURL url = embedded_test_server()->GetURL(test_case.host,
1351 "/pages_with_script/page.html");
1352 SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str()));
1353
1354 ui_test_utils::NavigateToURL(browser(), url);
1355 EXPECT_EQ(test_case.expect_main_frame_loaded,
1356 WasFrameWithScriptLoaded(GetMainFrame()));
1357 content::PageType expected_page_type = test_case.expect_main_frame_loaded
1358 ? content::PAGE_TYPE_NORMAL
1359 : content::PAGE_TYPE_ERROR;
1360 EXPECT_EQ(expected_page_type, GetPageType());
1361 }
1362 }
1363
1364 // Tests that multiple enabled extensions with declarative rulesets having
1365 // redirect rules behave correctly with preference given to more recently
1366 // installed extensions.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,RedirectRequests_MultipleExtensions)1367 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
1368 RedirectRequests_MultipleExtensions) {
1369 const int kNumExtensions = 4;
1370
1371 auto redirect_url_for_extension_number = [this](size_t num) {
1372 return embedded_test_server()
1373 ->GetURL(std::to_string(num) + ".com", "/pages_with_script/index.html")
1374 .spec();
1375 };
1376
1377 TestRule rule = CreateGenericRule();
1378 rule.id = kMinValidID;
1379 rule.priority = kMinValidPriority;
1380 rule.action->type = std::string("redirect");
1381 rule.condition->url_filter = std::string("example.com");
1382 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
1383
1384 base::Time last_extension_install_time = base::Time::Min();
1385
1386 // Add |kNumExtensions| each redirecting example.com to a different redirect
1387 // url.
1388 for (size_t i = 1; i <= kNumExtensions; ++i) {
1389 rule.action->redirect.emplace();
1390 rule.action->redirect->url = redirect_url_for_extension_number(i);
1391 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
1392 {rule}, std::to_string(i), {URLPattern::kAllUrlsPattern}));
1393
1394 // Verify that the install time of this extension is greater than the last
1395 // extension.
1396 base::Time install_time = ExtensionPrefs::Get(profile())->GetInstallTime(
1397 last_loaded_extension_id());
1398 EXPECT_GT(install_time, last_extension_install_time);
1399 last_extension_install_time = install_time;
1400 }
1401
1402 // Load a url on example.com. It should be redirected to the redirect url
1403 // corresponding to the most recently installed extension.
1404 GURL url = embedded_test_server()->GetURL("example.com",
1405 "/pages_with_script/page.html");
1406 ui_test_utils::NavigateToURL(browser(), url);
1407 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1408 EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
1409 GURL final_url = web_contents()->GetLastCommittedURL();
1410 EXPECT_EQ(GURL(redirect_url_for_extension_number(kNumExtensions)), final_url);
1411 }
1412
1413 // Tests a combination of blocking and redirect rules.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,BlockAndRedirect)1414 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, BlockAndRedirect) {
1415 auto get_url_for_host = [this](std::string hostname) {
1416 return embedded_test_server()
1417 ->GetURL(hostname, "/pages_with_script/index.html")
1418 .spec();
1419 };
1420
1421 struct {
1422 std::string url_filter;
1423 int id;
1424 int priority;
1425 std::string action_type;
1426 base::Optional<std::string> redirect_url;
1427 } rules_data[] = {
1428 {"example.com", 1, 1, "redirect", get_url_for_host("yahoo.com")},
1429 {"yahoo.com", 2, 1, "redirect", get_url_for_host("google.com")},
1430 {"abc.com", 3, 1, "redirect", get_url_for_host("def.com")},
1431 {"def.com", 4, 2, "block", base::nullopt},
1432 {"def.com", 5, 1, "redirect", get_url_for_host("xyz.com")},
1433 {"ghi*", 6, 1, "redirect", get_url_for_host("ghijk.com")},
1434 };
1435
1436 // Load the extension.
1437 std::vector<TestRule> rules;
1438 for (const auto& rule_data : rules_data) {
1439 TestRule rule = CreateGenericRule();
1440 rule.condition->url_filter = rule_data.url_filter;
1441 rule.id = rule_data.id;
1442 rule.priority = rule_data.priority;
1443 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
1444 rule.action->type = rule_data.action_type;
1445 rule.action->redirect.emplace();
1446 rule.action->redirect->url = rule_data.redirect_url;
1447 rules.push_back(rule);
1448 }
1449 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
1450 rules, "test_extension", {URLPattern::kAllUrlsPattern}));
1451
1452 struct {
1453 std::string hostname;
1454 bool expected_main_frame_loaded;
1455 base::Optional<GURL> expected_final_url;
1456 base::Optional<size_t> expected_redirect_chain_length;
1457 } test_cases[] = {
1458 // example.com -> yahoo.com -> google.com.
1459 {"example.com", true, GURL(get_url_for_host("google.com")), 3},
1460 // yahoo.com -> google.com.
1461 {"yahoo.com", true, GURL(get_url_for_host("google.com")), 2},
1462 // abc.com -> def.com (blocked).
1463 // Note def.com won't be redirected since blocking rules are given
1464 // priority over redirect rules.
1465 {"abc.com", false, base::nullopt, base::nullopt},
1466 // def.com (blocked).
1467 {"def.com", false, base::nullopt, base::nullopt},
1468 // ghi.com -> ghijk.com.
1469 // Though ghijk.com still matches the redirect rule for |ghi*|, it will
1470 // not redirect to itself.
1471 {"ghi.com", true, GURL(get_url_for_host("ghijk.com")), 2},
1472 };
1473
1474 for (const auto& test_case : test_cases) {
1475 std::string url = get_url_for_host(test_case.hostname);
1476 SCOPED_TRACE(base::StringPrintf("Testing %s", url.c_str()));
1477
1478 ui_test_utils::NavigateToURL(browser(), GURL(url));
1479 EXPECT_EQ(test_case.expected_main_frame_loaded,
1480 WasFrameWithScriptLoaded(GetMainFrame()));
1481
1482 if (!test_case.expected_final_url) {
1483 EXPECT_EQ(content::PAGE_TYPE_ERROR, GetPageType());
1484 } else {
1485 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1486
1487 GURL final_url = web_contents()->GetLastCommittedURL();
1488 EXPECT_EQ(*test_case.expected_final_url, final_url);
1489
1490 EXPECT_EQ(*test_case.expected_redirect_chain_length,
1491 web_contents()
1492 ->GetController()
1493 .GetLastCommittedEntry()
1494 ->GetRedirectChain()
1495 .size());
1496 }
1497 }
1498 }
1499
1500 // Tests that the redirect url within an extension ruleset is chosen based on
1501 // the highest priority matching rule.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,RedirectPriority)1502 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, RedirectPriority) {
1503 const size_t kNumPatternTypes = 7;
1504
1505 auto hostname_for_number = [](size_t num) {
1506 return std::to_string(num) + ".com";
1507 };
1508
1509 auto redirect_url_for_priority = [this,
1510 hostname_for_number](size_t priority) {
1511 return embedded_test_server()
1512 ->GetURL(hostname_for_number(priority + 1),
1513 "/pages_with_script/index.html")
1514 .spec();
1515 };
1516
1517 std::vector<TestRule> rules;
1518 TestRule rule = CreateGenericRule();
1519 int id = kMinValidID;
1520 for (size_t i = 1; i <= kNumPatternTypes; i++) {
1521 // For pattern type |i|, add |i| rules with priority from 1 to |i|, each
1522 // with a different redirect url.
1523 std::string pattern = hostname_for_number(i) + "*page.html";
1524
1525 for (size_t j = 1; j <= i; j++) {
1526 rule.id = id++;
1527 rule.priority = j;
1528 rule.action->type = std::string("redirect");
1529 rule.action->redirect.emplace();
1530 rule.action->redirect->url = redirect_url_for_priority(j);
1531 rule.condition->url_filter = pattern;
1532 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
1533 rules.push_back(rule);
1534 }
1535 }
1536
1537 // Shuffle the rules to ensure that the order in which rules are added has no
1538 // effect on the test.
1539 base::RandomShuffle(rules.begin(), rules.end());
1540 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
1541 rules, "test_extension", {URLPattern::kAllUrlsPattern}));
1542
1543 for (size_t i = 0; i <= kNumPatternTypes + 1; ++i) {
1544 GURL url = embedded_test_server()->GetURL(hostname_for_number(i),
1545 "/pages_with_script/page.html");
1546 SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str()));
1547
1548 ui_test_utils::NavigateToURL(browser(), url);
1549 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1550 EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
1551 GURL final_url = web_contents()->GetLastCommittedURL();
1552
1553 bool expect_redirection = i >= 1 && i <= kNumPatternTypes;
1554 if (expect_redirection) {
1555 // For pattern type |i|, the highest prioirity for any rule was |i|.
1556 EXPECT_EQ(GURL(redirect_url_for_priority(i)), final_url);
1557 } else {
1558 EXPECT_EQ(url, final_url);
1559 }
1560 }
1561 }
1562
1563 // Test that upgradeScheme rules will change the scheme of matching requests to
1564 // https.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,UpgradeRules)1565 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, UpgradeRules) {
1566 auto get_url_for_host = [this](std::string hostname, const char* scheme) {
1567 GURL url = embedded_test_server()->GetURL(hostname,
1568 "/pages_with_script/index.html");
1569
1570 url::Replacements<char> replacements;
1571 replacements.SetScheme(scheme, url::Component(0, strlen(scheme)));
1572
1573 return url.ReplaceComponents(replacements);
1574 };
1575
1576 GURL google_url = get_url_for_host("google.com", url::kHttpScheme);
1577 struct {
1578 std::string url_filter;
1579 int id;
1580 int priority;
1581 std::string action_type;
1582 base::Optional<std::string> redirect_url;
1583 } rules_data[] = {
1584 {"exa*", 1, 4, "upgradeScheme", base::nullopt},
1585 {"|http:*yahoo", 2, 100, "redirect", "http://other.com"},
1586 // Since the test server can only display http requests, redirect all
1587 // https requests to google.com in the end.
1588 // TODO(crbug.com/985104): Add a https test server to display https pages
1589 // so this redirect rule can be removed.
1590 {"|https*", 3, 6, "redirect", google_url.spec()},
1591 {"exact.com", 4, 5, "block", base::nullopt},
1592 };
1593
1594 // Load the extension.
1595 std::vector<TestRule> rules;
1596 for (const auto& rule_data : rules_data) {
1597 TestRule rule = CreateGenericRule();
1598 rule.condition->url_filter = rule_data.url_filter;
1599 rule.id = rule_data.id;
1600 rule.priority = rule_data.priority;
1601 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
1602 rule.action->type = rule_data.action_type;
1603 rule.action->redirect.emplace();
1604 rule.action->redirect->url = rule_data.redirect_url;
1605 rules.push_back(rule);
1606 }
1607
1608 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
1609 rules, "test_extension", {URLPattern::kAllUrlsPattern}));
1610
1611 // Now load an extension with another ruleset, except this extension has no
1612 // host permissions.
1613 TestRule upgrade_rule = CreateGenericRule();
1614 upgrade_rule.condition->url_filter = "yahoo";
1615 upgrade_rule.id = kMinValidID;
1616 upgrade_rule.priority = kMinValidPriority;
1617 upgrade_rule.condition->resource_types =
1618 std::vector<std::string>({"main_frame"});
1619 upgrade_rule.action->type = "upgradeScheme";
1620
1621 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
1622 std::vector<TestRule>({upgrade_rule}), "test_extension_2", {}));
1623
1624 struct {
1625 std::string hostname;
1626 const char* scheme;
1627 // |expected_final_url| is null if the request is expected to be blocked.
1628 base::Optional<GURL> expected_final_url;
1629 } test_cases[] = {
1630 {"exact.com", url::kHttpScheme, base::nullopt},
1631 // http://example.com -> https://example.com/ -> http://google.com
1632 {"example.com", url::kHttpScheme, google_url},
1633 // test_extension_2 should upgrade the scheme for http://yahoo.com
1634 // despite having no host permissions. Note that this request is not
1635 // matched with test_extension_1's ruleset as test_extension_2 is
1636 // installed more recently.
1637 // http://yahoo.com -> https://yahoo.com/ -> http://google.com
1638 {"yahoo.com", url::kHttpScheme, google_url},
1639 };
1640
1641 for (const auto& test_case : test_cases) {
1642 GURL url = get_url_for_host(test_case.hostname, test_case.scheme);
1643 SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str()));
1644
1645 ui_test_utils::NavigateToURL(browser(), url);
1646
1647 if (!test_case.expected_final_url) {
1648 EXPECT_EQ(content::PAGE_TYPE_ERROR, GetPageType());
1649 } else {
1650 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1651
1652 const GURL& final_url = web_contents()->GetLastCommittedURL();
1653 EXPECT_EQ(*test_case.expected_final_url, final_url);
1654 }
1655 }
1656 }
1657
1658 // Tests that only extensions enabled in incognito mode affect network requests
1659 // from an incognito context.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,BlockRequests_Incognito)1660 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
1661 BlockRequests_Incognito) {
1662 // Block all main-frame requests to example.com.
1663 TestRule rule = CreateGenericRule();
1664 rule.condition->url_filter = std::string("example.com");
1665 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
1666 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
1667
1668 ExtensionId extension_id = last_loaded_extension_id();
1669 GURL url = embedded_test_server()->GetURL("example.com",
1670 "/pages_with_script/page.html");
1671 Browser* incognito_browser = CreateIncognitoBrowser();
1672
1673 auto test_enabled_in_incognito = [&](bool expected_enabled_in_incognito) {
1674 EXPECT_EQ(expected_enabled_in_incognito,
1675 util::IsIncognitoEnabled(extension_id, profile()));
1676
1677 // The url should be blocked in normal context.
1678 ui_test_utils::NavigateToURL(browser(), url);
1679 EXPECT_FALSE(WasFrameWithScriptLoaded(GetMainFrame()));
1680 EXPECT_EQ(content::PAGE_TYPE_ERROR, GetPageType());
1681
1682 // In incognito context, the url should be blocked if the extension is
1683 // enabled in incognito mode.
1684 ui_test_utils::NavigateToURL(incognito_browser, url);
1685 EXPECT_EQ(!expected_enabled_in_incognito,
1686 WasFrameWithScriptLoaded(GetMainFrame(incognito_browser)));
1687 content::PageType expected_page_type = expected_enabled_in_incognito
1688 ? content::PAGE_TYPE_ERROR
1689 : content::PAGE_TYPE_NORMAL;
1690 EXPECT_EQ(expected_page_type, GetPageType(incognito_browser));
1691 };
1692
1693 {
1694 // By default, the extension will be disabled in incognito.
1695 SCOPED_TRACE("Testing extension after load");
1696 test_enabled_in_incognito(false);
1697 }
1698
1699 {
1700 // Enable the extension in incognito mode.
1701 SCOPED_TRACE("Testing extension after enabling it in incognito");
1702 util::SetIsIncognitoEnabled(extension_id, profile(), true /*enabled*/);
1703 // Toggling the incognito mode reloads the extension. Wait for it to reload.
1704 WaitForExtensionsWithRulesetsCount(0);
1705 WaitForExtensionsWithRulesetsCount(1);
1706 test_enabled_in_incognito(true);
1707 }
1708
1709 {
1710 // Disable the extension in incognito mode.
1711 SCOPED_TRACE("Testing extension after disabling it in incognito");
1712 util::SetIsIncognitoEnabled(extension_id, profile(), false /*enabled*/);
1713 // Toggling the incognito mode reloads the extension. Wait for it to reload.
1714 WaitForExtensionsWithRulesetsCount(0);
1715 WaitForExtensionsWithRulesetsCount(1);
1716 test_enabled_in_incognito(false);
1717 }
1718 }
1719
1720 // Ensure extensions can't intercept chrome:// urls, even after explicitly
1721 // requesting access to <all_urls>.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,ChromeURLS)1722 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, ChromeURLS) {
1723 // Have the extension block all chrome:// urls.
1724 TestRule rule = CreateGenericRule();
1725 rule.condition->url_filter =
1726 std::string(content::kChromeUIScheme) + url::kStandardSchemeSeparator;
1727 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
1728
1729 std::vector<const char*> test_urls = {
1730 chrome::kChromeUIFlagsURL, chrome::kChromeUIAboutURL,
1731 chrome::kChromeUIExtensionsURL, chrome::kChromeUIVersionURL};
1732
1733 for (const char* url : test_urls) {
1734 ui_test_utils::NavigateToURL(browser(), GURL(url));
1735 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1736 }
1737 }
1738
1739 // Test that a packed extension with a DNR ruleset behaves correctly after
1740 // browser restart.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,PRE_BrowserRestart)1741 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,
1742 PRE_BrowserRestart) {
1743 // This is not tested for unpacked extensions since the unpacked extension
1744 // directory won't be persisted across browser restarts.
1745 ASSERT_EQ(ExtensionLoadType::PACKED, GetParam());
1746
1747 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
1748
1749 // Block all main frame requests to "index.html".
1750 TestRule rule = CreateGenericRule();
1751 rule.condition->url_filter = std::string("index.html");
1752 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
1753 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
1754 const ExtensionId extension_id = last_loaded_extension_id();
1755
1756 // Add dynamic rule to block main-frame requests to "page.html".
1757 rule.condition->url_filter = std::string("page.html");
1758 ASSERT_NO_FATAL_FAILURE(AddDynamicRules(extension_id, {rule}));
1759
1760 EXPECT_TRUE(IsNavigationBlocked(embedded_test_server()->GetURL(
1761 "example.com", "/pages_with_script/index.html")));
1762 EXPECT_TRUE(IsNavigationBlocked(embedded_test_server()->GetURL(
1763 "example.com", "/pages_with_script/page.html")));
1764 EXPECT_FALSE(IsNavigationBlocked(embedded_test_server()->GetURL(
1765 "example.com", "/pages_with_script/page2.html")));
1766 }
1767
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,BrowserRestart)1768 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,
1769 BrowserRestart) {
1770 // This is not tested for unpacked extensions since the unpacked extension
1771 // directory won't be persisted across browser restarts.
1772 ASSERT_EQ(ExtensionLoadType::PACKED, GetParam());
1773
1774 // Ensure that the DNR extension enabled in previous browser session still
1775 // correctly blocks network requests.
1776 EXPECT_TRUE(IsNavigationBlocked(embedded_test_server()->GetURL(
1777 "example.com", "/pages_with_script/index.html")));
1778 EXPECT_TRUE(IsNavigationBlocked(embedded_test_server()->GetURL(
1779 "example.com", "/pages_with_script/page.html")));
1780 EXPECT_FALSE(IsNavigationBlocked(embedded_test_server()->GetURL(
1781 "example.com", "/pages_with_script/page2.html")));
1782 }
1783
1784 // Tests than an extension can omit the "declarative_net_request" manifest key
1785 // but can still use dynamic rules.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,ZeroRulesets)1786 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, ZeroRulesets) {
1787 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript |
1788 ConfigFlag::kConfig_OmitDeclarativeNetRequestKey);
1789
1790 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRulesets(
1791 {} /* rulesets */, "extension_directory", {} /* hosts */));
1792 const ExtensionId extension_id = last_loaded_extension_id();
1793
1794 const GURL url = embedded_test_server()->GetURL(
1795 "example.com", "/pages_with_script/page.html");
1796 EXPECT_FALSE(IsNavigationBlocked(url));
1797
1798 // Add dynamic rule to block main-frame requests to "page.html".
1799 TestRule rule = CreateGenericRule();
1800 rule.condition->url_filter = std::string("page.html");
1801 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
1802 EXPECT_EQ(0u, extensions_with_rulesets_count());
1803 ASSERT_NO_FATAL_FAILURE(AddDynamicRules(extension_id, {rule}));
1804 WaitForExtensionsWithRulesetsCount(1);
1805
1806 EXPECT_TRUE(IsNavigationBlocked(url));
1807
1808 DisableExtension(extension_id);
1809 WaitForExtensionsWithRulesetsCount(0);
1810 EXPECT_FALSE(IsNavigationBlocked(url));
1811
1812 EnableExtension(extension_id);
1813 WaitForExtensionsWithRulesetsCount(1);
1814 EXPECT_TRUE(IsNavigationBlocked(url));
1815 }
1816
1817 // Test an extension with multiple static rulesets.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,MultipleRulesets)1818 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, MultipleRulesets) {
1819 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
1820
1821 const int kNumStaticRulesets = 4;
1822 const char* kStaticFilterPrefix = "static";
1823 std::vector<GURL> expected_blocked_urls;
1824 std::vector<TestRulesetInfo> rulesets;
1825 std::vector<GURL> expected_allowed_urls;
1826 for (int i = 0; i < kNumStaticRulesets; ++i) {
1827 std::vector<TestRule> rules;
1828 std::string id = kStaticFilterPrefix + base::NumberToString(i);
1829 rules.push_back(CreateMainFrameBlockRule(id));
1830
1831 // Enable even indexed rulesets by default.
1832 bool enabled = i % 2 == 0;
1833 if (enabled)
1834 expected_blocked_urls.push_back(GetURLForFilter(id));
1835 else
1836 expected_allowed_urls.push_back(GetURLForFilter(id));
1837
1838 rulesets.emplace_back(id, *ToListValue(rules), enabled);
1839 }
1840
1841 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRulesets(
1842 rulesets, "extension_directory", {} /* hosts */));
1843 const ExtensionId extension_id = last_loaded_extension_id();
1844
1845 // Also add a dynamic rule blocking pages with string "dynamic".
1846 const char* kDynamicFilter = "dynamic";
1847 ASSERT_NO_FATAL_FAILURE(AddDynamicRules(
1848 extension_id, {CreateMainFrameBlockRule(kDynamicFilter)}));
1849 expected_blocked_urls.push_back(GetURLForFilter(kDynamicFilter));
1850
1851 expected_allowed_urls.push_back(GetURLForFilter("no_such_rule"));
1852
1853 VerifyNavigations(expected_blocked_urls, expected_allowed_urls);
1854 }
1855
1856 // Ensure that Blink's in-memory cache is cleared on adding/removing rulesets.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,RendererCacheCleared)1857 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, RendererCacheCleared) {
1858 // Observe requests to RulesetManager to monitor requests to script.js.
1859 GURL observed_url =
1860 embedded_test_server()->GetURL("example.com", "/cached/script.js");
1861
1862 GURL url = embedded_test_server()->GetURL(
1863 "example.com", "/cached/page_with_cacheable_script.html");
1864
1865 // With no extension loaded, the request to the script should succeed.
1866 ui_test_utils::NavigateToURL(browser(), url);
1867 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1868 EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
1869
1870 // NOTE: RulesetMatcher will not see network requests if no rulesets are
1871 // active.
1872 bool expect_request_seen =
1873 base::FeatureList::IsEnabled(
1874 extensions_features::kForceWebRequestProxyForTest);
1875 EXPECT_EQ(expect_request_seen,
1876 base::Contains(ruleset_manager_observer()->GetAndResetRequestSeen(),
1877 observed_url));
1878
1879 // Another request to |url| should not cause a network request for
1880 // script.js since it will be served by the renderer's in-memory
1881 // cache.
1882 ui_test_utils::NavigateToURL(browser(), url);
1883 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1884 EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
1885 EXPECT_FALSE(base::Contains(
1886 ruleset_manager_observer()->GetAndResetRequestSeen(), observed_url));
1887
1888 // Now block requests to script.js.
1889 TestRule rule = CreateGenericRule();
1890 rule.condition->url_filter = std::string("script.js");
1891 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
1892
1893 // Adding an extension ruleset should have cleared the renderer's in-memory
1894 // cache. Hence the browser process will observe the request to
1895 // script.js and block it.
1896 ui_test_utils::NavigateToURL(browser(), url);
1897 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1898 EXPECT_FALSE(WasFrameWithScriptLoaded(GetMainFrame()));
1899 EXPECT_TRUE(base::Contains(
1900 ruleset_manager_observer()->GetAndResetRequestSeen(), observed_url));
1901
1902 // Disable the extension.
1903 DisableExtension(last_loaded_extension_id());
1904 WaitForExtensionsWithRulesetsCount(0);
1905
1906 // Disabling the extension should cause the request to succeed again. The
1907 // request for the script will again be observed by the browser since it's not
1908 // in the renderer's in-memory cache.
1909 ui_test_utils::NavigateToURL(browser(), url);
1910 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1911 EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
1912 EXPECT_EQ(expect_request_seen,
1913 base::Contains(ruleset_manager_observer()->GetAndResetRequestSeen(),
1914 observed_url));
1915 }
1916
1917 // Tests that proxy requests aren't intercepted. See https://crbug.com/794674.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,PacRequestsBypassRules)1918 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
1919 PacRequestsBypassRules) {
1920 // Load the extension.
1921 std::vector<TestRule> rules;
1922 TestRule rule = CreateGenericRule();
1923 rule.condition->url_filter = std::string("*pac");
1924 rule.id = 1;
1925 rules.push_back(rule);
1926 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules));
1927
1928 // Configure a PAC script. Need to do this after the extension is loaded, so
1929 // that the PAC isn't already loaded by the time the extension starts
1930 // affecting requests.
1931 PrefService* pref_service = browser()->profile()->GetPrefs();
1932 pref_service->Set(proxy_config::prefs::kProxy,
1933 ProxyConfigDictionary::CreatePacScript(
1934 embedded_test_server()->GetURL("/self.pac").spec(),
1935 true /* pac_mandatory */));
1936 // Flush the proxy configuration change over the Mojo pipe to avoid any races.
1937 ProfileNetworkContextServiceFactory::GetForContext(browser()->profile())
1938 ->FlushProxyConfigMonitorForTesting();
1939
1940 // Verify that the extension can't intercept the network request.
1941 ui_test_utils::NavigateToURL(
1942 browser(),
1943 GURL("http://does.not.resolve.test/pages_with_script/page.html"));
1944 EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
1945 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1946 }
1947
1948 // Ensure that an extension can't intercept requests on the chrome-extension
1949 // scheme.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,InterceptExtensionScheme)1950 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
1951 InterceptExtensionScheme) {
1952 // Load two extensions. One blocks all urls, and the other blocks urls with
1953 // "google.com" as a substring.
1954 std::vector<TestRule> rules_1;
1955 TestRule rule = CreateGenericRule();
1956 rule.condition->url_filter = std::string("*");
1957 rules_1.push_back(rule);
1958
1959 std::vector<TestRule> rules_2;
1960 rule = CreateGenericRule();
1961 rule.condition->url_filter = std::string("google.com");
1962 rules_2.push_back(rule);
1963
1964 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
1965 rules_1, "extension_1", {URLPattern::kAllUrlsPattern}));
1966 const std::string extension_id_1 = last_loaded_extension_id();
1967
1968 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
1969 rules_2, "extension_2", {URLPattern::kAllUrlsPattern}));
1970 const std::string extension_id_2 = last_loaded_extension_id();
1971
1972 auto get_manifest_url = [](const ExtensionId& extension_id) {
1973 return GURL(base::StringPrintf("%s://%s/manifest.json",
1974 extensions::kExtensionScheme,
1975 extension_id.c_str()));
1976 };
1977
1978 // Extension 1 should not be able to block the request to its own
1979 // manifest.json or that of the Extension 2, even with "<all_urls>" host
1980 // permissions.
1981 ui_test_utils::NavigateToURL(browser(), get_manifest_url(extension_id_1));
1982 GURL final_url = web_contents()->GetLastCommittedURL();
1983 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1984 ui_test_utils::NavigateToURL(browser(), get_manifest_url(extension_id_2));
1985 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1986 }
1987
1988 // Ensures that any <img> elements blocked by the API are collapsed.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,ImageCollapsed)1989 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, ImageCollapsed) {
1990 // Loads a page with an image and returns whether the image was collapsed.
1991 auto is_image_collapsed = [this]() {
1992 ui_test_utils::NavigateToURL(
1993 browser(), embedded_test_server()->GetURL("google.com", "/image.html"));
1994 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
1995 bool is_image_collapsed = false;
1996 const std::string script =
1997 "domAutomationController.send(!!window.imageCollapsed);";
1998 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents(), script,
1999 &is_image_collapsed));
2000 return is_image_collapsed;
2001 };
2002
2003 // Initially the image shouldn't be collapsed.
2004 EXPECT_FALSE(is_image_collapsed());
2005
2006 // Load an extension which blocks all images.
2007 std::vector<TestRule> rules;
2008 TestRule rule = CreateGenericRule();
2009 rule.condition->url_filter = std::string("*");
2010 rule.condition->resource_types = std::vector<std::string>({"image"});
2011 rules.push_back(rule);
2012 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules));
2013
2014 // Now the image request should be blocked and the corresponding DOM element
2015 // should be collapsed.
2016 EXPECT_TRUE(is_image_collapsed());
2017 }
2018
2019 // Ensures that any <iframe> elements whose document load is blocked by the API,
2020 // are collapsed.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,IFrameCollapsed)2021 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, IFrameCollapsed) {
2022 // Verifies whether the frame with name |frame_name| is collapsed.
2023 auto test_frame_collapse = [this](const std::string& frame_name,
2024 bool expect_collapsed) {
2025 SCOPED_TRACE(base::StringPrintf("Testing frame %s", frame_name.c_str()));
2026 content::RenderFrameHost* frame = GetFrameByName(frame_name);
2027 ASSERT_TRUE(frame);
2028
2029 EXPECT_EQ(!expect_collapsed, WasFrameWithScriptLoaded(frame));
2030
2031 constexpr char kScript[] = R"(
2032 var iframe = document.getElementsByName('%s')[0];
2033 var collapsed = iframe.clientWidth === 0 && iframe.clientHeight === 0;
2034 domAutomationController.send(collapsed);
2035 )";
2036 bool collapsed = false;
2037 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
2038 GetMainFrame(), base::StringPrintf(kScript, frame_name.c_str()),
2039 &collapsed));
2040 EXPECT_EQ(expect_collapsed, collapsed);
2041 };
2042
2043 const std::string kFrameName1 = "frame1";
2044 const std::string kFrameName2 = "frame2";
2045 const GURL page_url = embedded_test_server()->GetURL(
2046 "google.com", "/page_with_two_frames.html");
2047
2048 // Load a page with two iframes (|kFrameName1| and |kFrameName2|). Initially
2049 // both the frames should be loaded successfully.
2050 ui_test_utils::NavigateToURL(browser(), page_url);
2051 ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
2052 {
2053 SCOPED_TRACE("No extension loaded");
2054 test_frame_collapse(kFrameName1, false);
2055 test_frame_collapse(kFrameName2, false);
2056 }
2057
2058 // Now load an extension which blocks all requests with "frame=1" in its url.
2059 TestRule rule = CreateGenericRule();
2060 rule.condition->url_filter = std::string("frame=1");
2061 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
2062
2063 // Reloading the page should cause |kFrameName1| to be collapsed.
2064 ui_test_utils::NavigateToURL(browser(), page_url);
2065 ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
2066 {
2067 SCOPED_TRACE("Extension loaded initial");
2068 test_frame_collapse(kFrameName1, true);
2069 test_frame_collapse(kFrameName2, false);
2070 }
2071
2072 // Now interchange the "src" of the two frames. This should cause
2073 // |kFrameName2| to be collapsed and |kFrameName1| to be un-collapsed.
2074 GURL frame_url_1 = GetFrameByName(kFrameName1)->GetLastCommittedURL();
2075 GURL frame_url_2 = GetFrameByName(kFrameName2)->GetLastCommittedURL();
2076 NavigateFrame(kFrameName1, frame_url_2);
2077 NavigateFrame(kFrameName2, frame_url_1);
2078 {
2079 SCOPED_TRACE("Extension loaded src swapped");
2080 test_frame_collapse(kFrameName1, false);
2081 test_frame_collapse(kFrameName2, true);
2082 }
2083 }
2084
2085 // Tests that we correctly reindex a corrupted ruleset. This is only tested for
2086 // packed extensions, since the JSON ruleset is reindexed on each extension
2087 // load for unpacked extensions, so corruption is not an issue.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,CorruptedIndexedRuleset)2088 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,
2089 CorruptedIndexedRuleset) {
2090 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
2091
2092 // Load an extension which blocks all main-frame requests to "google.com".
2093 TestRule rule = CreateGenericRule();
2094 rule.condition->url_filter = std::string("||google.com");
2095 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
2096 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
2097
2098 const ExtensionId extension_id = last_loaded_extension_id();
2099
2100 // Add a dynamic rule to block main-frame requests to "example.com".
2101 rule.condition->url_filter = std::string("||example.com");
2102 ASSERT_NO_FATAL_FAILURE(AddDynamicRules(extension_id, {rule}));
2103
2104 const GURL static_rule_url = embedded_test_server()->GetURL(
2105 "google.com", "/pages_with_script/index.html");
2106 const GURL dynamic_rule_url = embedded_test_server()->GetURL(
2107 "example.com", "/pages_with_script/index.html");
2108 const GURL unblocked_url = embedded_test_server()->GetURL(
2109 "yahoo.com", "/pages_with_script/index.html");
2110
2111 const Extension* extension = last_loaded_extension();
2112 std::vector<RulesetSource> static_sources =
2113 RulesetSource::CreateStatic(*extension);
2114 ASSERT_EQ(1u, static_sources.size());
2115 RulesetSource dynamic_source =
2116 RulesetSource::CreateDynamic(profile(), extension->id());
2117
2118 // Loading the extension should cause some main frame requests to be blocked.
2119 {
2120 SCOPED_TRACE("Page load after loading extension");
2121 EXPECT_TRUE(IsNavigationBlocked(static_rule_url));
2122 EXPECT_TRUE(IsNavigationBlocked(dynamic_rule_url));
2123 EXPECT_FALSE(IsNavigationBlocked(unblocked_url));
2124 }
2125
2126 // Helper to overwrite an indexed ruleset file with arbitrary data to mimic
2127 // corruption, while maintaining the correct version header.
2128 auto corrupt_file_for_checksum_mismatch =
2129 [](const base::FilePath& indexed_path) {
2130 base::ScopedAllowBlockingForTesting scoped_allow_blocking;
2131 std::string corrupted_data = GetVersionHeaderForTesting() + "data";
2132 ASSERT_TRUE(base::WriteFile(indexed_path, corrupted_data));
2133 };
2134
2135 // Helper to reload the extension and ensure it is working.
2136 auto test_extension_works_after_reload = [&]() {
2137 EXPECT_EQ(1u, extensions_with_rulesets_count());
2138 DisableExtension(extension_id);
2139 WaitForExtensionsWithRulesetsCount(0);
2140
2141 EnableExtension(extension_id);
2142 WaitForExtensionsWithRulesetsCount(1);
2143
2144 EXPECT_TRUE(IsNavigationBlocked(static_rule_url));
2145 EXPECT_TRUE(IsNavigationBlocked(dynamic_rule_url));
2146 EXPECT_FALSE(IsNavigationBlocked(unblocked_url));
2147 };
2148
2149 const char* kReindexHistogram =
2150 "Extensions.DeclarativeNetRequest.RulesetReindexSuccessful";
2151
2152 // Test static ruleset re-indexing.
2153 {
2154 SCOPED_TRACE("Static ruleset corruption");
2155 corrupt_file_for_checksum_mismatch(static_sources[0].indexed_path());
2156
2157 base::HistogramTester tester;
2158 test_extension_works_after_reload();
2159
2160 // Loading the static ruleset would fail initially due to checksum mismatch
2161 // but will succeed on re-indexing.
2162 tester.ExpectBucketCount(kReindexHistogram, true /*sample*/, 1 /*count*/);
2163 // Count of 2 because we load both static and dynamic rulesets.
2164 tester.ExpectBucketCount(kLoadRulesetResultHistogram,
2165 LoadRulesetResult::kSuccess /* sample */,
2166 2 /* count */);
2167 }
2168
2169 // Test dynamic ruleset re-indexing.
2170 {
2171 SCOPED_TRACE("Dynamic ruleset corruption");
2172 corrupt_file_for_checksum_mismatch(dynamic_source.indexed_path());
2173
2174 base::HistogramTester tester;
2175 test_extension_works_after_reload();
2176
2177 // Loading the dynamic ruleset would have failed initially due to checksum
2178 // mismatch and later succeeded on re-indexing.
2179 tester.ExpectBucketCount(kReindexHistogram, true /*sample*/, 1 /*count*/);
2180
2181 // Count of 2 because we load both static and dynamic rulesets.
2182 tester.ExpectBucketCount(kLoadRulesetResultHistogram,
2183 LoadRulesetResult::kSuccess /* sample */,
2184 2 /* count */);
2185 }
2186
2187 // Go crazy and corrupt both static and dynamic rulesets.
2188 {
2189 SCOPED_TRACE("Static and dynamic ruleset corruption");
2190 corrupt_file_for_checksum_mismatch(dynamic_source.indexed_path());
2191 corrupt_file_for_checksum_mismatch(static_sources[0].indexed_path());
2192
2193 base::HistogramTester tester;
2194 test_extension_works_after_reload();
2195
2196 // Loading the ruleset would have failed initially due to checksum mismatch
2197 // and later succeeded.
2198 tester.ExpectBucketCount(kReindexHistogram, true /*sample*/, 2 /*count*/);
2199 // Count of 2 because we load both static and dynamic rulesets.
2200 tester.ExpectBucketCount(kLoadRulesetResultHistogram,
2201 LoadRulesetResult::kSuccess /* sample */,
2202 2 /* count */);
2203 }
2204 }
2205
2206 // Tests that we surface a warning to the user if any of its ruleset fail to
2207 // load.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,WarningOnFailedRulesetLoad)2208 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
2209 WarningOnFailedRulesetLoad) {
2210 const size_t kNumStaticRulesets = 4;
2211 const char* kStaticFilterPrefix = "static";
2212 std::vector<TestRulesetInfo> rulesets;
2213 std::vector<GURL> urls_for_indices;
2214 for (size_t i = 0; i < kNumStaticRulesets; ++i) {
2215 std::vector<TestRule> rules;
2216 std::string id = kStaticFilterPrefix + base::NumberToString(i);
2217 rules.push_back(CreateMainFrameBlockRule(id));
2218
2219 urls_for_indices.push_back(GetURLForFilter(id));
2220
2221 rulesets.emplace_back(id, *ToListValue(rules));
2222 }
2223
2224 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRulesets(
2225 rulesets, "extension_directory", {} /* hosts */));
2226
2227 const ExtensionId extension_id = last_loaded_extension_id();
2228 EXPECT_TRUE(ruleset_manager()->GetMatcherForExtension(extension_id));
2229
2230 const Extension* extension = last_loaded_extension();
2231 ASSERT_TRUE(extension);
2232
2233 std::vector<RulesetSource> sources = RulesetSource::CreateStatic(*extension);
2234 ASSERT_EQ(kNumStaticRulesets, sources.size());
2235
2236 // Mimic extension prefs corruption by overwriting the indexed ruleset
2237 // checksum for the first, third and fourth rulesets.
2238 const int kInvalidRulesetChecksum = -1;
2239 std::vector<int> corrupted_ruleset_indices = {0, 2, 3};
2240 std::vector<int> non_corrupted_ruleset_indices = {1};
2241
2242 for (int index : corrupted_ruleset_indices) {
2243 ExtensionPrefs::Get(profile())->SetDNRStaticRulesetChecksum(
2244 extension_id, sources[index].id(), kInvalidRulesetChecksum);
2245 }
2246
2247 DisableExtension(extension_id);
2248 WaitForExtensionsWithRulesetsCount(0);
2249 EXPECT_FALSE(ruleset_manager()->GetMatcherForExtension(extension_id));
2250
2251 // Reindexing and loading corrupted rulesets should fail now. This should
2252 // cause a warning.
2253 base::HistogramTester tester;
2254 WarningService* warning_service = WarningService::Get(profile());
2255 WarningServiceObserver warning_observer(warning_service, extension_id);
2256 EnableExtension(extension_id);
2257 WaitForExtensionsWithRulesetsCount(1);
2258 EXPECT_TRUE(ruleset_manager()->GetMatcherForExtension(extension_id));
2259
2260 // Wait till we surface a warning.
2261 warning_observer.WaitForWarning();
2262 EXPECT_THAT(warning_service->GetWarningTypesAffectingExtension(extension_id),
2263 ::testing::ElementsAre(Warning::kRulesetFailedToLoad));
2264
2265 // Verify that loading the corrupted rulesets failed due to checksum mismatch.
2266 // The non-corrupted rulesets should load fine.
2267 tester.ExpectTotalCount(kLoadRulesetResultHistogram, rulesets.size());
2268 EXPECT_EQ(corrupted_ruleset_indices.size(),
2269 static_cast<size_t>(tester.GetBucketCount(
2270 kLoadRulesetResultHistogram,
2271 LoadRulesetResult::kErrorChecksumMismatch /*sample*/)));
2272 EXPECT_EQ(non_corrupted_ruleset_indices.size(),
2273 static_cast<size_t>(
2274 tester.GetBucketCount(kLoadRulesetResultHistogram,
2275 LoadRulesetResult::kSuccess /*sample*/)));
2276
2277 // Verify that re-indexing the corrupted rulesets failed.
2278 tester.ExpectUniqueSample(
2279 "Extensions.DeclarativeNetRequest.RulesetReindexSuccessful",
2280 false /*sample*/, corrupted_ruleset_indices.size() /*count*/);
2281
2282 // Finally verify that the non-corrupted rulesets still work fine, but others
2283 // don't.
2284 std::vector<GURL> expected_blocked_urls;
2285 for (int index : non_corrupted_ruleset_indices)
2286 expected_blocked_urls.push_back(urls_for_indices[index]);
2287
2288 std::vector<GURL> expected_allowed_urls;
2289 for (int index : corrupted_ruleset_indices)
2290 expected_allowed_urls.push_back(urls_for_indices[index]);
2291
2292 VerifyNavigations(expected_blocked_urls, expected_allowed_urls);
2293 }
2294
2295 // Tests that we reindex the extension rulesets in case its ruleset format
2296 // version is not the same as one used by Chrome.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,ReindexOnRulesetVersionMismatch)2297 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,
2298 ReindexOnRulesetVersionMismatch) {
2299 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
2300
2301 TestRule rule = CreateGenericRule();
2302 rule.condition->url_filter = std::string("*");
2303
2304 const int kNumStaticRulesets = 4;
2305 std::vector<TestRulesetInfo> rulesets;
2306 for (int i = 0; i < kNumStaticRulesets; ++i)
2307 rulesets.emplace_back(base::NumberToString(i), *ToListValue({rule}));
2308
2309 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRulesets(
2310 rulesets, "extension_directory", {} /* hosts */));
2311
2312 const ExtensionId extension_id = last_loaded_extension_id();
2313 EXPECT_TRUE(ruleset_manager()->GetMatcherForExtension(extension_id));
2314
2315 // Add a dynamic rule.
2316 AddDynamicRules(extension_id, {rule});
2317
2318 DisableExtension(extension_id);
2319 WaitForExtensionsWithRulesetsCount(0);
2320 EXPECT_FALSE(ruleset_manager()->GetMatcherForExtension(extension_id));
2321
2322 // Now change the current indexed ruleset format version. This should cause a
2323 // version mismatch when the extension is loaded again, but re-indexing should
2324 // still succeed.
2325 ScopedIncrementRulesetVersion scoped_version_change =
2326 CreateScopedIncrementRulesetVersionForTesting();
2327
2328 // Also override the checksum value for the indexed ruleset to simulate a
2329 // flatbuffer schema change. This will ensure that the checksum of the
2330 // re-indexed file differs from the current checksum.
2331 const int kTestChecksum = 100;
2332 OverrideGetChecksumForTest(kTestChecksum);
2333
2334 base::HistogramTester tester;
2335 EnableExtension(extension_id);
2336 WaitForExtensionsWithRulesetsCount(1);
2337 EXPECT_TRUE(ruleset_manager()->GetMatcherForExtension(extension_id));
2338
2339 // We add 1 to include the dynamic ruleset.
2340 const int kNumRulesets = kNumStaticRulesets + 1;
2341
2342 // Verify that loading the static and dynamic rulesets would cause reindexing
2343 // due to version header mismatch and later succeeded.
2344 tester.ExpectUniqueSample(
2345 "Extensions.DeclarativeNetRequest.RulesetReindexSuccessful",
2346 true /*sample*/, kNumRulesets /*count*/);
2347 EXPECT_EQ(kNumRulesets,
2348 tester.GetBucketCount(kLoadRulesetResultHistogram,
2349 LoadRulesetResult::kSuccess /*sample*/));
2350
2351 // Ensure that the new checksum was correctly persisted in prefs.
2352 const ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
2353 const Extension* extension = last_loaded_extension();
2354 std::vector<RulesetSource> static_sources =
2355 RulesetSource::CreateStatic(*extension);
2356 ASSERT_EQ(static_cast<size_t>(kNumStaticRulesets), static_sources.size());
2357
2358 int checksum = kTestChecksum + 1;
2359 for (const RulesetSource& source : static_sources) {
2360 EXPECT_TRUE(prefs->GetDNRStaticRulesetChecksum(extension_id, source.id(),
2361 &checksum));
2362 EXPECT_EQ(kTestChecksum, checksum);
2363
2364 // Reset checksum for the next test.
2365 checksum = kTestChecksum + 1;
2366 }
2367
2368 EXPECT_TRUE(prefs->GetDNRDynamicRulesetChecksum(extension_id, &checksum));
2369 EXPECT_EQ(kTestChecksum, checksum);
2370 }
2371
2372 // Tests that static ruleset preferences are deleted on uninstall for an edge
2373 // case where ruleset loading is completed after extension uninstallation.
2374 // Regression test for crbug.com/1067441.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,RulesetPrefsDeletedOnUninstall)2375 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
2376 RulesetPrefsDeletedOnUninstall) {
2377 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({} /* rules */));
2378
2379 const ExtensionId extension_id = last_loaded_extension_id();
2380 const Extension* extension = last_loaded_extension();
2381
2382 std::vector<RulesetSource> static_sources =
2383 RulesetSource::CreateStatic(*extension);
2384 ASSERT_EQ(1u, static_sources.size());
2385
2386 DisableExtension(extension_id);
2387 WaitForExtensionsWithRulesetsCount(0);
2388
2389 const ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
2390 int checksum = -1;
2391 EXPECT_TRUE(prefs->GetDNRStaticRulesetChecksum(
2392 extension_id, static_sources[0].id(), &checksum));
2393
2394 // Now change the current indexed ruleset format version. This should cause a
2395 // version mismatch when the extension is loaded again, but re-indexing should
2396 // still succeed.
2397 ScopedIncrementRulesetVersion scoped_version_change =
2398 CreateScopedIncrementRulesetVersionForTesting();
2399
2400 // Also override the checksum value for the indexed ruleset to simulate a
2401 // flatbuffer schema change. This will ensure that the checksum of the
2402 // re-indexed file differs from the current checksum.
2403 const int kTestChecksum = checksum + 1;
2404 OverrideGetChecksumForTest(kTestChecksum);
2405
2406 base::HistogramTester tester;
2407 RulesetLoadObserver load_observer(rules_monitor_service(), extension_id);
2408
2409 // Now enable the extension, causing the asynchronous extension ruleset load
2410 // which further results in an asynchronous re-indexing task. Immediately
2411 // uninstall the extension to ensure that the uninstallation precedes
2412 // completion of ruleset load.
2413 EnableExtension(extension_id);
2414 UninstallExtension(extension_id);
2415
2416 load_observer.Wait();
2417
2418 // Verify that reindexing succeeded.
2419 tester.ExpectUniqueSample(
2420 "Extensions.DeclarativeNetRequest.RulesetReindexSuccessful",
2421 true /*sample*/, 1 /*count*/);
2422
2423 // Verify that the prefs for the static ruleset were deleted successfully.
2424 EXPECT_FALSE(prefs->GetDNRStaticRulesetChecksum(
2425 extension_id, static_sources[0].id(), &checksum));
2426 }
2427
2428 // Tests that redirecting requests using the declarativeNetRequest API works
2429 // with runtime host permissions.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,WithheldPermissions_Redirect)2430 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
2431 WithheldPermissions_Redirect) {
2432 // Load an extension which redirects all script requests made to
2433 // "b.com/subresources/not_a_valid_script.js", to
2434 // "b.com/subresources/script.js".
2435 TestRule rule = CreateGenericRule();
2436 rule.condition->url_filter =
2437 std::string("b.com/subresources/not_a_valid_script.js");
2438 rule.condition->resource_types = std::vector<std::string>({"script"});
2439 rule.priority = kMinValidPriority;
2440 rule.action->type = std::string("redirect");
2441 rule.action->redirect.emplace();
2442 rule.action->redirect->url =
2443 embedded_test_server()->GetURL("b.com", "/subresources/script.js").spec();
2444
2445 std::vector<std::string> host_permissions = {"*://a.com/", "*://b.com/*"};
2446 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
2447 {rule}, "extension" /* directory */, host_permissions));
2448
2449 const Extension* extension = last_loaded_extension();
2450 ASSERT_TRUE(extension);
2451
2452 auto verify_script_redirected = [this, extension](
2453 const GURL& page_url,
2454 bool expect_script_redirected,
2455 int expected_blocked_actions) {
2456 ui_test_utils::NavigateToURL(browser(), page_url);
2457
2458 // The page should have loaded correctly.
2459 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
2460 EXPECT_EQ(expect_script_redirected,
2461 WasFrameWithScriptLoaded(GetMainFrame()));
2462
2463 // The EmbeddedTestServer sees requests after the hostname has been
2464 // resolved.
2465 const GURL requested_script_url =
2466 embedded_test_server()->GetURL("/subresources/not_a_valid_script.js");
2467 const GURL redirected_script_url =
2468 embedded_test_server()->GetURL("/subresources/script.js");
2469
2470 std::set<GURL> seen_requests = GetAndResetRequestsToServer();
2471 EXPECT_EQ(!expect_script_redirected,
2472 base::Contains(seen_requests, requested_script_url));
2473 EXPECT_EQ(expect_script_redirected,
2474 base::Contains(seen_requests, redirected_script_url));
2475
2476 ExtensionActionRunner* runner =
2477 ExtensionActionRunner::GetForWebContents(web_contents());
2478 ASSERT_TRUE(runner);
2479 EXPECT_EQ(expected_blocked_actions, runner->GetBlockedActions(extension));
2480 };
2481
2482 {
2483 const GURL page_url = embedded_test_server()->GetURL(
2484 "example.com", "/cross_site_script.html");
2485 SCOPED_TRACE(
2486 base::StringPrintf("Navigating to %s", page_url.spec().c_str()));
2487
2488 // The extension should not redirect the script request. It has access to
2489 // the |requested_script_url| but not its initiator |page_url|.
2490 bool expect_script_redirected = false;
2491 verify_script_redirected(page_url, expect_script_redirected,
2492 BLOCKED_ACTION_NONE);
2493 }
2494
2495 {
2496 const GURL page_url =
2497 embedded_test_server()->GetURL("a.com", "/cross_site_script.html");
2498 SCOPED_TRACE(
2499 base::StringPrintf("Navigating to %s", page_url.spec().c_str()));
2500
2501 // The extension should redirect the script request. It has access to both
2502 // the |requested_script_url| and its initiator |page_url|.
2503 bool expect_script_redirected = true;
2504 verify_script_redirected(page_url, expect_script_redirected,
2505 BLOCKED_ACTION_NONE);
2506 }
2507
2508 // Withhold access to all hosts.
2509 ScriptingPermissionsModifier scripting_modifier(
2510 profile(), base::WrapRefCounted(extension));
2511 scripting_modifier.SetWithholdHostPermissions(true);
2512
2513 {
2514 const GURL page_url =
2515 embedded_test_server()->GetURL("a.com", "/cross_site_script.html");
2516 SCOPED_TRACE(base::StringPrintf("Navigating to %s with all hosts withheld",
2517 page_url.spec().c_str()));
2518
2519 // The extension should not redirect the script request. It's access to both
2520 // the |requested_script_url| and its initiator |page_url| is withheld.
2521 bool expect_script_redirected = false;
2522 verify_script_redirected(page_url, expect_script_redirected,
2523 BLOCKED_ACTION_WEB_REQUEST);
2524 }
2525
2526 // Grant access to only "b.com".
2527 scripting_modifier.GrantHostPermission(GURL("http://b.com"));
2528 {
2529 const GURL page_url =
2530 embedded_test_server()->GetURL("a.com", "/cross_site_script.html");
2531 SCOPED_TRACE(base::StringPrintf("Navigating to %s with a.com withheld",
2532 page_url.spec().c_str()));
2533
2534 // The extension should not redirect the script request. It has access to
2535 // the |requested_script_url|, but its access to the request initiator
2536 // |page_url| is withheld.
2537 bool expect_script_redirected = false;
2538 verify_script_redirected(page_url, expect_script_redirected,
2539 BLOCKED_ACTION_WEB_REQUEST);
2540 }
2541
2542 // Grant access to only "a.com".
2543 scripting_modifier.RemoveAllGrantedHostPermissions();
2544 scripting_modifier.GrantHostPermission(GURL("http://a.com"));
2545 {
2546 const GURL page_url =
2547 embedded_test_server()->GetURL("a.com", "/cross_site_script.html");
2548 SCOPED_TRACE(base::StringPrintf("Navigating to %s with b.com withheld",
2549 page_url.spec().c_str()));
2550
2551 // The extension should redirect the script request. It's access to the
2552 // |requested_script_url| is withheld, but it has access to its initiator
2553 // |page_url|.
2554 bool expect_script_redirected = true;
2555 verify_script_redirected(page_url, expect_script_redirected,
2556 BLOCKED_ACTION_NONE);
2557 }
2558 }
2559
2560 // Ensures that withholding permissions has no effect on blocking requests using
2561 // the declarative net request API.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,WithheldPermissions_Block)2562 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
2563 WithheldPermissions_Block) {
2564 // Load an extension with access to <all_urls> which blocks all script
2565 // requests made on example.com.
2566 TestRule rule = CreateGenericRule();
2567 rule.condition->url_filter = std::string("*");
2568 rule.condition->domains = std::vector<std::string>({"example.com"});
2569 rule.condition->resource_types = std::vector<std::string>({"script"});
2570
2571 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
2572 {rule}, "test_extension", {URLPattern::kAllUrlsPattern}));
2573
2574 const Extension* extension = last_loaded_extension();
2575 ASSERT_TRUE(extension);
2576
2577 EXPECT_TRUE(extension->permissions_data()->HasEffectiveAccessToAllHosts());
2578
2579 // Withhold access to all hosts.
2580 ScriptingPermissionsModifier scripting_modifier(
2581 profile(), base::WrapRefCounted(extension));
2582 scripting_modifier.SetWithholdHostPermissions(true);
2583
2584 EXPECT_EQ(
2585 extension->permissions_data()->active_permissions().explicit_hosts(),
2586 URLPatternSet(
2587 {URLPattern(URLPattern::SCHEME_CHROMEUI, "chrome://favicon/*")}));
2588
2589 // Request made by index.html to script.js should be blocked despite the
2590 // extension having no active host permissions to the request.
2591 ui_test_utils::NavigateToURL(
2592 browser(), embedded_test_server()->GetURL(
2593 "example.com", "/pages_with_script/index.html"));
2594 EXPECT_FALSE(WasFrameWithScriptLoaded(GetMainFrame()));
2595
2596 // Sanity check that the script.js request is not blocked if does not match a
2597 // rule.
2598 ui_test_utils::NavigateToURL(browser(),
2599 embedded_test_server()->GetURL(
2600 "foo.com", "/pages_with_script/index.html"));
2601 EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
2602 }
2603
2604 // Tests the dynamic rule support.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,DynamicRules)2605 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, DynamicRules) {
2606 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
2607
2608 // Add an extension which blocks main-frame requests to "yahoo.com".
2609 TestRule block_static_rule = CreateGenericRule();
2610 block_static_rule.condition->resource_types =
2611 std::vector<std::string>({"main_frame"});
2612 block_static_rule.condition->url_filter = std::string("||yahoo.com");
2613 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
2614 {block_static_rule}, "test_extension", {URLPattern::kAllUrlsPattern}));
2615
2616 const char* kUrlPath = "/pages_with_script/index.html";
2617 GURL yahoo_url = embedded_test_server()->GetURL("yahoo.com", kUrlPath);
2618 GURL google_url = embedded_test_server()->GetURL("google.com", kUrlPath);
2619 EXPECT_TRUE(IsNavigationBlocked(yahoo_url));
2620 EXPECT_FALSE(IsNavigationBlocked(google_url));
2621
2622 // Add dynamic rules to block "google.com" and redirect pages on "example.com"
2623 // to |dynamic_redirect_url|.
2624 TestRule block_dynamic_rule = block_static_rule;
2625 block_dynamic_rule.condition->url_filter = std::string("||google.com");
2626 block_dynamic_rule.id = kMinValidID;
2627
2628 GURL dynamic_redirect_url =
2629 embedded_test_server()->GetURL("dynamic.com", kUrlPath);
2630 TestRule redirect_rule = CreateGenericRule();
2631 redirect_rule.condition->url_filter = std::string("||example.com");
2632 redirect_rule.condition->resource_types =
2633 std::vector<std::string>({"main_frame"});
2634 redirect_rule.priority = kMinValidPriority;
2635 redirect_rule.action->type = std::string("redirect");
2636 redirect_rule.action->redirect.emplace();
2637 redirect_rule.action->redirect->url = dynamic_redirect_url.spec();
2638 redirect_rule.id = kMinValidID + 1;
2639
2640 ASSERT_NO_FATAL_FAILURE(AddDynamicRules(last_loaded_extension_id(),
2641 {block_dynamic_rule, redirect_rule}));
2642
2643 EXPECT_TRUE(IsNavigationBlocked(google_url));
2644 EXPECT_TRUE(IsNavigationBlocked(yahoo_url));
2645
2646 // Navigate to a page on "example.com". It should be redirected to
2647 // |dynamic_redirect_url|.
2648 GURL example_url = embedded_test_server()->GetURL("example.com", kUrlPath);
2649 ui_test_utils::NavigateToURL(browser(), example_url);
2650 EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
2651 EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
2652 EXPECT_EQ(dynamic_redirect_url, web_contents()->GetLastCommittedURL());
2653
2654 // Now add a dynamic rule to allow requests to yahoo.com.
2655 TestRule allow_rule = block_static_rule;
2656 allow_rule.id = kMinValidID + 2;
2657 allow_rule.action->type = std::string("allow");
2658 ASSERT_NO_FATAL_FAILURE(
2659 AddDynamicRules(last_loaded_extension_id(), {allow_rule}));
2660
2661 // Dynamic ruleset gets more priority over the static ruleset and yahoo.com is
2662 // not blocked.
2663 EXPECT_FALSE(IsNavigationBlocked(yahoo_url));
2664
2665 // Now remove the |block_rule| and |allow_rule|. Rule ids not present will be
2666 // ignored.
2667 ASSERT_NO_FATAL_FAILURE(RemoveDynamicRules(
2668 last_loaded_extension_id(),
2669 {*block_dynamic_rule.id, *allow_rule.id, kMinValidID + 100}));
2670 EXPECT_FALSE(IsNavigationBlocked(google_url));
2671 EXPECT_TRUE(IsNavigationBlocked(yahoo_url));
2672 }
2673
2674 // Tests rules using the Redirect dictionary.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,Redirect)2675 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, Redirect) {
2676 TestRule rule1 = CreateGenericRule();
2677 rule1.condition->resource_types = std::vector<std::string>({"main_frame"});
2678 rule1.id = kMinValidID;
2679 rule1.condition->url_filter = std::string("ex");
2680 rule1.action->type = std::string("redirect");
2681 rule1.priority = kMinValidPriority;
2682 rule1.action->redirect.emplace();
2683 rule1.action->redirect->url =
2684 embedded_test_server()
2685 ->GetURL("google.com", "/pages_with_script/index.html")
2686 .spec();
2687
2688 TestRule rule2 = CreateGenericRule();
2689 rule2.condition->resource_types = std::vector<std::string>({"main_frame"});
2690 rule2.id = kMinValidID + 1;
2691 rule2.condition->url_filter = std::string("example.com");
2692 rule2.action->type = std::string("redirect");
2693 rule2.priority = kMinValidPriority + 1;
2694 rule2.action->redirect.emplace();
2695 rule2.action->redirect->extension_path = "/manifest.json?query#fragment";
2696
2697 TestRule rule3 = CreateGenericRule();
2698 rule3.condition->resource_types = std::vector<std::string>({"main_frame"});
2699 rule3.id = kMinValidID + 2;
2700 rule3.condition->url_filter = std::string("||example.com");
2701 rule3.action->type = std::string("redirect");
2702 rule3.priority = kMinValidPriority + 2;
2703 rule3.action->redirect.emplace();
2704 rule3.action->redirect->transform.emplace();
2705 auto& transform = rule3.action->redirect->transform;
2706 transform->host = "new.host.com";
2707 transform->path = "/pages_with_script/page.html";
2708 transform->query = "?new_query";
2709 transform->fragment = "#new_fragment";
2710
2711 TestRule rule4 = CreateGenericRule();
2712 rule4.condition->resource_types = std::vector<std::string>({"main_frame"});
2713 rule4.id = kMinValidID + 3;
2714 rule4.condition->url_filter.reset();
2715 rule4.condition->regex_filter = R"(^(.+?)://(abc|def)\.exy\.com(.*)$)";
2716 rule4.action->type = std::string("redirect");
2717 rule4.priority = kMinValidPriority + 1;
2718 rule4.action->redirect.emplace();
2719 rule4.action->redirect->regex_substitution = R"(\1://www.\2.com\3)";
2720
2721 ASSERT_NO_FATAL_FAILURE(
2722 LoadExtensionWithRules({rule1, rule2, rule3, rule4}, "test_extension",
2723 {URLPattern::kAllUrlsPattern}));
2724
2725 struct {
2726 GURL url;
2727 GURL expected_url;
2728 } cases[] = {{embedded_test_server()->GetURL("example.com",
2729 "/pages_with_script/index.html"),
2730 // Because of higher priority, the transform rule is chosen.
2731 embedded_test_server()->GetURL(
2732 "new.host.com",
2733 "/pages_with_script/page.html?new_query#new_fragment")},
2734 // Because of higher priority, the extensionPath rule is chosen.
2735 {embedded_test_server()->GetURL(
2736 "xyz.com", "/pages_with_script/index.html?example.com"),
2737 GURL("chrome-extension://" + last_loaded_extension_id() +
2738 "/manifest.json?query#fragment")},
2739 {embedded_test_server()->GetURL("ex.com",
2740 "/pages_with_script/index.html"),
2741 embedded_test_server()->GetURL(
2742 "google.com", "/pages_with_script/index.html")},
2743 // Because of a priority higher than |rule1|, |rule4| is chosen.
2744 {embedded_test_server()->GetURL("abc.exy.com",
2745 "/pages_with_script/page.html"),
2746 embedded_test_server()->GetURL("www.abc.com",
2747 "/pages_with_script/page.html")},
2748 {embedded_test_server()->GetURL("xyz.exy.com",
2749 "/pages_with_script/page.html"),
2750 embedded_test_server()->GetURL(
2751 "google.com", "/pages_with_script/index.html")}};
2752
2753 for (const auto& test_case : cases) {
2754 SCOPED_TRACE("Testing " + test_case.url.spec());
2755 ui_test_utils::NavigateToURL(browser(), test_case.url);
2756 EXPECT_EQ(test_case.expected_url, web_contents()->GetLastCommittedURL());
2757 }
2758 }
2759
2760 // Test that the badge text for an extension will update to reflect the number
2761 // of actions taken on requests matching the extension's ruleset.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,ActionsMatchedCountAsBadgeText)2762 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
2763 ActionsMatchedCountAsBadgeText) {
2764 // Load the extension with a background script so scripts can be run from its
2765 // generated background page. Also grant the feedback permission for the
2766 // extension so it has access to the getMatchedRules API function.
2767 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript |
2768 ConfigFlag::kConfig_HasFeedbackPermission);
2769
2770 auto get_url_for_host = [this](std::string hostname) {
2771 return embedded_test_server()->GetURL(hostname,
2772 "/pages_with_script/index.html");
2773 };
2774
2775 // This page simulates a user clicking on a link, so that the next page it
2776 // navigates to has a Referrer header.
2777 auto get_url_with_referrer = [this](std::string hostname) {
2778 return embedded_test_server()->GetURL(hostname, "/simulate_click.html");
2779 };
2780
2781 const std::string kFrameName1 = "frame1";
2782 const GURL page_url = embedded_test_server()->GetURL(
2783 "norulesmatched.com", "/page_with_two_frames.html");
2784
2785 struct {
2786 std::string url_filter;
2787 int id;
2788 int priority;
2789 std::string action_type;
2790 base::Optional<std::string> redirect_url;
2791 base::Optional<std::vector<TestHeaderInfo>> request_headers;
2792 } rules_data[] = {
2793 {"abc.com", 1, 1, "block", base::nullopt, base::nullopt},
2794 {"def.com", 2, 1, "redirect", "http://zzz.com", base::nullopt},
2795 {"jkl.com", 3, 1, "modifyHeaders", base::nullopt,
2796 std::vector<TestHeaderInfo>(
2797 {TestHeaderInfo("referer", "remove", base::nullopt)})},
2798 {"abcd.com", 4, 1, "block", base::nullopt, base::nullopt},
2799 {"abcd", 5, 1, "allow", base::nullopt, base::nullopt},
2800 };
2801
2802 // Load the extension.
2803 std::vector<TestRule> rules;
2804 for (const auto& rule_data : rules_data) {
2805 TestRule rule = CreateGenericRule();
2806 rule.condition->url_filter = rule_data.url_filter;
2807 rule.id = rule_data.id;
2808 rule.priority = rule_data.priority;
2809 rule.condition->resource_types =
2810 std::vector<std::string>({"main_frame", "sub_frame"});
2811 rule.action->type = rule_data.action_type;
2812 rule.action->redirect.emplace();
2813 rule.action->redirect->url = rule_data.redirect_url;
2814 rule.action->request_headers = rule_data.request_headers;
2815 rules.push_back(rule);
2816 }
2817
2818 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
2819 rules, "test_extension", {URLPattern::kAllUrlsPattern}));
2820
2821 const ExtensionId& extension_id = last_loaded_extension_id();
2822 const Extension* dnr_extension = last_loaded_extension();
2823
2824 ExtensionPrefs::Get(profile())->SetDNRUseActionCountAsBadgeText(extension_id,
2825 true);
2826
2827 ExtensionAction* action =
2828 ExtensionActionManager::Get(web_contents()->GetBrowserContext())
2829 ->GetExtensionAction(*dnr_extension);
2830
2831 struct {
2832 std::string frame_hostname;
2833 std::string expected_badge_text;
2834 bool has_referrer_header;
2835 } test_cases[] = {
2836 // zzz.com does not match any rules, so no badge text should be displayed.
2837 {"zzz.com", "", false},
2838 // abc.com is blocked by a matching rule and should increment the badge
2839 // text.
2840 {"abc.com", "1", false},
2841 // def.com is redirected by a matching rule and should increment the badge
2842 // text.
2843 {"def.com", "2", false},
2844 // jkl.com matches with a modifyHeaders rule which removes the referer
2845 // header, but has no headers. Therefore no action is taken and the badge
2846 // text stays the same.
2847 {"jkl.com", "2", false},
2848 // jkl.com matches with a modifyHeaders rule and has a referer header.
2849 // Therefore the badge text should be incremented.
2850 {"jkl.com", "3", true},
2851 // abcd.com matches both a block rule and an allow rule. Since the allow
2852 // rule overrides the block rule, no action is taken and the badge text
2853 // stays the same,
2854 {"abcd.com", "3", false},
2855 };
2856
2857 ui_test_utils::NavigateToURL(browser(), page_url);
2858 ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
2859
2860 // Verify that the badge text is empty when navigation finishes because no
2861 // actions have been matched.
2862 int first_tab_id = ExtensionTabUtil::GetTabId(web_contents());
2863 EXPECT_EQ("", action->GetDisplayBadgeText(first_tab_id));
2864
2865 for (const auto& test_case : test_cases) {
2866 GURL url = test_case.has_referrer_header
2867 ? get_url_with_referrer(test_case.frame_hostname)
2868 : get_url_for_host(test_case.frame_hostname);
2869 SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str()));
2870
2871 NavigateFrame(kFrameName1, url, false /* use_frame_referrer */);
2872 EXPECT_EQ(test_case.expected_badge_text,
2873 action->GetDisplayBadgeText(first_tab_id));
2874 }
2875
2876 std::string first_tab_badge_text = action->GetDisplayBadgeText(first_tab_id);
2877
2878 const GURL second_tab_url = get_url_for_host("nomatch.com");
2879 ui_test_utils::NavigateToURLWithDisposition(
2880 browser(), second_tab_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
2881 ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
2882
2883 ASSERT_EQ(2, browser()->tab_strip_model()->count());
2884 ASSERT_TRUE(browser()->tab_strip_model()->IsTabSelected(1));
2885
2886 int second_tab_id = ExtensionTabUtil::GetTabId(web_contents());
2887 EXPECT_EQ("", action->GetDisplayBadgeText(second_tab_id));
2888
2889 // Verify that the badge text for the first tab is unaffected.
2890 EXPECT_EQ(first_tab_badge_text, action->GetDisplayBadgeText(first_tab_id));
2891
2892 // Verify that the correct rules are returned via getMatchedRules. Returns "|"
2893 // separated pairs of <rule_id>,<ruleset_id>, sorted by rule ids, e.g.
2894 // "ruleId1,rulesetId1|ruleId2,rulesetId2".
2895 auto get_matched_rules = [this](int tab_id) {
2896 const char kGetMatchedRulesScript[] = R"(
2897 chrome.declarativeNetRequest.getMatchedRules({tabId: %d}, (matches) => {
2898 // Sort by |ruleId|.
2899 matches.rulesMatchedInfo.sort((a, b) => a.rule.ruleId - b.rule.ruleId);
2900
2901 var ruleAndRulesetIDs = matches.rulesMatchedInfo.map(
2902 match => [match.rule.ruleId, match.rule.rulesetId].join());
2903
2904 window.domAutomationController.send(ruleAndRulesetIDs.join('|'));
2905 });
2906 )";
2907
2908 return ExecuteScriptInBackgroundPage(
2909 last_loaded_extension_id(),
2910 base::StringPrintf(kGetMatchedRulesScript, tab_id),
2911 browsertest_util::ScriptUserActivation::kDontActivate);
2912 };
2913
2914 DeclarativeNetRequestGetMatchedRulesFunction::
2915 set_disable_throttling_for_tests(true);
2916
2917 // Four rules should be matched on the tab with |first_tab_id|:
2918 // - the block rule for abc.com (ruleId = 1)
2919 // - the redirect rule for def.com (ruleId = 2)
2920 // - the modifyHeaders rule for jkl.com (ruleId = 3)
2921 // - the allow rule for abcd.com (ruleId = 5)
2922 EXPECT_EQ(base::StringPrintf("1,%s|2,%s|3,%s|5,%s", kDefaultRulesetID,
2923 kDefaultRulesetID, kDefaultRulesetID,
2924 kDefaultRulesetID),
2925 get_matched_rules(first_tab_id));
2926
2927 // No rule should be matched on the tab with |second_tab_id|.
2928 EXPECT_EQ("", get_matched_rules(second_tab_id));
2929 }
2930
2931 // Ensure web request events are still dispatched even if DNR blocks/redirects
2932 // the request. (Regression test for crbug.com/999744).
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,WebRequestEvents)2933 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, WebRequestEvents) {
2934 // Load the extension with a background script so scripts can be run from its
2935 // generated background page.
2936 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
2937
2938 TestRule rule = CreateGenericRule();
2939 rule.condition->url_filter = "||example.com";
2940 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
2941 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
2942 {rule}, "test_extension", {URLPattern::kAllUrlsPattern}));
2943
2944 GURL url = embedded_test_server()->GetURL("example.com",
2945 "/pages_with_script/index.html");
2946
2947 // Set up web request listeners listening to request to |url|.
2948 const char kWebRequestListenerScript[] = R"(
2949 let filter = {'urls' : ['%s'], 'types' : ['main_frame']};
2950
2951 let onBeforeRequestSeen = false;
2952 chrome.webRequest.onBeforeRequest.addListener(() => {
2953 onBeforeRequestSeen = true;
2954 }, filter);
2955
2956 // The request will fail since it will be blocked by DNR.
2957 chrome.webRequest.onErrorOccurred.addListener(() => {
2958 if (onBeforeRequestSeen)
2959 chrome.test.sendMessage('PASS');
2960 }, filter);
2961
2962 chrome.test.sendMessage('INSTALLED');
2963 )";
2964
2965 ExtensionTestMessageListener pass_listener("PASS", false /* will_reply */);
2966 ExtensionTestMessageListener installed_listener("INSTALLED",
2967 false /* will_reply */);
2968 ExecuteScriptInBackgroundPageNoWait(
2969 last_loaded_extension_id(),
2970 base::StringPrintf(kWebRequestListenerScript, url.spec().c_str()));
2971
2972 // Wait for the web request listeners to be installed before navigating.
2973 ASSERT_TRUE(installed_listener.WaitUntilSatisfied());
2974
2975 ui_test_utils::NavigateToURL(browser(), url);
2976
2977 ASSERT_FALSE(WasFrameWithScriptLoaded(GetMainFrame()));
2978 EXPECT_TRUE(pass_listener.WaitUntilSatisfied());
2979 }
2980
2981 // Ensure Declarative Net Request gets priority over the web request API.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,WebRequestPriority)2982 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, WebRequestPriority) {
2983 // Load the extension with a background script so scripts can be run from its
2984 // generated background page.
2985 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
2986
2987 GURL url = embedded_test_server()->GetURL("example.com",
2988 "/pages_with_script/index.html");
2989 GURL redirect_url = embedded_test_server()->GetURL(
2990 "redirect.com", "/pages_with_script/index.html");
2991
2992 TestRule example_com_redirect_rule = CreateGenericRule();
2993 example_com_redirect_rule.condition->url_filter = "||example.com";
2994 example_com_redirect_rule.condition->resource_types =
2995 std::vector<std::string>({"main_frame"});
2996 example_com_redirect_rule.action->type = std::string("redirect");
2997 example_com_redirect_rule.action->redirect.emplace();
2998 example_com_redirect_rule.action->redirect->url = redirect_url.spec();
2999 example_com_redirect_rule.priority = kMinValidPriority;
3000
3001 ASSERT_NO_FATAL_FAILURE(
3002 LoadExtensionWithRules({example_com_redirect_rule}, "test_extension",
3003 {URLPattern::kAllUrlsPattern}));
3004
3005 // Set up a web request listener to block the request to example.com.
3006 const char kWebRequestBlockScript[] = R"(
3007 let filter = {'urls' : ['%s'], 'types' : ['main_frame']};
3008 chrome.webRequest.onBeforeRequest.addListener((details) => {
3009 chrome.test.sendMessage('SEEN')
3010 }, filter, ['blocking']);
3011 chrome.test.sendMessage('INSTALLED');
3012 )";
3013
3014 ExtensionTestMessageListener seen_listener("SEEN", false /* will_reply */);
3015 ExtensionTestMessageListener installed_listener("INSTALLED",
3016 false /* will_reply */);
3017 ExecuteScriptInBackgroundPageNoWait(
3018 last_loaded_extension_id(),
3019 base::StringPrintf(kWebRequestBlockScript, url.spec().c_str()));
3020
3021 // Wait for the web request listeners to be installed before navigating.
3022 ASSERT_TRUE(installed_listener.WaitUntilSatisfied());
3023
3024 ui_test_utils::NavigateToURL(browser(), url);
3025
3026 // Ensure the response from the web request listener was ignored and the
3027 // request was redirected.
3028 ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
3029 EXPECT_EQ(web_contents()->GetLastCommittedURL(), redirect_url);
3030
3031 // Ensure onBeforeRequest is seen by the web request extension.
3032 EXPECT_TRUE(seen_listener.WaitUntilSatisfied());
3033 }
3034
3035 // Test that the extension cannot retrieve the number of actions matched
3036 // from the badge text by calling chrome.browserAction.getBadgeText.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,GetBadgeTextForActionsMatched)3037 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
3038 GetBadgeTextForActionsMatched) {
3039 auto query_badge_text_from_ext = [this](const ExtensionId& extension_id,
3040 int tab_id) {
3041 static constexpr char kBadgeTextQueryScript[] = R"(
3042 chrome.browserAction.getBadgeText({tabId: %d}, badgeText => {
3043 window.domAutomationController.send(badgeText);
3044 });
3045 )";
3046
3047 return ExecuteScriptInBackgroundPage(
3048 extension_id, base::StringPrintf(kBadgeTextQueryScript, tab_id));
3049 };
3050
3051 // Load the extension with a background script so scripts can be run from its
3052 // generated background page.
3053 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
3054
3055 TestRule rule = CreateGenericRule();
3056 rule.condition->url_filter = "abc.com";
3057 rule.id = kMinValidID;
3058 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
3059 rule.action->type = "block";
3060
3061 std::vector<TestRule> rules({rule});
3062 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
3063 {rules}, "test_extension", {URLPattern::kAllUrlsPattern}));
3064
3065 const ExtensionId& extension_id = last_loaded_extension_id();
3066 const Extension* dnr_extension = last_loaded_extension();
3067
3068 ExtensionAction* action =
3069 ExtensionActionManager::Get(web_contents()->GetBrowserContext())
3070 ->GetExtensionAction(*dnr_extension);
3071
3072 const std::string default_badge_text = "asdf";
3073 action->SetBadgeText(ExtensionAction::kDefaultTabId, default_badge_text);
3074
3075 const GURL page_url = embedded_test_server()->GetURL(
3076 "abc.com", "/pages_with_script/index.html");
3077 ui_test_utils::NavigateToURL(browser(), page_url);
3078
3079 // The preference is initially turned off. Both the visible badge text and the
3080 // badge text queried by the extension using getBadgeText() should return the
3081 // default badge text.
3082 int first_tab_id = ExtensionTabUtil::GetTabId(web_contents());
3083 EXPECT_EQ(default_badge_text, action->GetDisplayBadgeText(first_tab_id));
3084
3085 std::string queried_badge_text =
3086 query_badge_text_from_ext(extension_id, first_tab_id);
3087 EXPECT_EQ(default_badge_text, queried_badge_text);
3088
3089 SetActionsAsBadgeText(extension_id, true);
3090 // Since the preference is on for the current tab, attempting to query the
3091 // badge text from the extension should return the placeholder text instead of
3092 // the matched action count.
3093 queried_badge_text = query_badge_text_from_ext(extension_id, first_tab_id);
3094 EXPECT_EQ(declarative_net_request::kActionCountPlaceholderBadgeText,
3095 queried_badge_text);
3096
3097 // One action was matched, and this should be reflected in the badge text.
3098 EXPECT_EQ("1", action->GetDisplayBadgeText(first_tab_id));
3099
3100 SetActionsAsBadgeText(extension_id, false);
3101 // Switching the preference off should cause the extension queried badge text
3102 // to be the explicitly set badge text for this tab if it exists. In this
3103 // case, the queried badge text should be the default badge text.
3104 queried_badge_text = query_badge_text_from_ext(extension_id, first_tab_id);
3105 EXPECT_EQ(default_badge_text, queried_badge_text);
3106
3107 // The displayed badge text should be the default badge text now that the
3108 // preference is off.
3109 EXPECT_EQ(default_badge_text, action->GetDisplayBadgeText(first_tab_id));
3110
3111 // Verify that turning off the preference deletes the DNR action count within
3112 // the extension action.
3113 EXPECT_FALSE(action->HasDNRActionCount(first_tab_id));
3114 }
3115
3116 // Test that enabling the "displayActionCountAsBadgeText" preference using
3117 // setExtensionActionOptions will update all browsers sharing the same browser
3118 // context.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,ActionCountPreferenceMultipleWindows)3119 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
3120 ActionCountPreferenceMultipleWindows) {
3121 // Load the extension with a background script so scripts can be run from its
3122 // generated background page.
3123 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
3124
3125 TestRule rule = CreateGenericRule();
3126 rule.condition->url_filter = "abc.com";
3127 rule.id = kMinValidID;
3128 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
3129 rule.action->type = "block";
3130
3131 std::vector<TestRule> rules({rule});
3132 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
3133 {rules}, "test_extension", {URLPattern::kAllUrlsPattern}));
3134
3135 const ExtensionId& extension_id = last_loaded_extension_id();
3136 const Extension* dnr_extension = last_loaded_extension();
3137
3138 ExtensionAction* extension_action =
3139 ExtensionActionManager::Get(web_contents()->GetBrowserContext())
3140 ->GetExtensionAction(*dnr_extension);
3141
3142 const GURL page_url = embedded_test_server()->GetURL(
3143 "abc.com", "/pages_with_script/index.html");
3144 ui_test_utils::NavigateToURL(browser(), page_url);
3145
3146 int first_browser_tab_id = ExtensionTabUtil::GetTabId(web_contents());
3147 EXPECT_EQ("", extension_action->GetDisplayBadgeText(first_browser_tab_id));
3148
3149 // Now create a new browser with the same profile as |browser()| and navigate
3150 // to |page_url|.
3151 Browser* second_browser = CreateBrowser(profile());
3152 ui_test_utils::NavigateToURL(second_browser, page_url);
3153 content::WebContents* second_browser_contents =
3154 second_browser->tab_strip_model()->GetActiveWebContents();
3155
3156 int second_browser_tab_id =
3157 ExtensionTabUtil::GetTabId(second_browser_contents);
3158 EXPECT_EQ("", extension_action->GetDisplayBadgeText(second_browser_tab_id));
3159
3160 // Set up an observer to listen for ExtensionAction updates for the active web
3161 // contents of both browser windows.
3162 TestExtensionActionAPIObserver test_api_observer(
3163 profile(), extension_id, {web_contents(), second_browser_contents});
3164
3165 SetActionsAsBadgeText(extension_id, true);
3166
3167 // Wait until ExtensionActionAPI::NotifyChange is called, then perform a
3168 // sanity check that one action was matched, and this is reflected in the
3169 // badge text.
3170 test_api_observer.Wait();
3171
3172 EXPECT_EQ("1", extension_action->GetDisplayBadgeText(first_browser_tab_id));
3173
3174 // The badge text for the second browser window should also update to the
3175 // matched action count because the second browser shares the same browser
3176 // context as the first.
3177 EXPECT_EQ("1", extension_action->GetDisplayBadgeText(second_browser_tab_id));
3178 }
3179
3180 // Test that the action matched badge text for an extension is visible in an
3181 // incognito context if the extension is incognito enabled.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,ActionsMatchedCountAsBadgeTextIncognito)3182 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
3183 ActionsMatchedCountAsBadgeTextIncognito) {
3184 TestRule rule = CreateGenericRule();
3185 rule.condition->url_filter = "abc.com";
3186 rule.id = kMinValidID;
3187 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
3188 rule.action->type = "block";
3189
3190 std::vector<TestRule> rules({rule});
3191 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
3192 {rules}, "test_extension", {URLPattern::kAllUrlsPattern}));
3193
3194 const ExtensionId extension_id = last_loaded_extension_id();
3195 util::SetIsIncognitoEnabled(extension_id, profile(), true /*enabled*/);
3196
3197 ExtensionPrefs::Get(profile())->SetDNRUseActionCountAsBadgeText(extension_id,
3198 true);
3199
3200 Browser* incognito_browser = CreateIncognitoBrowser();
3201 ui_test_utils::NavigateToURL(incognito_browser, GURL("http://abc.com"));
3202
3203 content::WebContents* incognito_contents =
3204 incognito_browser->tab_strip_model()->GetActiveWebContents();
3205
3206 const Extension* dnr_extension = last_loaded_extension();
3207 ExtensionAction* incognito_action =
3208 ExtensionActionManager::Get(incognito_contents->GetBrowserContext())
3209 ->GetExtensionAction(*dnr_extension);
3210
3211 EXPECT_EQ("1", incognito_action->GetDisplayBadgeText(
3212 ExtensionTabUtil::GetTabId(incognito_contents)));
3213 }
3214
3215 // Test that the actions matched badge text for an extension will be reset
3216 // when a main-frame navigation finishes.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,ActionsMatchedCountAsBadgeTextMainFrame)3217 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
3218 ActionsMatchedCountAsBadgeTextMainFrame) {
3219 auto get_url_for_host = [this](std::string hostname) {
3220 return embedded_test_server()->GetURL(hostname,
3221 "/pages_with_script/index.html");
3222 };
3223
3224 auto get_set_cookie_url = [this](std::string hostname) {
3225 return embedded_test_server()->GetURL(hostname, "/set-cookie?a=b");
3226 };
3227
3228 struct {
3229 std::string url_filter;
3230 int id;
3231 int priority;
3232 std::string action_type;
3233 std::vector<std::string> resource_types;
3234 base::Optional<std::string> redirect_url;
3235 base::Optional<std::vector<TestHeaderInfo>> response_headers;
3236 } rules_data[] = {
3237 {"abc.com", 1, 1, "block", std::vector<std::string>({"script"}),
3238 base::nullopt, base::nullopt},
3239 {"||def.com", 2, 1, "redirect", std::vector<std::string>({"main_frame"}),
3240 get_url_for_host("abc.com").spec(), base::nullopt},
3241 {"gotodef.com", 3, 1, "redirect",
3242 std::vector<std::string>({"main_frame"}),
3243 get_url_for_host("def.com").spec(), base::nullopt},
3244 {"ghi.com", 4, 1, "block", std::vector<std::string>({"main_frame"}),
3245 base::nullopt, base::nullopt},
3246 {"gotosetcookie.com", 5, 1, "redirect",
3247 std::vector<std::string>({"main_frame"}),
3248 get_set_cookie_url("setcookie.com").spec(), base::nullopt},
3249 {"setcookie.com", 6, 1, "modifyHeaders",
3250 std::vector<std::string>({"main_frame"}), base::nullopt,
3251 std::vector<TestHeaderInfo>(
3252 {TestHeaderInfo("set-cookie", "remove", base::nullopt)})},
3253 };
3254
3255 // Load the extension.
3256 std::vector<TestRule> rules;
3257 for (const auto& rule_data : rules_data) {
3258 TestRule rule = CreateGenericRule();
3259 rule.condition->url_filter = rule_data.url_filter;
3260 rule.id = rule_data.id;
3261 rule.priority = rule_data.priority;
3262 rule.condition->resource_types = rule_data.resource_types;
3263 rule.action->type = rule_data.action_type;
3264 rule.action->redirect.emplace();
3265 rule.action->redirect->url = rule_data.redirect_url;
3266 rule.action->response_headers = rule_data.response_headers;
3267 rules.push_back(rule);
3268 }
3269
3270 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
3271 rules, "test_extension", {URLPattern::kAllUrlsPattern}));
3272
3273 const Extension* dnr_extension = last_loaded_extension();
3274
3275 ExtensionPrefs::Get(profile())->SetDNRUseActionCountAsBadgeText(
3276 last_loaded_extension_id(), true);
3277
3278 ExtensionAction* action =
3279 ExtensionActionManager::Get(web_contents()->GetBrowserContext())
3280 ->GetExtensionAction(*dnr_extension);
3281
3282 struct {
3283 std::string frame_hostname;
3284 std::string expected_badge_text;
3285 } test_cases[] = {
3286 // The request to ghi.com should be blocked, so the badge text should be 1
3287 // once navigation finishes.
3288 {"ghi.com", "1"},
3289 // The script on get_url_for_host("abc.com") matches with a rule and
3290 // should increment the badge text.
3291 {"abc.com", "1"},
3292 // No rules match, so there should be no badge text once navigation
3293 // finishes.
3294 {"nomatch.com", ""},
3295 // The request to def.com will redirect to get_url_for_host("abc.com") and
3296 // the script on abc.com should match with a rule.
3297 {"def.com", "2"},
3298 // Same as the above test, except with an additional redirect from
3299 // gotodef.com to def.com caused by a rule match. Therefore the badge text
3300 // should be 3.
3301 {"gotodef.com", "3"},
3302 // The request to gotosetcookie.com will match with a rule and redirect to
3303 // setcookie.com. The Set-Cookie header on setcookie.com will also match
3304 // with a rule and get removed. Therefore the badge text should be 2.
3305 {"gotosetcookie.com", "2"},
3306 };
3307
3308 int first_tab_id = ExtensionTabUtil::GetTabId(web_contents());
3309 for (const auto& test_case : test_cases) {
3310 GURL url = get_url_for_host(test_case.frame_hostname);
3311 SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str()));
3312
3313 ui_test_utils::NavigateToURL(browser(), url);
3314 EXPECT_EQ(test_case.expected_badge_text,
3315 action->GetDisplayBadgeText(first_tab_id));
3316 }
3317 }
3318
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,ModifyHeadersBadgeText)3319 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
3320 ModifyHeadersBadgeText) {
3321 auto get_referer_url = [this](const std::string& host) {
3322 return embedded_test_server()->GetURL(host, "/set-header?referer: none");
3323 };
3324 auto get_set_cookie_url = [this](const std::string& host) {
3325 return embedded_test_server()->GetURL(host, "/set-cookie?a=b");
3326 };
3327 auto get_no_headers_url = [this](const std::string& host) {
3328 return embedded_test_server()->GetURL(host,
3329 "/pages_with_script/index.html");
3330 };
3331
3332 const std::string kFrameName1 = "frame1";
3333 const GURL page_url = embedded_test_server()->GetURL(
3334 "nomatch.com", "/page_with_two_frames.html");
3335
3336 // Create an extension with rules and get the ExtensionAction for it.
3337 TestRule example_set_cookie_rule = CreateModifyHeadersRule(
3338 kMinValidID, kMinValidPriority, "example.com", base::nullopt,
3339 std::vector<TestHeaderInfo>(
3340 {TestHeaderInfo("set-cookie", "remove", base::nullopt)}));
3341
3342 TestRule both_headers_rule = CreateModifyHeadersRule(
3343 kMinValidID + 1, kMinValidPriority, "google.com",
3344 std::vector<TestHeaderInfo>(
3345 {TestHeaderInfo("referer", "remove", base::nullopt)}),
3346 std::vector<TestHeaderInfo>(
3347 {TestHeaderInfo("set-cookie", "remove", base::nullopt)}));
3348
3349 TestRule abc_set_cookie_rule = CreateModifyHeadersRule(
3350 kMinValidID + 2, kMinValidPriority, "abc.com", base::nullopt,
3351 std::vector<TestHeaderInfo>(
3352 {TestHeaderInfo("set-cookie", "remove", base::nullopt)}));
3353
3354 TestRule abc_referer_rule = CreateModifyHeadersRule(
3355 kMinValidID + 3, kMinValidPriority, "abc.com",
3356 std::vector<TestHeaderInfo>(
3357 {TestHeaderInfo("referer", "remove", base::nullopt)}),
3358 base::nullopt);
3359
3360 TestRule ext1_set_custom_request_header_rule = CreateModifyHeadersRule(
3361 kMinValidID + 4, kMinValidPriority, "def.com",
3362 std::vector<TestHeaderInfo>({TestHeaderInfo("header1", "set", "ext_1")}),
3363 base::nullopt);
3364
3365 TestRule ext1_add_custom_response_header_rule = CreateModifyHeadersRule(
3366 kMinValidID + 5, kMinValidPriority, "ghi.com", base::nullopt,
3367 std::vector<TestHeaderInfo>(
3368 {TestHeaderInfo("header2", "append", "ext_1")}));
3369
3370 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
3371 {example_set_cookie_rule, both_headers_rule, abc_set_cookie_rule,
3372 abc_referer_rule, ext1_set_custom_request_header_rule,
3373 ext1_add_custom_response_header_rule},
3374 "extension_1", {URLPattern::kAllUrlsPattern}));
3375
3376 const ExtensionId extension_1_id = last_loaded_extension_id();
3377 ExtensionPrefs::Get(profile())->SetDNRUseActionCountAsBadgeText(
3378 extension_1_id, true);
3379
3380 ExtensionAction* extension_1_action =
3381 ExtensionActionManager::Get(web_contents()->GetBrowserContext())
3382 ->GetExtensionAction(*extension_registry()->GetExtensionById(
3383 extension_1_id, extensions::ExtensionRegistry::ENABLED));
3384
3385 // Create another extension which removes the referer header from example.com
3386 // and get the ExtensionAction for it.
3387 TestRule example_referer_rule = CreateModifyHeadersRule(
3388 kMinValidID, kMinValidPriority, "example.com",
3389 std::vector<TestHeaderInfo>(
3390 {TestHeaderInfo("referer", "remove", base::nullopt)}),
3391 base::nullopt);
3392
3393 TestRule ext2_set_custom_request_header_rule = CreateModifyHeadersRule(
3394 kMinValidID + 4, kMinValidPriority, "def.com",
3395 std::vector<TestHeaderInfo>({TestHeaderInfo("header1", "set", "ext_2")}),
3396 base::nullopt);
3397
3398 TestRule ext2_add_custom_response_header_rule = CreateModifyHeadersRule(
3399 kMinValidID + 5, kMinValidPriority, "ghi.com", base::nullopt,
3400 std::vector<TestHeaderInfo>(
3401 {TestHeaderInfo("header2", "append", "ext_2")}));
3402
3403 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
3404 {example_referer_rule, ext2_set_custom_request_header_rule,
3405 ext2_add_custom_response_header_rule},
3406 "extension_2", {URLPattern::kAllUrlsPattern}));
3407
3408 const ExtensionId extension_2_id = last_loaded_extension_id();
3409 ExtensionPrefs::Get(profile())->SetDNRUseActionCountAsBadgeText(
3410 extension_2_id, true);
3411
3412 ExtensionAction* extension_2_action =
3413 ExtensionActionManager::Get(web_contents()->GetBrowserContext())
3414 ->GetExtensionAction(*extension_registry()->GetExtensionById(
3415 extension_2_id, extensions::ExtensionRegistry::ENABLED));
3416
3417 struct {
3418 GURL url;
3419 bool use_referrer;
3420 std::string expected_ext_1_badge_text;
3421 std::string expected_ext_2_badge_text;
3422 } test_cases[] = {
3423 // This request only has a Set-Cookie header. Only the badge text for the
3424 // extension with a remove Set-Cookie header rule should be incremented.
3425 {get_set_cookie_url("example.com"), false, "1", ""},
3426 // This request only has a Referer header. Only the badge text for the
3427 // extension with a remove Referer header rule should be incremented.
3428 {get_referer_url("example.com"), true, "1", "1"},
3429 // This request has both a Referer and a Set-Cookie header. The badge text
3430 // for both extensions should be incremented.
3431 {get_set_cookie_url("example.com"), true, "2", "2"},
3432 // This request with a Referer and Set-Cookie header matches with one rule
3433 // from |extension_1| and so the action count for |extension_1| should
3434 // only increment by one,
3435 {get_set_cookie_url("google.com"), true, "3", "2"},
3436 // This request with a Referer and Set-Cookie header matches with two
3437 // separate rules from |extension_1| and so the action count for
3438 // |extension_1| should increment by two.
3439 {get_set_cookie_url("abc.com"), true, "5", "2"},
3440 // This request without headers matches rules to set the header1 request
3441 // header from both extensions. Since |extension_2| was installed later
3442 // than |extension_1|, only the rule from |extension_2| should take effect
3443 // and so the action count for |extension_2| should increment by one.
3444 {get_no_headers_url("def.com"), false, "5", "3"},
3445 // This request without headers matches rules to append the header2
3446 // response header from both extensions. Since each extension has a rule
3447 // which has taken effect, the action count for both extensions should
3448 // increment by one.
3449 {get_no_headers_url("ghi.com"), false, "6", "4"},
3450 };
3451
3452 ui_test_utils::NavigateToURL(browser(), page_url);
3453 ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
3454
3455 int first_tab_id = ExtensionTabUtil::GetTabId(web_contents());
3456 EXPECT_EQ("", extension_1_action->GetDisplayBadgeText(first_tab_id));
3457 EXPECT_EQ("", extension_2_action->GetDisplayBadgeText(first_tab_id));
3458
3459 for (const auto& test_case : test_cases) {
3460 SCOPED_TRACE(base::StringPrintf("Testing URL: %s, using referrer: %s",
3461 test_case.url.spec().c_str(),
3462 test_case.use_referrer ? "true" : "false"));
3463
3464 NavigateFrame(kFrameName1, test_case.url, test_case.use_referrer);
3465 EXPECT_EQ(test_case.expected_ext_1_badge_text,
3466 extension_1_action->GetDisplayBadgeText(first_tab_id));
3467
3468 EXPECT_EQ(test_case.expected_ext_2_badge_text,
3469 extension_2_action->GetDisplayBadgeText(first_tab_id));
3470 }
3471 }
3472
3473 // Test that the onRuleMatchedDebug event is only available for unpacked
3474 // extensions.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,OnRuleMatchedDebugAvailability)3475 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
3476 OnRuleMatchedDebugAvailability) {
3477 // Load the extension with a background script so scripts can be run from its
3478 // generated background page. Also grant the feedback permission for the
3479 // extension so it can have access to the onRuleMatchedDebug event when
3480 // unpacked.
3481 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript |
3482 ConfigFlag::kConfig_HasFeedbackPermission);
3483
3484 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
3485 {}, "test_extension", {URLPattern::kAllUrlsPattern}));
3486
3487 const char kGetOnRuleMatchedDebugScript[] = R"(
3488 const hasEvent = !!chrome.declarativeNetRequest.onRuleMatchedDebug ?
3489 'true' : 'false';
3490 window.domAutomationController.send(hasEvent);
3491 )";
3492 std::string actual_event_availability = ExecuteScriptInBackgroundPage(
3493 last_loaded_extension_id(), kGetOnRuleMatchedDebugScript);
3494
3495 std::string expected_event_availability =
3496 GetParam() == ExtensionLoadType::UNPACKED ? "true" : "false";
3497
3498 ASSERT_EQ(expected_event_availability, actual_event_availability);
3499 }
3500
3501 // Test that the onRuleMatchedDebug event returns the correct number of matched
3502 // rules for a request which is matched with multiple rules.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Unpacked,OnRuleMatchedDebugMultipleRules)3503 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Unpacked,
3504 OnRuleMatchedDebugMultipleRules) {
3505 // This is only tested for unpacked extensions since the onRuleMatchedDebug
3506 // event is only available for unpacked extensions.
3507 ASSERT_EQ(ExtensionLoadType::UNPACKED, GetParam());
3508
3509 // Load the extension with a background script so scripts can be run from its
3510 // generated background page. Also grant the feedback permission for the
3511 // extension so it has access to the onRuleMatchedDebug event.
3512 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript |
3513 ConfigFlag::kConfig_HasFeedbackPermission);
3514
3515 const std::string kFrameName1 = "frame1";
3516 const std::string sub_frame_host = "abc.com";
3517 const GURL page_url = embedded_test_server()->GetURL(
3518 "nomatch.com", "/page_with_two_frames.html");
3519
3520 TestRule abc_referer_rule = CreateModifyHeadersRule(
3521 kMinValidID, kMinValidPriority, sub_frame_host,
3522 std::vector<TestHeaderInfo>(
3523 {TestHeaderInfo("referer", "remove", base::nullopt)}),
3524 base::nullopt);
3525
3526 TestRule abc_set_cookie_rule = CreateModifyHeadersRule(
3527 kMinValidID + 1, kMinValidPriority, sub_frame_host, base::nullopt,
3528 std::vector<TestHeaderInfo>(
3529 {TestHeaderInfo("set-cookie", "remove", base::nullopt)}));
3530
3531 // Load an extension with removeHeaders rules for the Referer and Set-Cookie
3532 // headers.
3533 ASSERT_NO_FATAL_FAILURE(
3534 LoadExtensionWithRules({abc_set_cookie_rule, abc_referer_rule},
3535 "extension_1", {URLPattern::kAllUrlsPattern}));
3536
3537 ui_test_utils::NavigateToURL(browser(), page_url);
3538 ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
3539
3540 // Start the onRuleMatchedDebug observer.
3541 const char kOnRuleMatchedDebugScript[] = R"(
3542 var matchedRules = [];
3543 var onRuleMatchedDebugCallback = (rule) => {
3544 matchedRules.push(rule);
3545 };
3546
3547 chrome.declarativeNetRequest.onRuleMatchedDebug.addListener(
3548 onRuleMatchedDebugCallback);
3549 window.domAutomationController.send('ready');
3550 )";
3551
3552 ASSERT_EQ("ready", ExecuteScriptInBackgroundPage(last_loaded_extension_id(),
3553 kOnRuleMatchedDebugScript));
3554
3555 auto set_cookie_and_referer_url =
3556 embedded_test_server()->GetURL(sub_frame_host, "/set-cookie?a=b");
3557
3558 NavigateFrame(kFrameName1, set_cookie_and_referer_url);
3559
3560 // Now query the onRuleMatchedDebug results.
3561 const char kQueryMatchedRulesScript[] = R"(
3562 chrome.declarativeNetRequest.onRuleMatchedDebug.removeListener(
3563 onRuleMatchedDebugCallback);
3564 var ruleIds = matchedRules.map(matchedRule => matchedRule.rule.ruleId);
3565 window.domAutomationController.send(ruleIds.sort().join());
3566 )";
3567
3568 std::string matched_rule_ids = ExecuteScriptInBackgroundPage(
3569 last_loaded_extension_id(), kQueryMatchedRulesScript);
3570
3571 // The request to |set_cookie_and_referer_url| should be matched with the
3572 // Referer rule (ruleId 1) and the Set-Cookie rule (ruleId 2).
3573 EXPECT_EQ("1,2", matched_rule_ids);
3574 }
3575
3576 // Test that getMatchedRules returns the correct rules when called by different
3577 // extensions with rules matched by requests initiated from different tabs.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,GetMatchedRulesMultipleTabs)3578 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
3579 GetMatchedRulesMultipleTabs) {
3580 // Load the extension with a background script so scripts can be run from its
3581 // generated background page. Also grant the feedback permission for the
3582 // extension so it has access to the getMatchedRules API function.
3583 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript |
3584 ConfigFlag::kConfig_HasFeedbackPermission);
3585
3586 DeclarativeNetRequestGetMatchedRulesFunction::
3587 set_disable_throttling_for_tests(true);
3588
3589 // Sub-frames are used for navigations instead of the main-frame to allow
3590 // multiple requests to be made without triggering a main-frame navigation,
3591 // which would move rules attributed to the previous main-frame to the unknown
3592 // tab ID.
3593 const std::string kFrameName1 = "frame1";
3594 const GURL page_url = embedded_test_server()->GetURL(
3595 "nomatch.com", "/page_with_two_frames.html");
3596
3597 auto get_url_for_host = [this](std::string hostname) {
3598 return embedded_test_server()->GetURL(hostname,
3599 "/pages_with_script/index.html");
3600 };
3601
3602 auto create_block_rule = [](int id, const std::string& url_filter) {
3603 TestRule rule = CreateGenericRule();
3604 rule.id = id;
3605 rule.condition->url_filter = url_filter;
3606 rule.condition->resource_types = std::vector<std::string>({"sub_frame"});
3607 rule.action->type = "block";
3608 return rule;
3609 };
3610
3611 TestRule abc_rule = create_block_rule(kMinValidID, "abc.com");
3612 TestRule def_rule = create_block_rule(kMinValidID + 1, "def.com");
3613
3614 ASSERT_NO_FATAL_FAILURE(
3615 LoadExtensionWithRules({abc_rule}, "extension_1", {}));
3616 auto extension_1_id = last_loaded_extension_id();
3617
3618 ASSERT_NO_FATAL_FAILURE(
3619 LoadExtensionWithRules({def_rule}, "extension_2", {}));
3620 auto extension_2_id = last_loaded_extension_id();
3621
3622 ui_test_utils::NavigateToURL(browser(), page_url);
3623 ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
3624 const int first_tab_id = ExtensionTabUtil::GetTabId(web_contents());
3625
3626 // Navigate to abc.com. The rule from |extension_1| should match for
3627 // |first_tab_id|.
3628 NavigateFrame(kFrameName1, get_url_for_host("abc.com"));
3629
3630 // Navigate to abc.com. The rule from |extension_2| should match for
3631 // |first_tab_id|.
3632 NavigateFrame(kFrameName1, get_url_for_host("def.com"));
3633
3634 ui_test_utils::NavigateToURLWithDisposition(
3635 browser(), page_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
3636 ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
3637
3638 ASSERT_EQ(2, browser()->tab_strip_model()->count());
3639 ASSERT_TRUE(browser()->tab_strip_model()->IsTabSelected(1));
3640
3641 ui_test_utils::NavigateToURL(browser(), page_url);
3642 ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
3643 const int second_tab_id = ExtensionTabUtil::GetTabId(web_contents());
3644
3645 // Navigate to abc.com from the second tab. The rule from |extension_1| should
3646 // match for |second_tab_id|.
3647 NavigateFrame(kFrameName1, get_url_for_host("abc.com"));
3648
3649 int abc_id = *abc_rule.id;
3650 int def_id = *def_rule.id;
3651
3652 struct {
3653 ExtensionId extension_id;
3654 base::Optional<int> tab_id;
3655 std::string expected_rule_and_tab_ids;
3656 } test_cases[] = {
3657 // No rules should be matched for |extension_1| and the unknown tab ID.
3658 {extension_1_id, extension_misc::kUnknownTabId, ""},
3659
3660 // No filter is specified for |extension_1|, therefore two MatchedRuleInfo
3661 // should be returned:
3662 // (abc_id, first_tab_id) and (abc_id, second_tab_id)
3663 {extension_1_id, base::nullopt,
3664 base::StringPrintf("%d,%d|%d,%d", abc_id, first_tab_id, abc_id,
3665 second_tab_id)},
3666
3667 // Filtering by tab_id = |first_tab_id| should return one MatchedRuleInfo:
3668 // (abc_id, first_tab_id)
3669 {extension_1_id, first_tab_id,
3670 base::StringPrintf("%d,%d", abc_id, first_tab_id)},
3671
3672 // Filtering by tab_id = |second_tab_id| should return one
3673 // MatchedRuleInfo: (abc_id, second_tab_id)
3674 {extension_1_id, second_tab_id,
3675 base::StringPrintf("%d,%d", abc_id, second_tab_id)},
3676
3677 // For |extension_2|, filtering by tab_id = |first_tab_id| should return
3678 // one MatchedRuleInfo: (def_id, first_tab_id)
3679 {extension_2_id, first_tab_id,
3680 base::StringPrintf("%d,%d", def_id, first_tab_id)},
3681
3682 // Since no rules from |extension_2| was matched for the second tab,
3683 // getMatchedRules should not return any rules.
3684 {extension_2_id, second_tab_id, ""}};
3685
3686 for (const auto& test_case : test_cases) {
3687 if (test_case.tab_id) {
3688 SCOPED_TRACE(base::StringPrintf(
3689 "Testing getMatchedRules for tab %d and extension %s",
3690 *test_case.tab_id, test_case.extension_id.c_str()));
3691 } else {
3692 SCOPED_TRACE(base::StringPrintf(
3693 "Testing getMatchedRules for all tabs and extension %s",
3694 test_case.extension_id.c_str()));
3695 }
3696
3697 std::string actual_rule_and_tab_ids =
3698 GetRuleAndTabIdsMatched(test_case.extension_id, test_case.tab_id);
3699 EXPECT_EQ(test_case.expected_rule_and_tab_ids, actual_rule_and_tab_ids);
3700 }
3701
3702 // Close the second tab opened.
3703 browser()->tab_strip_model()->CloseSelectedTabs();
3704
3705 ASSERT_EQ(1, browser()->tab_strip_model()->count());
3706 ASSERT_TRUE(browser()->tab_strip_model()->IsTabSelected(0));
3707
3708 std::string actual_rule_and_tab_ids =
3709 GetRuleAndTabIdsMatched(extension_1_id, base::nullopt);
3710
3711 // The matched rule info for the second tab should have its tab ID changed to
3712 // the unknown tab ID after the second tab has been closed.
3713 EXPECT_EQ(
3714 base::StringPrintf("%d,%d|%d,%d", abc_id, extension_misc::kUnknownTabId,
3715 abc_id, first_tab_id),
3716 actual_rule_and_tab_ids);
3717 }
3718
3719 // Test that rules matched for main-frame navigations are attributed will be
3720 // reset when a main-frame navigation finishes.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,GetMatchedRulesMainFrame)3721 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
3722 GetMatchedRulesMainFrame) {
3723 // Load the extension with a background script so scripts can be run from its
3724 // generated background page. Also grant the feedback permission for the
3725 // extension so it has access to the getMatchedRules API function.
3726 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript |
3727 ConfigFlag::kConfig_HasFeedbackPermission);
3728
3729 DeclarativeNetRequestGetMatchedRulesFunction::
3730 set_disable_throttling_for_tests(true);
3731
3732 const std::string test_host = "abc.com";
3733 GURL page_url = embedded_test_server()->GetURL(
3734 test_host, "/pages_with_script/index.html");
3735
3736 TestRule rule = CreateGenericRule();
3737 rule.id = kMinValidID;
3738 rule.condition->url_filter = test_host;
3739 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
3740 rule.action->type = "block";
3741
3742 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}, "extension_1", {}));
3743
3744 // Navigate to abc.com.
3745 ui_test_utils::NavigateToURL(browser(), page_url);
3746 std::string actual_rule_and_tab_ids =
3747 GetRuleAndTabIdsMatched(last_loaded_extension_id(), base::nullopt);
3748
3749 // Since the block rule for abc.com is matched for the main-frame navigation
3750 // request, it should be attributed to the current tab.
3751 const int first_tab_id = ExtensionTabUtil::GetTabId(web_contents());
3752 std::string expected_rule_and_tab_ids =
3753 base::StringPrintf("%d,%d", *rule.id, first_tab_id);
3754
3755 EXPECT_EQ(expected_rule_and_tab_ids, actual_rule_and_tab_ids);
3756
3757 // Navigate to abc.com again.
3758 ui_test_utils::NavigateToURL(browser(), page_url);
3759 actual_rule_and_tab_ids =
3760 GetRuleAndTabIdsMatched(last_loaded_extension_id(), base::nullopt);
3761
3762 // The same block rule is matched for this navigation request, and should be
3763 // attributed to the current tab. Since the main-frame for which the older
3764 // matched rule is associated with is no longer active, the older matched
3765 // rule's tab ID should be changed to the unknown tab ID.
3766 expected_rule_and_tab_ids =
3767 base::StringPrintf("%d,%d|%d,%d", *rule.id, extension_misc::kUnknownTabId,
3768 *rule.id, first_tab_id);
3769
3770 EXPECT_EQ(expected_rule_and_tab_ids, actual_rule_and_tab_ids);
3771
3772 // Navigate to nomatch,com.
3773 ui_test_utils::NavigateToURL(
3774 browser(), embedded_test_server()->GetURL(
3775 "nomatch.com", "/pages_with_script/index.html"));
3776
3777 // No rules should be matched for the navigation request to nomatch.com and
3778 // all rules previously attributed to |first_tab_id| should now be attributed
3779 // to the unknown tab ID as a result of the navigation. Therefore
3780 // GetMatchedRules should return no matched rules.
3781 actual_rule_and_tab_ids =
3782 GetRuleAndTabIdsMatched(last_loaded_extension_id(), first_tab_id);
3783 EXPECT_EQ("", actual_rule_and_tab_ids);
3784 }
3785
3786 // Test that getMatchedRules only returns rules more recent than the provided
3787 // timestamp.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,GetMatchedRulesTimestampFiltering)3788 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
3789 GetMatchedRulesTimestampFiltering) {
3790 base::Time start_time = base::Time::Now();
3791
3792 base::SimpleTestClock clock_;
3793 clock_.SetNow(start_time);
3794 rules_monitor_service()->action_tracker().SetClockForTests(&clock_);
3795
3796 // Load the extension with a background script so scripts can be run from its
3797 // generated background page. Also grant the feedback permission for the
3798 // extension so it has access to the getMatchedRules API function.
3799 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript |
3800 ConfigFlag::kConfig_HasFeedbackPermission);
3801
3802 DeclarativeNetRequestGetMatchedRulesFunction::
3803 set_disable_throttling_for_tests(true);
3804
3805 const std::string kFrameName1 = "frame1";
3806 const GURL page_url = embedded_test_server()->GetURL(
3807 "nomatch.com", "/page_with_two_frames.html");
3808
3809 const std::string example_host = "example.com";
3810 const GURL sub_frame_url = embedded_test_server()->GetURL(
3811 example_host, "/pages_with_script/index.html");
3812
3813 TestRule rule = CreateGenericRule();
3814 rule.id = kMinValidID;
3815 rule.condition->url_filter = example_host;
3816 rule.condition->resource_types = std::vector<std::string>({"sub_frame"});
3817 rule.action->type = "block";
3818
3819 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}, "extension_1", {}));
3820 const ExtensionId& extension_id = last_loaded_extension_id();
3821 ui_test_utils::NavigateToURL(browser(), page_url);
3822
3823 // Using subframes here to make requests without triggering main-frame
3824 // navigations. This request will match with the block rule for example.com at
3825 // |start_time|.
3826 NavigateFrame(kFrameName1, sub_frame_url);
3827
3828 const char getMatchedRuleTimestampScript[] = R"(
3829 chrome.declarativeNetRequest.getMatchedRules((rules) => {
3830 var rule_count = rules.rulesMatchedInfo.length;
3831 var timestamp = rule_count === 1 ?
3832 rules.rulesMatchedInfo[0].timeStamp.toString() : '';
3833
3834 window.domAutomationController.send(timestamp);
3835 });
3836 )";
3837
3838 std::string timestamp_string = ExecuteScriptInBackgroundPage(
3839 extension_id, getMatchedRuleTimestampScript,
3840 browsertest_util::ScriptUserActivation::kDontActivate);
3841
3842 double matched_rule_timestamp;
3843 ASSERT_TRUE(base::StringToDouble(timestamp_string, &matched_rule_timestamp));
3844
3845 // Verify that the rule was matched at |start_time|.
3846 EXPECT_DOUBLE_EQ(start_time.ToJsTimeIgnoringNull(), matched_rule_timestamp);
3847
3848 // Advance the clock to capture a timestamp after when the first request was
3849 // made.
3850 clock_.Advance(base::TimeDelta::FromMilliseconds(100));
3851 base::Time timestamp_1 = clock_.Now();
3852 clock_.Advance(base::TimeDelta::FromMilliseconds(100));
3853
3854 // Navigate to example.com again. This should cause |rule| to be matched.
3855 NavigateFrame(kFrameName1, sub_frame_url);
3856
3857 // Advance the clock to capture a timestamp after when the second request was
3858 // made.
3859 clock_.Advance(base::TimeDelta::FromMilliseconds(100));
3860 base::Time timestamp_2 = clock_.Now();
3861
3862 int first_tab_id = ExtensionTabUtil::GetTabId(web_contents());
3863
3864 // Two rules should be matched on |first_tab_id|.
3865 std::string rule_count = GetMatchedRuleCount(extension_id, first_tab_id,
3866 base::nullopt /* timestamp */);
3867 EXPECT_EQ("2", rule_count);
3868
3869 // Only one rule should be matched on |first_tab_id| after |timestamp_1|.
3870 rule_count = GetMatchedRuleCount(extension_id, first_tab_id, timestamp_1);
3871 EXPECT_EQ("1", rule_count);
3872
3873 // No rules should be matched on |first_tab_id| after |timestamp_2|.
3874 rule_count = GetMatchedRuleCount(extension_id, first_tab_id, timestamp_2);
3875 EXPECT_EQ("0", rule_count);
3876
3877 rules_monitor_service()->action_tracker().SetClockForTests(nullptr);
3878 }
3879
3880 // Test that getMatchedRules will only return matched rules for individual tabs
3881 // where activeTab is granted.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,GetMatchedRulesActiveTab)3882 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
3883 GetMatchedRulesActiveTab) {
3884 // Load the extension with a background script so scripts can be run from its
3885 // generated background page.
3886 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript |
3887 ConfigFlag::kConfig_HasActiveTab);
3888
3889 DeclarativeNetRequestGetMatchedRulesFunction::
3890 set_disable_throttling_for_tests(true);
3891
3892 const std::string test_host = "abc.com";
3893 GURL page_url = embedded_test_server()->GetURL(
3894 test_host, "/pages_with_script/index.html");
3895
3896 TestRule rule = CreateGenericRule();
3897 rule.id = kMinValidID;
3898 rule.condition->url_filter = test_host;
3899 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
3900 rule.action->type = "block";
3901
3902 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}, "extension_1", {}));
3903 const ExtensionId& extension_id = last_loaded_extension_id();
3904
3905 // Navigate to |page_url| which will cause |rule| to be matched.
3906 ui_test_utils::NavigateToURL(browser(), page_url);
3907
3908 int first_tab_id = ExtensionTabUtil::GetTabId(web_contents());
3909
3910 // Open a new tab and navigate to |page_url| which will cause |rule| to be
3911 // matched.
3912 ui_test_utils::NavigateToURLWithDisposition(
3913 browser(), page_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
3914 ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
3915
3916 ASSERT_EQ(2, browser()->tab_strip_model()->count());
3917 ASSERT_TRUE(browser()->tab_strip_model()->IsTabSelected(1));
3918
3919 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents());
3920 ASSERT_TRUE(tab_helper);
3921
3922 // Get the ActiveTabPermissionGranter for the second tab.
3923 ActiveTabPermissionGranter* active_tab_granter =
3924 tab_helper->active_tab_permission_granter();
3925 ASSERT_TRUE(active_tab_granter);
3926
3927 const Extension* dnr_extension = last_loaded_extension();
3928
3929 // Grant the activeTab permission for the second tab.
3930 active_tab_granter->GrantIfRequested(dnr_extension);
3931
3932 int second_tab_id = ExtensionTabUtil::GetTabId(web_contents());
3933
3934 // Calling getMatchedRules with no tab ID specified should result in an error
3935 // since the extension does not have the feedback permission.
3936 EXPECT_EQ(declarative_net_request::kErrorGetMatchedRulesMissingPermissions,
3937 GetMatchedRuleCount(extension_id, base::nullopt /* tab_id */,
3938 base::nullopt /* timestamp */));
3939
3940 // Calling getMatchedRules for a tab without the activeTab permission granted
3941 // should result in an error.
3942 EXPECT_EQ(declarative_net_request::kErrorGetMatchedRulesMissingPermissions,
3943 GetMatchedRuleCount(extension_id, first_tab_id,
3944 base::nullopt /* timestamp */));
3945
3946 // Calling getMatchedRules for a tab with the activeTab permission granted
3947 // should return the rules matched for that tab.
3948 EXPECT_EQ("1", GetMatchedRuleCount(extension_id, second_tab_id,
3949 base::nullopt /* timestamp */));
3950 }
3951
3952 // Test that getMatchedRules will not be throttled if the call is associated
3953 // with a user gesture.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,GetMatchedRulesNoThrottlingIfUserGesture)3954 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
3955 GetMatchedRulesNoThrottlingIfUserGesture) {
3956 // Load the extension with a background script so scripts can be run from its
3957 // generated background page. Also grant the feedback permission for the
3958 // extension so it has access to the getMatchedRules API function.
3959 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript |
3960 ConfigFlag::kConfig_HasFeedbackPermission);
3961
3962 // Ensure that GetMatchedRules is being throttled.
3963 DeclarativeNetRequestGetMatchedRulesFunction::
3964 set_disable_throttling_for_tests(false);
3965
3966 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({}));
3967 const ExtensionId& extension_id = last_loaded_extension_id();
3968
3969 auto get_matched_rules_count = [this, &extension_id](bool user_gesture) {
3970 auto user_gesture_setting =
3971 user_gesture ? browsertest_util::ScriptUserActivation::kActivate
3972 : browsertest_util::ScriptUserActivation::kDontActivate;
3973
3974 return GetMatchedRuleCount(extension_id, base::nullopt /* tab_id */,
3975 base::nullopt /* timestamp */,
3976 user_gesture_setting);
3977 };
3978
3979 // Call getMatchedRules without a user gesture, until the quota is reached.
3980 // None of these calls should return an error.
3981 for (int i = 1; i <= dnr_api::MAX_GETMATCHEDRULES_CALLS_PER_INTERVAL; ++i) {
3982 SCOPED_TRACE(base::StringPrintf(
3983 "Testing getMatchedRules call without user gesture %d of %d", i,
3984 dnr_api::MAX_GETMATCHEDRULES_CALLS_PER_INTERVAL));
3985 EXPECT_EQ("0", get_matched_rules_count(false));
3986 }
3987
3988 // Calling getMatchedRules without a user gesture should return an error after
3989 // the quota has been reached.
3990 EXPECT_EQ(
3991 "This request exceeds the MAX_GETMATCHEDRULES_CALLS_PER_INTERVAL quota.",
3992 get_matched_rules_count(false));
3993
3994 // Calling getMatchedRules with a user gesture should not return an error even
3995 // after the quota has been reached.
3996 EXPECT_EQ("0", get_matched_rules_count(true));
3997 }
3998
3999 // Tests extension update for an extension using declarativeNetRequest.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,ExtensionRemovesOneRulesetOnUpdate)4000 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,
4001 ExtensionRemovesOneRulesetOnUpdate) {
4002 auto create_single_rule_ruleset = [this](
4003 const std::string& ruleset_id_and_path,
4004 bool enabled,
4005 const std::string& filter) {
4006 std::vector<TestRule> rules = {CreateMainFrameBlockRule(filter)};
4007 return TestRulesetInfo(ruleset_id_and_path, *ToListValue(rules), enabled);
4008 };
4009
4010 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
4011
4012 std::vector<TestRulesetInfo> rulesets = {
4013 create_single_rule_ruleset("id1", true, "google"),
4014 create_single_rule_ruleset("id2", false, "yahoo"),
4015 create_single_rule_ruleset("id3", true, "example"),
4016 };
4017
4018 constexpr char kDirectory1[] = "dir1";
4019 ASSERT_NO_FATAL_FAILURE(
4020 LoadExtensionWithRulesets(rulesets, kDirectory1, {} /* hosts */));
4021 const ExtensionId extension_id = last_loaded_extension_id();
4022 const Extension* extension = last_loaded_extension();
4023
4024 // Also add a dynamic rule.
4025 ASSERT_NO_FATAL_FAILURE(
4026 AddDynamicRules(extension_id, {CreateMainFrameBlockRule("dynamic")}));
4027
4028 // Also update the set of enabled static rulesets.
4029 ASSERT_NO_FATAL_FAILURE(
4030 UpdateEnabledRulesets(extension_id, {"id1"}, {"id2", "id3"}));
4031
4032 CompositeMatcher* composite_matcher =
4033 ruleset_manager()->GetMatcherForExtension(extension_id);
4034 ASSERT_TRUE(composite_matcher);
4035 EXPECT_THAT(GetPublicRulesetIDs(*extension, *composite_matcher),
4036 UnorderedElementsAre("id2", "id3", dnr_api::DYNAMIC_RULESET_ID));
4037
4038 // Also sanity check the extension prefs entry for the rulesets.
4039 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
4040 ASSERT_TRUE(prefs);
4041 int checksum = -1;
4042 int dynamic_checksum_1 = -1;
4043 EXPECT_TRUE(prefs->GetDNRStaticRulesetChecksum(
4044 extension_id, kMinValidStaticRulesetID, &checksum));
4045 EXPECT_TRUE(prefs->GetDNRStaticRulesetChecksum(
4046 extension_id, RulesetID(kMinValidStaticRulesetID.value() + 1),
4047 &checksum));
4048 EXPECT_TRUE(prefs->GetDNRStaticRulesetChecksum(
4049 extension_id, RulesetID(kMinValidStaticRulesetID.value() + 2),
4050 &checksum));
4051 EXPECT_FALSE(prefs->GetDNRStaticRulesetChecksum(
4052 extension_id, RulesetID(kMinValidStaticRulesetID.value() + 3),
4053 &checksum));
4054 EXPECT_TRUE(
4055 prefs->GetDNRDynamicRulesetChecksum(extension_id, &dynamic_checksum_1));
4056 base::Optional<std::set<RulesetID>> enabled_static_rulesets =
4057 prefs->GetDNREnabledStaticRulesets(extension_id);
4058 ASSERT_TRUE(enabled_static_rulesets);
4059 EXPECT_THAT(
4060 *enabled_static_rulesets,
4061 UnorderedElementsAre(RulesetID(kMinValidStaticRulesetID.value() + 1),
4062 RulesetID(kMinValidStaticRulesetID.value() + 2)));
4063
4064 std::vector<TestRulesetInfo> new_rulesets = {
4065 create_single_rule_ruleset("id1", true, "yahoo"),
4066 create_single_rule_ruleset("new_id2", false, "msn")};
4067
4068 const char* kDirectory2 = "dir2";
4069 ASSERT_NO_FATAL_FAILURE(
4070 UpdateLastLoadedExtension(new_rulesets, kDirectory2, {} /* hosts */,
4071 0 /* expected_extension_ruleset_count_change */,
4072 true /* has_dynamic_ruleset */));
4073 extension = extension_registry()->GetExtensionById(
4074 extension_id, extensions::ExtensionRegistry::ENABLED);
4075
4076 composite_matcher = ruleset_manager()->GetMatcherForExtension(extension_id);
4077 ASSERT_TRUE(composite_matcher);
4078
4079 EXPECT_THAT(GetPublicRulesetIDs(*extension, *composite_matcher),
4080 UnorderedElementsAre("id1", dnr_api::DYNAMIC_RULESET_ID));
4081
4082 int dynamic_checksum_2;
4083 EXPECT_TRUE(prefs->GetDNRStaticRulesetChecksum(
4084 extension_id, kMinValidStaticRulesetID, &checksum));
4085 EXPECT_TRUE(prefs->GetDNRStaticRulesetChecksum(
4086 extension_id, RulesetID(kMinValidStaticRulesetID.value() + 1),
4087 &checksum));
4088 EXPECT_FALSE(prefs->GetDNRStaticRulesetChecksum(
4089 extension_id, RulesetID(kMinValidStaticRulesetID.value() + 2),
4090 &checksum));
4091 EXPECT_TRUE(
4092 prefs->GetDNRDynamicRulesetChecksum(extension_id, &dynamic_checksum_2));
4093 EXPECT_EQ(dynamic_checksum_2, dynamic_checksum_1);
4094
4095 // Ensure the preference for enabled static rulesets is cleared on extension
4096 // update.
4097 EXPECT_FALSE(prefs->GetDNREnabledStaticRulesets(extension_id));
4098 }
4099
4100 // Tests extension update for an extension using declarativeNetRequest.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,ExtensionRemovesAllRulesetsOnUpdate)4101 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,
4102 ExtensionRemovesAllRulesetsOnUpdate) {
4103 auto create_single_rule_ruleset = [this](
4104 const std::string& ruleset_id_and_path,
4105 bool enabled,
4106 const std::string& filter) {
4107 std::vector<TestRule> rules = {CreateMainFrameBlockRule(filter)};
4108 return TestRulesetInfo(ruleset_id_and_path, *ToListValue(rules), enabled);
4109 };
4110
4111 std::vector<TestRulesetInfo> rulesets = {
4112 create_single_rule_ruleset("id1", true, "google"),
4113 create_single_rule_ruleset("id2", true, "example")};
4114
4115 const char* kDirectory1 = "dir1";
4116 ASSERT_NO_FATAL_FAILURE(
4117 LoadExtensionWithRulesets(rulesets, kDirectory1, {} /* hosts */));
4118 const ExtensionId extension_id = last_loaded_extension_id();
4119 const Extension* extension = last_loaded_extension();
4120
4121 CompositeMatcher* composite_matcher =
4122 ruleset_manager()->GetMatcherForExtension(extension_id);
4123 ASSERT_TRUE(composite_matcher);
4124
4125 EXPECT_THAT(GetPublicRulesetIDs(*extension, *composite_matcher),
4126 UnorderedElementsAre("id1", "id2"));
4127
4128 // Also sanity check the extension prefs entry for the rulesets.
4129 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
4130 ASSERT_TRUE(prefs);
4131 int checksum = -1;
4132 EXPECT_TRUE(prefs->GetDNRStaticRulesetChecksum(
4133 extension_id, kMinValidStaticRulesetID, &checksum));
4134 EXPECT_TRUE(prefs->GetDNRStaticRulesetChecksum(
4135 extension_id, RulesetID(kMinValidStaticRulesetID.value() + 1),
4136 &checksum));
4137 EXPECT_FALSE(prefs->GetDNRDynamicRulesetChecksum(extension_id, &checksum));
4138
4139 const char* kDirectory2 = "dir2";
4140 ASSERT_NO_FATAL_FAILURE(UpdateLastLoadedExtension(
4141 {} /* new_rulesets */, kDirectory2, {} /* hosts */,
4142 -1 /* expected_extension_ruleset_count_change */,
4143 false /* has_dynamic_ruleset */));
4144 extension = extension_registry()->GetExtensionById(
4145 extension_id, extensions::ExtensionRegistry::ENABLED);
4146
4147 composite_matcher = ruleset_manager()->GetMatcherForExtension(extension_id);
4148 EXPECT_FALSE(composite_matcher);
4149
4150 // Ensure the prefs entry are cleared appropriately.
4151 EXPECT_FALSE(prefs->GetDNRStaticRulesetChecksum(
4152 extension_id, kMinValidStaticRulesetID, &checksum));
4153 EXPECT_FALSE(prefs->GetDNRStaticRulesetChecksum(
4154 extension_id, RulesetID(kMinValidStaticRulesetID.value() + 1),
4155 &checksum));
4156 EXPECT_FALSE(prefs->GetDNRDynamicRulesetChecksum(extension_id, &checksum));
4157 }
4158
4159 // Tests the allowAllRequests action.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,AllowAllRequests)4160 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, AllowAllRequests) {
4161 struct RuleData {
4162 int id;
4163 int priority;
4164 std::string action_type;
4165 std::string url_filter;
4166 bool is_regex_rule;
4167 base::Optional<std::vector<std::string>> resource_types;
4168 };
4169
4170 auto run_test = [this](const std::string& extension_directory,
4171 const GURL& page_url,
4172 const std::vector<RuleData>& rule_data,
4173 const std::vector<std::string>& paths_seen,
4174 const std::vector<std::string>& paths_not_seen) {
4175 std::vector<TestRule> test_rules;
4176 for (const auto& rule : rule_data) {
4177 TestRule test_rule = CreateGenericRule();
4178 test_rule.id = rule.id;
4179 test_rule.priority = rule.priority;
4180 test_rule.action->type = rule.action_type;
4181 test_rule.condition->url_filter.reset();
4182 if (rule.is_regex_rule)
4183 test_rule.condition->regex_filter = rule.url_filter;
4184 else
4185 test_rule.condition->url_filter = rule.url_filter;
4186 test_rule.condition->resource_types = rule.resource_types;
4187 test_rules.push_back(test_rule);
4188 }
4189
4190 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
4191 test_rules, extension_directory, {} /* hosts */));
4192
4193 ui_test_utils::NavigateToURL(browser(), page_url);
4194
4195 const std::set<GURL> requests_seen = GetAndResetRequestsToServer();
4196
4197 for (const auto& path : paths_seen) {
4198 GURL expected_request_url = embedded_test_server()->GetURL(path);
4199 EXPECT_TRUE(base::Contains(requests_seen, expected_request_url))
4200 << expected_request_url.spec()
4201 << " was not requested from the server.";
4202 }
4203
4204 for (const auto& path : paths_not_seen) {
4205 GURL expected_request_url = embedded_test_server()->GetURL(path);
4206 EXPECT_FALSE(base::Contains(requests_seen, expected_request_url))
4207 << expected_request_url.spec() << " request seen unexpectedly.";
4208 }
4209
4210 UninstallExtension(last_loaded_extension_id());
4211 };
4212
4213 // This page causes the following requests.
4214 GURL page_url = embedded_test_server()->GetURL("example.com",
4215 "/page_with_two_frames.html");
4216 std::vector<std::string> requests = {
4217 {"/page_with_two_frames.html"}, // 0
4218 {"/subresources/script.js"}, // 1
4219 {"/child_frame.html?frame=1"}, // 2
4220 {"/subresources/script.js?frameId=1"}, // 3
4221 {"/child_frame.html?frame=2"}, // 4
4222 {"/subresources/script.js?frameId=2"}, // 5
4223 };
4224
4225 {
4226 SCOPED_TRACE("Testing case 1");
4227 std::vector<RuleData> rule_data = {
4228 {1, 4, "allowAllRequests", "page_with_two_frames\\.html", true,
4229 std::vector<std::string>({"main_frame"})},
4230 {2, 3, "block", "script.js|", false},
4231 {3, 5, "block", "script.js?frameId=1", false},
4232 {4, 3, "block", "script\\.js?frameId=2", true}};
4233 // Requests:
4234 // -/page_with_two_frames.html (Matching rule=1)
4235 // -/subresources/script.js (Matching rule=[1,2] Winner=1)
4236 // -/child_frame.html?frame=1 (Matching Rule=1)
4237 // -/subresources/script.js?frameId=1 (Matching Rule=[1,3] Winner=3)
4238 // -/child_frame.html?frame=2 (Matching Rule=1)
4239 // -/subresources/script.js?frameId=2 (Matching Rule=1,4 Winner=1)
4240 // Hence only requests[3] is blocked.
4241 run_test("case_1", page_url, rule_data,
4242 {requests[0], requests[1], requests[2], requests[4], requests[5]},
4243 {requests[3]});
4244 }
4245
4246 {
4247 SCOPED_TRACE("Testing case 2");
4248 std::vector<RuleData> rule_data = {
4249 {1, 4, "allowAllRequests", "page_with_two_frames.html", false,
4250 std::vector<std::string>({"main_frame"})},
4251 {2, 5, "block", "script\\.js", true},
4252 {3, 6, "allowAllRequests", "child_frame.html", false,
4253 std::vector<std::string>({"sub_frame"})},
4254 {4, 7, "block", "frame=1", true}};
4255
4256 // Requests:
4257 // -/page_with_two_frames.html (Matching rule=1)
4258 // -/subresources/script.js (Matching rule=[1,2] Winner=2, Blocked)
4259 // -/child_frame.html?frame=1 (Matching Rule=[1,3,4] Winner=4, Blocked)
4260 // -/subresources/script.js?frameId=1 (Source Frame was blocked)
4261 // -/child_frame.html?frame=2 (Matching Rule=[1,3] Winner=3)
4262 // -/subresources/script.js?frameId=2 (Matching Rule=[1,2,3] Winner=3)
4263 run_test("case_2", page_url, rule_data,
4264 {requests[0], requests[4], requests[5]},
4265 {requests[1], requests[2], requests[3]});
4266 }
4267
4268 {
4269 SCOPED_TRACE("Testing case 3");
4270 std::vector<RuleData> rule_data = {
4271 {1, 1, "allowAllRequests", "page_with_two_frames.html", false,
4272 std::vector<std::string>({"main_frame"})},
4273 {2, 5, "block", ".*", true},
4274 };
4275
4276 // Requests:
4277 // -/page_with_two_frames.html (Matching rule=1)
4278 // -/subresources/script.js (Matching rule=[1,2] Winner=2)
4279 // -/child_frame.html?frame=1 (Matching Rule=[1,2] Winner=2)
4280 // -/subresources/script.js?frameId=1 (Source Frame was blocked)
4281 // -/child_frame.html?frame=2 (Matching Rule=[1,2] Winner=2)
4282 // -/subresources/script.js?frameId=2 (Source frame was blocked)
4283 // Hence only the main-frame request goes through.
4284 run_test("case_3", page_url, rule_data, {requests[0]},
4285 {requests[1], requests[2], requests[3], requests[4], requests[5]});
4286 }
4287 {
4288 SCOPED_TRACE("Testing case 4");
4289 std::vector<RuleData> rule_data = {
4290 {1, 6, "allowAllRequests", "page_with_two_frames\\.html", true,
4291 std::vector<std::string>({"main_frame"})},
4292 {2, 5, "block", "*", false},
4293 };
4294
4295 // Requests:
4296 // -/page_with_two_frames.html (Matching rule=1)
4297 // -/subresources/script.js (Matching rule=[1,2] Winner=1)
4298 // -/child_frame.html?frame=1 (Matching Rule=[1,2] Winner=1)
4299 // -/subresources/script.js?frameId=1 (Matching Rule=[1,2] Winner=1)
4300 // -/child_frame.html?frame=2 (Matching Rule=[1,2] Winner=1)
4301 // -/subresources/script.js?frameId=2 (Matching Rule=[1,2] Winner=1)
4302 // Hence all requests go through.
4303 run_test("case_4", page_url, rule_data,
4304 {requests[0], requests[1], requests[2], requests[3], requests[4],
4305 requests[5]},
4306 {});
4307 }
4308 }
4309
4310 class DeclarativeNetRequestIdentifiabilityTest
4311 : public DeclarativeNetRequestBrowserTest {
4312 public:
SetUpOnMainThread()4313 void SetUpOnMainThread() override {
4314 identifiability_metrics_test_helper_.SetUpOnMainThread();
4315 DeclarativeNetRequestBrowserTest::SetUpOnMainThread();
4316 }
4317
4318 protected:
4319 IdentifiabilityMetricsTestHelper identifiability_metrics_test_helper_;
4320 };
4321
4322 // Make sure that a declaratively-blocked request gets recorded for
4323 // identifiability study.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestIdentifiabilityTest,DeclarativeBlockRecorded)4324 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestIdentifiabilityTest,
4325 DeclarativeBlockRecorded) {
4326 TestRule rule = CreateGenericRule();
4327 rule.condition->url_filter = std::string("page2.html^");
4328 rule.condition->resource_types = std::vector<std::string>({"main_frame"});
4329
4330 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
4331
4332 base::RunLoop run_loop;
4333 identifiability_metrics_test_helper_.PrepareForTest(&run_loop);
4334
4335 GURL url = embedded_test_server()->GetURL("google.com",
4336 "/pages_with_script/page2.html");
4337 ui_test_utils::NavigateToURL(browser(), url);
4338 EXPECT_FALSE(WasFrameWithScriptLoaded(GetMainFrame()));
4339 EXPECT_EQ(content::PAGE_TYPE_ERROR, GetPageType());
4340
4341 content::WebContents* web_contents =
4342 browser()->tab_strip_model()->GetActiveWebContents();
4343 ukm::SourceId frame_id = web_contents->GetMainFrame()->GetPageUkmSourceId();
4344
4345 std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
4346 identifiability_metrics_test_helper_.NavigateToBlankAndWaitForMetrics(
4347 web_contents, &run_loop);
4348
4349 std::set<ukm::SourceId> cancel_ids =
4350 IdentifiabilityMetricsTestHelper::GetSourceIDsForSurfaceAndExtension(
4351 merged_entries,
4352 blink::IdentifiableSurface::Type::kExtensionCancelRequest,
4353 last_loaded_extension_id());
4354 ASSERT_EQ(1u, cancel_ids.size());
4355 EXPECT_TRUE(base::Contains(cancel_ids, frame_id));
4356 }
4357
4358 // Test fixture to verify that host permissions for the request url and the
4359 // request initiator are properly checked when redirecting requests. Loads an
4360 // example.com url with four sub-frames named frame_[1..4] from hosts
4361 // frame_[1..4].com. These subframes point to invalid sources. The initiator for
4362 // these frames will be example.com. Loads an extension which redirects these
4363 // sub-frames to a valid source. Verifies that the correct frames are redirected
4364 // depending on the host permissions for the extension.
4365 class DeclarativeNetRequestHostPermissionsBrowserTest
4366 : public DeclarativeNetRequestBrowserTest {
4367 public:
DeclarativeNetRequestHostPermissionsBrowserTest()4368 DeclarativeNetRequestHostPermissionsBrowserTest() {}
4369
4370 protected:
4371 struct FrameRedirectResult {
4372 std::string child_frame_name;
4373 bool expect_frame_redirected;
4374 };
4375
LoadExtensionWithHostPermissions(const std::vector<std::string> & hosts)4376 void LoadExtensionWithHostPermissions(const std::vector<std::string>& hosts) {
4377 TestRule rule = CreateGenericRule();
4378 rule.priority = kMinValidPriority;
4379 rule.condition->url_filter = std::string("not_a_valid_child_frame.html");
4380 rule.condition->resource_types = std::vector<std::string>({"sub_frame"});
4381 rule.action->type = std::string("redirect");
4382 rule.action->redirect.emplace();
4383 rule.action->redirect->url =
4384 embedded_test_server()->GetURL("foo.com", "/child_frame.html").spec();
4385
4386 ASSERT_NO_FATAL_FAILURE(
4387 LoadExtensionWithRules({rule}, "test_extension", hosts));
4388 }
4389
RunTests(const std::vector<FrameRedirectResult> & expected_results)4390 void RunTests(const std::vector<FrameRedirectResult>& expected_results) {
4391 ASSERT_EQ(4u, expected_results.size());
4392
4393 GURL url = embedded_test_server()->GetURL("example.com",
4394 "/page_with_four_frames.html");
4395 ui_test_utils::NavigateToURL(browser(), url);
4396 ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
4397
4398 for (const auto& frame_result : expected_results) {
4399 SCOPED_TRACE(base::StringPrintf("Testing child frame named %s",
4400 frame_result.child_frame_name.c_str()));
4401
4402 content::RenderFrameHost* child =
4403 GetFrameByName(frame_result.child_frame_name);
4404 ASSERT_TRUE(child);
4405 EXPECT_EQ(frame_result.expect_frame_redirected,
4406 WasFrameWithScriptLoaded(child));
4407 }
4408 }
4409
GetMatchPatternForDomain(const std::string & domain) const4410 std::string GetMatchPatternForDomain(const std::string& domain) const {
4411 return "*://*." + domain + ".com/*";
4412 }
4413
4414 private:
4415 DISALLOW_COPY_AND_ASSIGN(DeclarativeNetRequestHostPermissionsBrowserTest);
4416 };
4417
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestHostPermissionsBrowserTest,AllURLs1)4418 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestHostPermissionsBrowserTest,
4419 AllURLs1) {
4420 // All frames should be redirected since the extension has access to all
4421 // hosts.
4422 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithHostPermissions({"<all_urls>"}));
4423 RunTests({{"frame_1", true},
4424 {"frame_2", true},
4425 {"frame_3", true},
4426 {"frame_4", true}});
4427 }
4428
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestHostPermissionsBrowserTest,NoPermissions)4429 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestHostPermissionsBrowserTest,
4430 NoPermissions) {
4431 // The extension has no host permissions. No frames should be redirected.
4432 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithHostPermissions({}));
4433 RunTests({{"frame_1", false},
4434 {"frame_2", false},
4435 {"frame_3", false},
4436 {"frame_4", false}});
4437 }
4438
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestHostPermissionsBrowserTest,SubframesRequireNoInitiatorPermissions)4439 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestHostPermissionsBrowserTest,
4440 SubframesRequireNoInitiatorPermissions) {
4441 // The extension has access to requests to "frame_1.com" and "frame_2.com".
4442 // These should be redirected. Note: extensions don't need access to the
4443 // initiator of a navigation request to redirect it (See crbug.com/918137).
4444 ASSERT_NO_FATAL_FAILURE(
4445 LoadExtensionWithHostPermissions({GetMatchPatternForDomain("frame_1"),
4446 GetMatchPatternForDomain("frame_2")}));
4447 RunTests({{"frame_1", true},
4448 {"frame_2", true},
4449 {"frame_3", false},
4450 {"frame_4", false}});
4451 }
4452
4453 // Fixture to test the "resourceTypes" and "excludedResourceTypes" fields of a
4454 // declarative rule condition.
4455 class DeclarativeNetRequestResourceTypeBrowserTest
4456 : public DeclarativeNetRequestBrowserTest {
4457 public:
DeclarativeNetRequestResourceTypeBrowserTest()4458 DeclarativeNetRequestResourceTypeBrowserTest() {}
4459
4460 protected:
4461 // TODO(crbug.com/696822): Add tests for "object", "ping", "other", "font",
4462 // "csp_report".
4463 enum ResourceTypeMask {
4464 kNone = 0,
4465 kSubframe = 1 << 0,
4466 kStylesheet = 1 << 1,
4467 kScript = 1 << 2,
4468 kImage = 1 << 3,
4469 kXHR = 1 << 4,
4470 kMedia = 1 << 5,
4471 kWebSocket = 1 << 6,
4472 kAll = (1 << 7) - 1
4473 };
4474
4475 struct TestCase {
4476 std::string hostname;
4477 int blocked_mask;
4478 };
4479
RunTests(const std::vector<TestCase> & test_cases)4480 void RunTests(const std::vector<TestCase>& test_cases) {
4481 // Start a web socket test server to test the websocket resource type.
4482 net::SpawnedTestServer websocket_test_server(
4483 net::SpawnedTestServer::TYPE_WS, net::GetWebSocketTestDataDirectory());
4484 ASSERT_TRUE(websocket_test_server.Start());
4485
4486 // The |websocket_url| will echo the message we send to it.
4487 GURL websocket_url = websocket_test_server.GetURL("echo-with-no-extension");
4488
4489 auto execute_script = [](content::RenderFrameHost* frame,
4490 const std::string& script) {
4491 bool subresource_loaded = false;
4492 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(frame, script,
4493 &subresource_loaded));
4494 return subresource_loaded;
4495 };
4496
4497 for (const auto& test_case : test_cases) {
4498 GURL url = embedded_test_server()->GetURL(test_case.hostname,
4499 "/subresources.html");
4500 SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str()));
4501
4502 ui_test_utils::NavigateToURL(browser(), url);
4503 ASSERT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
4504
4505 content::RenderFrameHost* frame = GetMainFrame();
4506
4507 // sub-frame.
4508 EXPECT_EQ(
4509 !(test_case.blocked_mask & kSubframe),
4510 execute_script(
4511 frame, "domAutomationController.send(!!window.frameLoaded);"));
4512
4513 // stylesheet
4514 EXPECT_EQ(!(test_case.blocked_mask & kStylesheet),
4515 execute_script(frame, "testStylesheet();"));
4516
4517 // script
4518 EXPECT_EQ(!(test_case.blocked_mask & kScript),
4519 execute_script(frame, "testScript();"));
4520
4521 // image
4522 EXPECT_EQ(!(test_case.blocked_mask & kImage),
4523 execute_script(frame, "testImage();"));
4524
4525 // xhr
4526 EXPECT_EQ(!(test_case.blocked_mask & kXHR),
4527 execute_script(frame, "testXHR();"));
4528
4529 // media
4530 EXPECT_EQ(!(test_case.blocked_mask & kMedia),
4531 execute_script(frame, "testMedia();"));
4532
4533 // websocket
4534 EXPECT_EQ(!(test_case.blocked_mask & kWebSocket),
4535 execute_script(
4536 frame, base::StringPrintf("testWebSocket('%s');",
4537 websocket_url.spec().c_str())));
4538 }
4539 }
4540
4541 // Loads an extension to test blocking different resource types.
LoadExtension()4542 void LoadExtension() {
4543 struct {
4544 std::string domain;
4545 size_t id;
4546 std::vector<std::string> resource_types;
4547 std::vector<std::string> excluded_resource_types;
4548 } rules_data[] = {
4549 {"block_subframe.com", 1, {"sub_frame"}, {}},
4550 {"block_stylesheet.com", 2, {"stylesheet"}, {}},
4551 {"block_script.com", 3, {"script"}, {}},
4552 {"block_image.com", 4, {"image"}, {}},
4553 {"block_xhr.com", 5, {"xmlhttprequest"}, {}},
4554 {"block_media.com", 6, {"media"}, {}},
4555 {"block_websocket.com", 7, {"websocket"}, {}},
4556 {"block_image_and_stylesheet.com", 8, {"image", "stylesheet"}, {}},
4557 {"block_subframe_and_xhr.com", 11, {"sub_frame", "xmlhttprequest"}, {}},
4558 {"block_all.com", 9, {}, {}},
4559 {"block_all_but_xhr_and_script.com",
4560 10,
4561 {},
4562 {"xmlhttprequest", "script"}},
4563 };
4564
4565 std::vector<TestRule> rules;
4566 for (const auto& rule_data : rules_data) {
4567 TestRule rule = CreateGenericRule();
4568
4569 // The "resourceTypes" property (i.e. |rule.condition->resource_types|)
4570 // should not be an empty list. It should either be omitted or be a non-
4571 // empty list.
4572 if (rule_data.resource_types.empty())
4573 rule.condition->resource_types = base::nullopt;
4574 else
4575 rule.condition->resource_types = rule_data.resource_types;
4576
4577 rule.condition->excluded_resource_types =
4578 rule_data.excluded_resource_types;
4579 rule.id = rule_data.id;
4580 rule.condition->domains = std::vector<std::string>({rule_data.domain});
4581 // Don't specify the urlFilter, which should behaves the same as "*".
4582 rule.condition->url_filter = base::nullopt;
4583 rules.push_back(rule);
4584 }
4585 LoadExtensionWithRules(rules);
4586 }
4587
4588 private:
4589 DISALLOW_COPY_AND_ASSIGN(DeclarativeNetRequestResourceTypeBrowserTest);
4590 };
4591
4592 // These are split into two tests to prevent a timeout. See crbug.com/787957.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestResourceTypeBrowserTest,Test1)4593 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestResourceTypeBrowserTest, Test1) {
4594 ASSERT_NO_FATAL_FAILURE(LoadExtension());
4595 RunTests({{"block_subframe.com", kSubframe},
4596 {"block_stylesheet.com", kStylesheet},
4597 {"block_script.com", kScript},
4598 {"block_image.com", kImage},
4599 {"block_xhr.com", kXHR},
4600 {"block_media.com", kMedia}});
4601 }
4602
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestResourceTypeBrowserTest,Test2)4603 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestResourceTypeBrowserTest, Test2) {
4604 ASSERT_NO_FATAL_FAILURE(LoadExtension());
4605 RunTests({{"block_websocket.com", kWebSocket},
4606 {"block_image_and_stylesheet.com", kImage | kStylesheet},
4607 {"block_subframe_and_xhr.com", kSubframe | kXHR},
4608 {"block_all.com", kAll},
4609 {"block_all_but_xhr_and_script.com", kAll & ~kXHR & ~kScript},
4610 {"block_none.com", kNone}});
4611 }
4612
4613 class DeclarativeNetRequestSubresourceWebBundlesBrowserTest
4614 : public DeclarativeNetRequestBrowserTest {
4615 public:
4616 DeclarativeNetRequestSubresourceWebBundlesBrowserTest() = default;
SetUp()4617 void SetUp() override {
4618 feature_list_.InitWithFeatures({features::kSubresourceWebBundles}, {});
4619 DeclarativeNetRequestBrowserTest::SetUp();
4620 }
SetUpOnMainThread()4621 void SetUpOnMainThread() override {
4622 ExtensionBrowserTest::SetUpOnMainThread();
4623 CreateTempDir();
4624 InitializeRulesetManagerObserver();
4625 }
4626
4627 protected:
TryLoadScript(const char * script_src)4628 bool TryLoadScript(const char* script_src) {
4629 content::WebContents* web_contents =
4630 browser()->tab_strip_model()->GetActiveWebContents();
4631 bool success = false;
4632 std::string script = base::StringPrintf(R"(
4633 (() => {
4634 const script = document.createElement('script');
4635 script.addEventListener('load', () => {
4636 window.domAutomationController.send(true);
4637 });
4638 script.addEventListener('error', () => {
4639 window.domAutomationController.send(false);
4640 });
4641 script.src = '%s';
4642 document.body.appendChild(script);
4643 })();
4644 )",
4645 script_src);
4646 EXPECT_TRUE(ExecuteScriptAndExtractBool(web_contents->GetMainFrame(),
4647 script, &success));
4648 return success;
4649 }
4650
4651 // Registers a request handler for static content.
RegisterRequestHandler(const std::string & relative_url,const std::string & content_type,const std::string & content)4652 void RegisterRequestHandler(const std::string& relative_url,
4653 const std::string& content_type,
4654 const std::string& content) {
4655 embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting(
4656 [relative_url, content_type,
4657 content](const net::test_server::HttpRequest& request)
4658 -> std::unique_ptr<net::test_server::HttpResponse> {
4659 if (request.relative_url == relative_url) {
4660 auto response =
4661 std::make_unique<net::test_server::BasicHttpResponse>();
4662 response->set_code(net::HTTP_OK);
4663 response->set_content_type(content_type);
4664 response->set_content(content);
4665 return std::move(response);
4666 }
4667 return nullptr;
4668 }));
4669 }
4670
4671 // Registers a request handler for web bundle. This method takes a pointer of
4672 // the content of the web bundle, because we can't create the content of the
4673 // web bundle before starting the server since we need to know the port number
4674 // of the test server due to the same-origin restriction (the origin of
4675 // subresource which is written in the web bundle must be same as the origin
4676 // of the web bundle), and we can't call
4677 // EmbeddedTestServer::RegisterRequestHandler() after starting the server.
RegisterWebBundleRequestHandler(const std::string & relative_url,const std::string * web_bundle)4678 void RegisterWebBundleRequestHandler(const std::string& relative_url,
4679 const std::string* web_bundle) {
4680 embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting(
4681 [relative_url, web_bundle](const net::test_server::HttpRequest& request)
4682 -> std::unique_ptr<net::test_server::HttpResponse> {
4683 if (request.relative_url == relative_url) {
4684 auto response =
4685 std::make_unique<net::test_server::BasicHttpResponse>();
4686 response->set_code(net::HTTP_OK);
4687 response->set_content_type("application/webbundle");
4688 response->set_content(*web_bundle);
4689 return std::move(response);
4690 }
4691 return nullptr;
4692 }));
4693 }
4694
4695 private:
4696 base::test::ScopedFeatureList feature_list_;
4697 };
4698
4699 // Ensure DeclarativeNetRequest API can block the requests for the subresources
4700 // inside the web bundle.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestSubresourceWebBundlesBrowserTest,RequestCanceled)4701 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestSubresourceWebBundlesBrowserTest,
4702 RequestCanceled) {
4703 TestRule rule = CreateGenericRule();
4704 std::vector<TestRule> rules;
4705 rule.id = kMinValidID;
4706 rule.condition->url_filter = "cancel.js|";
4707 rule.condition->resource_types = std::vector<std::string>({"script"});
4708 rule.priority = 1;
4709 rules.push_back(rule);
4710 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules));
4711
4712 const char kPageHtml[] = R"(
4713 <title>Loaded</title>
4714 <body>
4715 <script>
4716 (() => {
4717 const wbn_url =
4718 new URL('./web_bundle.wbn', location.href).toString();
4719 const pass_js_url = new URL('./pass.js', location.href).toString();
4720 const cancel_js_url =
4721 new URL('./cancel.js', location.href).toString();
4722 const link = document.createElement('link');
4723 link.rel = 'webbundle';
4724 link.href = wbn_url;
4725 link.resources = pass_js_url + ' ' + cancel_js_url;
4726 document.body.appendChild(link);
4727 })();
4728 </script>
4729 </body>
4730 )";
4731
4732 std::string web_bundle;
4733 RegisterWebBundleRequestHandler("/web_bundle.wbn", &web_bundle);
4734 RegisterRequestHandler("/test.html", "text/html", kPageHtml);
4735 ASSERT_TRUE(embedded_test_server()->Start());
4736
4737 // Create a web bundle.
4738 std::string pass_js_url_str =
4739 embedded_test_server()->GetURL("/pass.js").spec();
4740 std::string cancel_js_url_str =
4741 embedded_test_server()->GetURL("/cancel.js").spec();
4742 // Currently the web bundle format requires a valid GURL for the fallback URL
4743 // of a web bundle. So we use |pass_js_url_str| for the fallback URL.
4744 // TODO(crbug.com/966753): Stop using |pass_js_url_str| when
4745 // https://github.com/WICG/webpackage/issues/590 is resolved.
4746 web_package::test::WebBundleBuilder builder(pass_js_url_str, "");
4747 auto pass_js_location = builder.AddResponse(
4748 {{":status", "200"}, {"content-type", "application/javascript"}},
4749 "document.title = 'script loaded';");
4750 auto cancel_js_location = builder.AddResponse(
4751 {{":status", "200"}, {"content-type", "application/javascript"}}, "");
4752 builder.AddIndexEntry(pass_js_url_str, "", {pass_js_location});
4753 builder.AddIndexEntry(cancel_js_url_str, "", {cancel_js_location});
4754 std::vector<uint8_t> bundle = builder.CreateBundle();
4755 web_bundle = std::string(bundle.begin(), bundle.end());
4756
4757 GURL page_url = embedded_test_server()->GetURL("/test.html");
4758 content::WebContents* web_contents =
4759 browser()->tab_strip_model()->GetActiveWebContents();
4760 ui_test_utils::NavigateToURL(browser(), page_url);
4761 EXPECT_EQ(page_url, web_contents->GetLastCommittedURL());
4762
4763 base::string16 expected_title = base::ASCIIToUTF16("script loaded");
4764 content::TitleWatcher title_watcher(web_contents, expected_title);
4765 EXPECT_TRUE(TryLoadScript("pass.js"));
4766 // Check that the script in the web bundle is correctly loaded even when the
4767 // extension with blocking handler intercepted the request.
4768 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
4769
4770 EXPECT_FALSE(TryLoadScript("cancel.js"));
4771 }
4772
4773 // Ensure DeclarativeNetRequest API can redirect the requests for the
4774 // subresources inside the web bundle.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestSubresourceWebBundlesBrowserTest,RequestRedirected)4775 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestSubresourceWebBundlesBrowserTest,
4776 RequestRedirected) {
4777 const char kPageHtml[] = R"(
4778 <title>Loaded</title>
4779 <body>
4780 <script>
4781 (() => {
4782 const wbn_url = new URL('./web_bundle.wbn', location.href).toString();
4783 const redirect_js_url =
4784 new URL('./redirect.js', location.href).toString();
4785 const redirected_js_url =
4786 new URL('./redirected.js', location.href).toString();
4787 const redirect_to_unlisted_js_url =
4788 new URL('./redirect_to_unlisted.js', location.href).toString();
4789 const redirect_to_server =
4790 new URL('./redirect_to_server.js', location.href).toString();
4791 const link = document.createElement('link');
4792 link.rel = 'webbundle';
4793 link.href = wbn_url;
4794 link.resources = redirect_js_url + ' ' + redirected_js_url + ' ' +
4795 redirect_to_unlisted_js_url + ' ' +
4796 redirect_to_server;
4797 document.body.appendChild(link);
4798 })();
4799 </script>
4800 </body>
4801 )";
4802
4803 std::string web_bundle;
4804 RegisterWebBundleRequestHandler("/web_bundle.wbn", &web_bundle);
4805 RegisterRequestHandler("/test.html", "text/html", kPageHtml);
4806 RegisterRequestHandler("/redirect_to_server.js", "application/javascript",
4807 "document.title = 'redirect_to_server';");
4808 ASSERT_TRUE(embedded_test_server()->Start());
4809
4810 // Create a web bundle.
4811 std::string redirect_js_url_str =
4812 embedded_test_server()->GetURL("/redirect.js").spec();
4813 std::string redirected_js_url_str =
4814 embedded_test_server()->GetURL("/redirected.js").spec();
4815 std::string redirect_to_unlisted_js_url_str =
4816 embedded_test_server()->GetURL("/redirect_to_unlisted.js").spec();
4817 std::string redirected_to_unlisted_js_url_str =
4818 embedded_test_server()->GetURL("/redirected_to_unlisted.js").spec();
4819 std::string redirect_to_server_js_url_str =
4820 embedded_test_server()->GetURL("/redirect_to_server.js").spec();
4821 // Currently the web bundle format requires a valid GURL for the fallback URL
4822 // of a web bundle. So we use |redirect_js_url_str| for the fallback URL.
4823 // TODO(crbug.com/966753): Stop using |redirect_js_url_str| when
4824 // https://github.com/WICG/webpackage/issues/590 is resolved.
4825 web_package::test::WebBundleBuilder builder(redirect_js_url_str, "");
4826 auto redirect_js_location = builder.AddResponse(
4827 {{":status", "200"}, {"content-type", "application/javascript"}},
4828 "document.title = 'redirect';");
4829 auto redirected_js_location = builder.AddResponse(
4830 {{":status", "200"}, {"content-type", "application/javascript"}},
4831 "document.title = 'redirected';");
4832 auto redirect_to_unlisted_location = builder.AddResponse(
4833 {{":status", "200"}, {"content-type", "application/javascript"}},
4834 "document.title = 'redirect_to_unlisted';");
4835 auto redirected_to_unlisted_js_location = builder.AddResponse(
4836 {{":status", "200"}, {"content-type", "application/javascript"}},
4837 "document.title = 'redirected_to_unlisted';");
4838 auto redirect_to_server_js_location = builder.AddResponse(
4839 {{":status", "200"}, {"content-type", "application/javascript"}},
4840 "document.title = 'redirect_to_server';");
4841 builder.AddIndexEntry(redirect_js_url_str, "", {redirect_js_location});
4842 builder.AddIndexEntry(redirected_js_url_str, "", {redirected_js_location});
4843 builder.AddIndexEntry(redirect_to_unlisted_js_url_str, "",
4844 {redirect_to_unlisted_location});
4845 builder.AddIndexEntry(redirected_to_unlisted_js_url_str, "",
4846 {redirected_to_unlisted_js_location});
4847 builder.AddIndexEntry(redirect_to_server_js_url_str, "",
4848 {redirect_to_server_js_location});
4849 std::vector<uint8_t> bundle = builder.CreateBundle();
4850 web_bundle = std::string(bundle.begin(), bundle.end());
4851
4852 struct {
4853 std::string url_filter;
4854 std::string redirect_url_path;
4855 } rules_data[] = {
4856 {"redirect.js|", "/redirected.js"},
4857 {"redirect_to_unlisted.js|", "/redirected_to_unlisted.js"},
4858 {"redirect_to_server.js|", "/redirected_to_server.js"},
4859 };
4860
4861 std::vector<TestRule> rules;
4862 int rule_id = kMinValidID;
4863 int rule_priority = 1;
4864 for (const auto& rule_data : rules_data) {
4865 TestRule rule = CreateGenericRule();
4866 rule.id = rule_id++;
4867 rule.priority = rule_priority++;
4868 rule.condition->url_filter = rule_data.url_filter;
4869 rule.condition->resource_types = std::vector<std::string>({"script"});
4870 rule.action->type = "redirect";
4871 rule.action->redirect.emplace();
4872 rule.action->redirect->url =
4873 embedded_test_server()->GetURL(rule_data.redirect_url_path).spec();
4874 rules.push_back(rule);
4875 }
4876 ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
4877 rules, "test_extension", {URLPattern::kAllUrlsPattern}));
4878
4879 GURL page_url = embedded_test_server()->GetURL("/test.html");
4880 content::WebContents* web_contents =
4881 browser()->tab_strip_model()->GetActiveWebContents();
4882 ui_test_utils::NavigateToURL(browser(), page_url);
4883 EXPECT_EQ(page_url, web_contents->GetLastCommittedURL());
4884 {
4885 base::string16 expected_title = base::ASCIIToUTF16("redirected");
4886 content::TitleWatcher title_watcher(web_contents, expected_title);
4887 EXPECT_TRUE(TryLoadScript("redirect.js"));
4888 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
4889 }
4890 {
4891 // In the current implementation, extensions can redirect the request to
4892 // the other resource in the web bundle even if the resource is not listed
4893 // in the resources attribute.
4894 base::string16 expected_title =
4895 base::ASCIIToUTF16("redirected_to_unlisted");
4896 content::TitleWatcher title_watcher(web_contents, expected_title);
4897 EXPECT_TRUE(TryLoadScript("redirect_to_unlisted.js"));
4898 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
4899 }
4900 // In the current implementation, extensions can't redirect the request to
4901 // outside the web bundle.
4902 EXPECT_FALSE(TryLoadScript("redirect_to_server.js"));
4903 }
4904
4905 class DeclarativeNetRequestGlobalRulesBrowserTest
4906 : public DeclarativeNetRequestBrowserTest {
4907 public:
DeclarativeNetRequestGlobalRulesBrowserTest()4908 DeclarativeNetRequestGlobalRulesBrowserTest() {
4909 scoped_feature_list_.InitAndEnableFeature(
4910 kDeclarativeNetRequestGlobalRules);
4911 }
4912
4913 protected:
VerifyExtensionAllocationInPrefs(const ExtensionId & extension_id,const base::Optional<size_t> & expected_rules_count)4914 void VerifyExtensionAllocationInPrefs(
4915 const ExtensionId& extension_id,
4916 const base::Optional<size_t>& expected_rules_count) {
4917 size_t actual_rules_count = 0;
4918
4919 const ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
4920 bool has_allocated_rules_count = prefs->GetDNRAllocatedGlobalRuleCount(
4921 extension_id, &actual_rules_count);
4922
4923 EXPECT_EQ(expected_rules_count.has_value(), has_allocated_rules_count);
4924 if (expected_rules_count.has_value())
4925 EXPECT_EQ(*expected_rules_count, actual_rules_count);
4926 }
4927
VerifyKeepExcessAllocation(const ExtensionId & extension_id,bool expected_keep_allocation)4928 void VerifyKeepExcessAllocation(const ExtensionId& extension_id,
4929 bool expected_keep_allocation) {
4930 const ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
4931 EXPECT_EQ(expected_keep_allocation,
4932 prefs->GetDNRKeepExcessAllocation(extension_id));
4933 }
4934
4935 private:
4936 base::test::ScopedFeatureList scoped_feature_list_;
4937
4938 // Override the API guaranteed minimum to prevent a timeout on loading the
4939 // extension.
4940 base::AutoReset<int> guaranteed_minimum_override_ =
4941 CreateScopedStaticGuaranteedMinimumOverrideForTesting(1);
4942
4943 // Similarly, override the global limit to prevent a timeout.
4944 base::AutoReset<int> global_limit_override_ =
4945 CreateScopedGlobalStaticRuleLimitOverrideForTesting(2);
4946 };
4947
4948 using DeclarativeNetRequestGlobalRulesBrowserTest_Packed =
4949 DeclarativeNetRequestGlobalRulesBrowserTest;
4950
4951 // Test that extensions with allocated global rules keep their allocations after
4952 // the browser restarts.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestGlobalRulesBrowserTest_Packed,PRE_GlobalRulesBrowserRestart)4953 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestGlobalRulesBrowserTest_Packed,
4954 PRE_GlobalRulesBrowserRestart) {
4955 // This is not tested for unpacked extensions since the unpacked extension
4956 // directory won't be persisted across browser restarts.
4957 ASSERT_EQ(ExtensionLoadType::PACKED, GetParam());
4958
4959 // Sanity check that the extension can index and enable up to
4960 // |rule_limit_override_| + |global_limit_override_| rules.
4961 ASSERT_EQ(3, GetMaximumRulesPerRuleset());
4962
4963 ASSERT_NO_FATAL_FAILURE(
4964 LoadExtensionWithRules({CreateGenericRule(1), CreateGenericRule(2)},
4965 "extension_1", {} /* hosts */));
4966 const ExtensionId first_extension_id = last_loaded_extension_id();
4967
4968 ASSERT_NO_FATAL_FAILURE(
4969 LoadExtensionWithRules({CreateGenericRule(1), CreateGenericRule(2)},
4970 "extension_2", {} /* hosts */));
4971 const ExtensionId second_extension_id = last_loaded_extension_id();
4972
4973 std::vector<TestRulesetInfo> rulesets;
4974 rulesets.emplace_back("ruleset_1", *ToListValue({CreateGenericRule(1)}));
4975 rulesets.emplace_back("ruleset_2", *ToListValue({CreateGenericRule(2)}));
4976
4977 ASSERT_NO_FATAL_FAILURE(
4978 LoadExtensionWithRulesets(rulesets, "extension_3", {} /* hosts */));
4979
4980 const Extension* third_extension = last_loaded_extension();
4981 CompositeMatcher* composite_matcher =
4982 ruleset_manager()->GetMatcherForExtension(third_extension->id());
4983
4984 ASSERT_TRUE(composite_matcher);
4985
4986 // Check that the third extension only has |ruleset_1| enabled.
4987 EXPECT_THAT(GetPublicRulesetIDs(*third_extension, *composite_matcher),
4988 UnorderedElementsAre("ruleset_1"));
4989
4990 // First and second extension should have 1 rule in the global pool, third
4991 // extension should have none because one of its rulesets is not enabled since
4992 // the global limit is reached.
4993 VerifyExtensionAllocationInPrefs(first_extension_id, 1);
4994 VerifyExtensionAllocationInPrefs(second_extension_id, 1);
4995 VerifyExtensionAllocationInPrefs(third_extension->id(), base::nullopt);
4996 }
4997
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestGlobalRulesBrowserTest_Packed,GlobalRulesBrowserRestart)4998 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestGlobalRulesBrowserTest_Packed,
4999 GlobalRulesBrowserRestart) {
5000 // This is not tested for unpacked extensions since the unpacked extension
5001 // directory won't be persisted across browser restarts.
5002 ASSERT_EQ(ExtensionLoadType::PACKED, GetParam());
5003
5004 // Sanity check that the extension can index and enable up to
5005 // |rule_limit_override| + |global_limit_override| rules.
5006 ASSERT_EQ(3, GetMaximumRulesPerRuleset());
5007
5008 const ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
5009 std::map<std::string, size_t> allocated_rule_counts;
5010 std::map<std::string, const Extension*> extensions_by_name;
5011 for (const auto& extension : extension_registry()->enabled_extensions()) {
5012 extensions_by_name[extension->name()] = extension.get();
5013
5014 size_t allocated_rule_count = 0;
5015 if (prefs->GetDNRAllocatedGlobalRuleCount(extension->id(),
5016 &allocated_rule_count)) {
5017 allocated_rule_counts[extension->name()] = allocated_rule_count;
5018 }
5019 }
5020
5021 // Check that all three extensions have the same rulesets enabled as before
5022 // the browser restart.
5023 VerifyPublicRulesetIds(extensions_by_name["extension_1"],
5024 {kDefaultRulesetID});
5025 VerifyPublicRulesetIds(extensions_by_name["extension_2"],
5026 {kDefaultRulesetID});
5027 VerifyPublicRulesetIds(extensions_by_name["extension_3"], {"ruleset_1"});
5028
5029 EXPECT_THAT(allocated_rule_counts,
5030 UnorderedElementsAre(std::make_pair("extension_1", 1),
5031 std::make_pair("extension_2", 1)));
5032 }
5033
5034 // Test that an extension will not have its allocation reclaimed on load if it
5035 // is the first load after a packed update.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestGlobalRulesBrowserTest_Packed,PackedUpdateAndReload)5036 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestGlobalRulesBrowserTest_Packed,
5037 PackedUpdateAndReload) {
5038 // This is not tested for unpacked extensions since the unpacked extension
5039 // directory won't be persisted across browser restarts.
5040 ASSERT_EQ(ExtensionLoadType::PACKED, GetParam());
5041
5042 // Load the extension with a background page so updateEnabledRulesets can be
5043 // called.
5044 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
5045
5046 std::vector<TestRulesetInfo> rulesets;
5047 rulesets.emplace_back(
5048 "ruleset_1", *ToListValue({CreateGenericRule(1), CreateGenericRule(2)}));
5049 rulesets.emplace_back("ruleset_2", *ToListValue({CreateGenericRule(3)}),
5050 false /* enabled */);
5051
5052 ASSERT_NO_FATAL_FAILURE(
5053 LoadExtensionWithRulesets(rulesets, "test_extension", {} /* hosts */));
5054
5055 // Check that the extension only has |ruleset_1| enabled.
5056 VerifyPublicRulesetIds(last_loaded_extension(), {"ruleset_1"});
5057
5058 // There should be one rule allocated.
5059 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), 1);
5060
5061 // Enable |ruleset_2| and verify the allocation.
5062 ASSERT_NO_FATAL_FAILURE(
5063 UpdateEnabledRulesets(last_loaded_extension_id(), {}, {"ruleset_2"}));
5064 VerifyPublicRulesetIds(last_loaded_extension(), {"ruleset_1", "ruleset_2"});
5065 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), 2);
5066
5067 // Update the extension. This should keep the allocated rule count in prefs
5068 // but not the set of enabled rulesets.
5069 UpdateLastLoadedExtension(rulesets, "test_extension2", {} /* hosts */,
5070 0 /* expected_extension_ruleset_count_change */,
5071 false /* has_dynamic_ruleset */);
5072
5073 VerifyPublicRulesetIds(last_loaded_extension(), {"ruleset_1"});
5074 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), 2);
5075
5076 // Disable then enable the extension to trigger another extension load.
5077 DisableExtension(last_loaded_extension_id());
5078 WaitForExtensionsWithRulesetsCount(0);
5079 EnableExtension(last_loaded_extension_id());
5080 WaitForExtensionsWithRulesetsCount(1);
5081
5082 // Since this is the second load after an update, the unused allocation should
5083 // be reclaimed.
5084 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), 1);
5085 }
5086
5087 // Tests that excess allocation is no longer kept after an update once the
5088 // current allocation exceeds the allocation from before the update.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestGlobalRulesBrowserTest_Packed,UpdateEnabledRulesetsAfterPackedUpdate)5089 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestGlobalRulesBrowserTest_Packed,
5090 UpdateEnabledRulesetsAfterPackedUpdate) {
5091 // This is not tested for unpacked extensions since the unpacked extension
5092 // directory won't be persisted across browser restarts.
5093 ASSERT_EQ(ExtensionLoadType::PACKED, GetParam());
5094
5095 // Load the extension with a background page so updateEnabledRulesets can be
5096 // called.
5097 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
5098
5099 std::vector<TestRulesetInfo> rulesets;
5100 rulesets.emplace_back("ruleset_1", *ToListValue({CreateGenericRule(1)}));
5101 rulesets.emplace_back("ruleset_2", *ToListValue({CreateGenericRule(2)}),
5102 false /* enabled */);
5103 rulesets.emplace_back(
5104 "big_ruleset",
5105 *ToListValue(
5106 {CreateGenericRule(3), CreateGenericRule(4), CreateGenericRule(5)}),
5107 false /* enabled */);
5108
5109 ASSERT_NO_FATAL_FAILURE(
5110 LoadExtensionWithRulesets(rulesets, "test_extension", {} /* hosts */));
5111
5112 // Enable |ruleset_2|.
5113 ASSERT_NO_FATAL_FAILURE(
5114 UpdateEnabledRulesets(last_loaded_extension_id(), {}, {"ruleset_2"}));
5115 VerifyPublicRulesetIds(last_loaded_extension(), {"ruleset_1", "ruleset_2"});
5116
5117 // There should be one rule allocated.
5118 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), 1);
5119
5120 // Update the extension. This should keep the allocated rule count in prefs
5121 // but not the set of enabled rulesets.
5122 UpdateLastLoadedExtension(rulesets, "test_extension2", {} /* hosts */,
5123 0 /* expected_extension_ruleset_count_change */,
5124 false /* has_dynamic_ruleset */);
5125
5126 VerifyPublicRulesetIds(last_loaded_extension(), {"ruleset_1"});
5127 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), 1);
5128 VerifyKeepExcessAllocation(last_loaded_extension_id(), true);
5129
5130 // Adding |big_ruleset| should fail right now, and should not change the
5131 // allocation.
5132 ASSERT_NO_FATAL_FAILURE(UpdateEnabledRulesetsAndFail(
5133 last_loaded_extension_id(), {}, {"big_ruleset"},
5134 kEnabledRulesetsRuleCountExceeded));
5135
5136 VerifyPublicRulesetIds(last_loaded_extension(), {"ruleset_1"});
5137 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), 1);
5138 VerifyKeepExcessAllocation(last_loaded_extension_id(), true);
5139
5140 // Removing |ruleset_1| should not change the allocation.
5141 ASSERT_NO_FATAL_FAILURE(
5142 UpdateEnabledRulesets(last_loaded_extension_id(), {"ruleset_1"}, {}));
5143 VerifyPublicRulesetIds(last_loaded_extension(), {});
5144 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), 1);
5145 VerifyKeepExcessAllocation(last_loaded_extension_id(), true);
5146
5147 // Adding |big_ruleset| should succeed now, and should increase the allocation
5148 // past the value from just before the update.
5149 ASSERT_NO_FATAL_FAILURE(
5150 UpdateEnabledRulesets(last_loaded_extension_id(), {}, {"big_ruleset"}));
5151 VerifyPublicRulesetIds(last_loaded_extension(), {"big_ruleset"});
5152 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), 2);
5153 VerifyKeepExcessAllocation(last_loaded_extension_id(), false);
5154
5155 // Removing |big_ruleset| should now drop the allocation down to 0 since the
5156 // excess allocation has been exceeded and no longer needs to be kept.
5157 ASSERT_NO_FATAL_FAILURE(
5158 UpdateEnabledRulesets(last_loaded_extension_id(), {"big_ruleset"}, {}));
5159 VerifyPublicRulesetIds(last_loaded_extension(), {});
5160 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), base::nullopt);
5161 VerifyKeepExcessAllocation(last_loaded_extension_id(), false);
5162 }
5163
5164 // Test that GetAvailableStaticRuleCount includes the excess unused allocation
5165 // after an extension update.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestGlobalRulesBrowserTest_Packed,GetAvailableStaticRuleCountAfterPackedUpdate)5166 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestGlobalRulesBrowserTest_Packed,
5167 GetAvailableStaticRuleCountAfterPackedUpdate) {
5168 // This is not tested for unpacked extensions since the unpacked extension
5169 // directory won't be persisted across browser restarts.
5170 ASSERT_EQ(ExtensionLoadType::PACKED, GetParam());
5171
5172 // Load the extension with a background page so updateEnabledRulesets can be
5173 // called.
5174 set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
5175
5176 std::vector<TestRulesetInfo> rulesets;
5177 rulesets.emplace_back("ruleset_1", *ToListValue({CreateGenericRule(1)}));
5178 rulesets.emplace_back("ruleset_2", *ToListValue({CreateGenericRule(2)}));
5179 rulesets.emplace_back("ruleset_3", *ToListValue({CreateGenericRule(3)}),
5180 false /* enabled */);
5181
5182 ASSERT_NO_FATAL_FAILURE(
5183 LoadExtensionWithRulesets(rulesets, "test_extension", {} /* hosts */));
5184
5185 // Enable |ruleset_3|.
5186 ASSERT_NO_FATAL_FAILURE(
5187 UpdateEnabledRulesets(last_loaded_extension_id(), {}, {"ruleset_3"}));
5188 VerifyPublicRulesetIds(last_loaded_extension(),
5189 {"ruleset_1", "ruleset_2", "ruleset_3"});
5190
5191 // There should be two rules allocated.
5192 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), 2);
5193
5194 // There should not be any more rules available.
5195 EXPECT_EQ("0", GetAvailableStaticRuleCount(last_loaded_extension_id()));
5196
5197 // Update the extension. This should keep the allocated rule count in prefs
5198 // but not the set of enabled rulesets.
5199 UpdateLastLoadedExtension(rulesets, "test_extension2", {} /* hosts */,
5200 0 /* expected_extension_ruleset_count_change */,
5201 false /* has_dynamic_ruleset */);
5202
5203 VerifyPublicRulesetIds(last_loaded_extension(), {"ruleset_1", "ruleset_2"});
5204 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), 2);
5205
5206 // Since there are two rules enabled, and one rule that's allocated but
5207 // unused, there should be one rule available.
5208 EXPECT_EQ("1", GetAvailableStaticRuleCount(last_loaded_extension_id()));
5209
5210 // Disabling |ruleset_1| and |ruleset_2| should not decrease the allocation,
5211 // but should increase the number of rules available.
5212 ASSERT_NO_FATAL_FAILURE(
5213 UpdateEnabledRulesets(last_loaded_extension_id(), {"ruleset_2"}, {}));
5214 VerifyPublicRulesetIds(last_loaded_extension(), {"ruleset_1"});
5215 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), 2);
5216 EXPECT_EQ("2", GetAvailableStaticRuleCount(last_loaded_extension_id()));
5217
5218 ASSERT_NO_FATAL_FAILURE(
5219 UpdateEnabledRulesets(last_loaded_extension_id(), {"ruleset_1"}, {}));
5220 VerifyPublicRulesetIds(last_loaded_extension(), {});
5221 VerifyExtensionAllocationInPrefs(last_loaded_extension_id(), 2);
5222 EXPECT_EQ("3", GetAvailableStaticRuleCount(last_loaded_extension_id()));
5223 }
5224
5225 INSTANTIATE_TEST_SUITE_P(All,
5226 DeclarativeNetRequestBrowserTest,
5227 ::testing::Values(ExtensionLoadType::PACKED,
5228 ExtensionLoadType::UNPACKED));
5229
5230 INSTANTIATE_TEST_SUITE_P(All,
5231 DeclarativeNetRequestIdentifiabilityTest,
5232 ::testing::Values(ExtensionLoadType::PACKED,
5233 ExtensionLoadType::UNPACKED));
5234
5235 INSTANTIATE_TEST_SUITE_P(All,
5236 DeclarativeNetRequestHostPermissionsBrowserTest,
5237 ::testing::Values(ExtensionLoadType::PACKED,
5238 ExtensionLoadType::UNPACKED));
5239 INSTANTIATE_TEST_SUITE_P(All,
5240 DeclarativeNetRequestResourceTypeBrowserTest,
5241 ::testing::Values(ExtensionLoadType::PACKED,
5242 ExtensionLoadType::UNPACKED));
5243 INSTANTIATE_TEST_SUITE_P(All,
5244 DeclarativeNetRequestSubresourceWebBundlesBrowserTest,
5245 ::testing::Values(ExtensionLoadType::PACKED,
5246 ExtensionLoadType::UNPACKED));
5247
5248 INSTANTIATE_TEST_SUITE_P(All,
5249 DeclarativeNetRequestBrowserTest_Packed,
5250 ::testing::Values(ExtensionLoadType::PACKED));
5251
5252 INSTANTIATE_TEST_SUITE_P(All,
5253 DeclarativeNetRequestBrowserTest_Unpacked,
5254 ::testing::Values(ExtensionLoadType::UNPACKED));
5255
5256 INSTANTIATE_TEST_SUITE_P(All,
5257 DeclarativeNetRequestGlobalRulesBrowserTest_Packed,
5258 ::testing::Values(ExtensionLoadType::PACKED));
5259
5260 } // namespace
5261 } // namespace declarative_net_request
5262 } // namespace extensions
5263