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