1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chrome_content_browser_client.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/macros.h"
13 #include "base/no_destructor.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/test/scoped_feature_list.h"
17 #include "build/build_config.h"
18 #include "chrome/browser/custom_handlers/protocol_handler_registry.h"
19 #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/search/instant_service.h"
22 #include "chrome/browser/search/instant_service_factory.h"
23 #include "chrome/browser/search/search.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/search/instant_test_base.h"
26 #include "chrome/browser/ui/tabs/tab_strip_model.h"
27 #include "chrome/browser/ui/ui_features.h"
28 #include "chrome/common/chrome_features.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/pref_names.h"
31 #include "chrome/common/url_constants.h"
32 #include "chrome/test/base/in_process_browser_test.h"
33 #include "chrome/test/base/ui_test_utils.h"
34 #include "components/network_session_configurator/common/network_switches.h"
35 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
36 #include "components/prefs/pref_service.h"
37 #include "components/site_isolation/site_isolation_policy.h"
38 #include "content/public/browser/navigation_controller.h"
39 #include "content/public/browser/navigation_entry.h"
40 #include "content/public/browser/render_frame_host.h"
41 #include "content/public/browser/render_process_host.h"
42 #include "content/public/browser/render_view_host.h"
43 #include "content/public/browser/web_contents.h"
44 #include "content/public/common/content_client.h"
45 #include "content/public/common/content_switches.h"
46 #include "content/public/test/browser_test.h"
47 #include "content/public/test/browser_test_utils.h"
48 #include "content/public/test/test_navigation_observer.h"
49 #include "net/dns/mock_host_resolver.h"
50 #include "net/http/http_status_code.h"
51 #include "net/test/embedded_test_server/embedded_test_server.h"
52 #include "net/test/embedded_test_server/http_request.h"
53 #include "net/test/embedded_test_server/http_response.h"
54 #include "ui/native_theme/native_theme.h"
55 #include "ui/native_theme/test_native_theme.h"
57 #include "url/origin.h"
59 #if BUILDFLAG(ENABLE_EXTENSIONS)
60 #include "extensions/common/constants.h"
61 #include "extensions/common/extension_urls.h"
62 #include "url/url_constants.h"
65 #if defined(OS_MACOSX)
66 #include "chrome/test/base/launchservices_utils_mac.h"
73 enum class NetworkServiceState {
80 // Use a test class with SetUpCommandLine to ensure the flag is sent to the
81 // first renderer process.
82 class ChromeContentBrowserClientBrowserTest : public InProcessBrowserTest {
84 ChromeContentBrowserClientBrowserTest() {}
86 void SetUpCommandLine(base::CommandLine* command_line) override {
87 IsolateAllSitesForTesting(command_line);
91 DISALLOW_COPY_AND_ASSIGN(ChromeContentBrowserClientBrowserTest);
94 // Test that a basic navigation works in --site-per-process mode. This prevents
95 // regressions when that mode calls out into the ChromeContentBrowserClient,
96 // such as http://crbug.com/164223.
97 IN_PROC_BROWSER_TEST_F(ChromeContentBrowserClientBrowserTest,
98 SitePerProcessNavigation) {
99 ASSERT_TRUE(embedded_test_server()->Start());
100 const GURL url(embedded_test_server()->GetURL("/title1.html"));
102 ui_test_utils::NavigateToURL(browser(), url);
103 NavigationEntry* entry = browser()
105 ->GetWebContentsAt(0)
107 .GetLastCommittedEntry();
109 ASSERT_TRUE(entry != NULL);
110 EXPECT_EQ(url, entry->GetURL());
111 EXPECT_EQ(url, entry->GetVirtualURL());
114 class ChromeContentBrowserClientPopupsTest : public InProcessBrowserTest {
116 void SetUpCommandLine(base::CommandLine* command_line) override {
117 // Required setup for kAllowPopupsDuringPageUnload switch
118 // as its being checked (whether its going to be enabled or not)
119 // only if the process type is renderer process.
120 command_line_.AppendSwitchASCII(switches::kProcessType,
121 switches::kRendererProcess);
123 void SetUpOnMainThread() override {
124 kChildProcessId = browser()
126 ->GetActiveWebContents()
131 ChromeContentBrowserClientPopupsTest()
132 : command_line_(base::CommandLine::NO_PROGRAM) {}
134 void AppendContentBrowserClientSwitches() {
135 client_.AppendExtraCommandLineSwitches(&command_line_, kChildProcessId);
138 const base::CommandLine& command_line() const { return command_line_; }
141 ChromeContentBrowserClient client_;
142 base::CommandLine command_line_;
146 IN_PROC_BROWSER_TEST_F(ChromeContentBrowserClientPopupsTest,
147 AllowPopupsDuringPageUnload) {
148 // Verify that the switch is included only when the
149 // pref AllowPopupsDuringPageUnload value is true.
151 PrefService* pref_service = browser()->profile()->GetPrefs();
152 pref_service->SetBoolean(prefs::kAllowPopupsDuringPageUnload, false);
153 AppendContentBrowserClientSwitches();
155 command_line().HasSwitch(switches::kAllowPopupsDuringPageUnload));
156 // When the pref value is being set to true
157 // the switch should be included.
158 pref_service->SetBoolean(prefs::kAllowPopupsDuringPageUnload, true);
159 AppendContentBrowserClientSwitches();
160 EXPECT_TRUE(command_line().HasSwitch(switches::kAllowPopupsDuringPageUnload));
163 // Helper class to mark "https://ntp.com/" as an isolated origin.
164 class IsolatedOriginNTPBrowserTest : public InProcessBrowserTest,
165 public InstantTestBase {
167 IsolatedOriginNTPBrowserTest() {}
169 void SetUpCommandLine(base::CommandLine* command_line) override {
170 ASSERT_TRUE(https_test_server().InitializeAndListen());
172 // Mark ntp.com (with an appropriate port from the test server) as an
174 GURL isolated_url(https_test_server().GetURL("ntp.com", "/"));
175 command_line->AppendSwitchASCII(switches::kIsolateOrigins,
176 isolated_url.spec());
177 command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
180 void SetUpOnMainThread() override {
181 InProcessBrowserTest::SetUpOnMainThread();
182 host_resolver()->AddRule("*", "127.0.0.1");
183 https_test_server().StartAcceptingConnections();
187 DISALLOW_COPY_AND_ASSIGN(IsolatedOriginNTPBrowserTest);
190 // Verifies that when the remote NTP URL has an origin which is also marked as
191 // an isolated origin (i.e., requiring a dedicated process), the NTP URL
192 // still loads successfully, and the resulting process is marked as an Instant
193 // process. See https://crbug.com/755595.
194 IN_PROC_BROWSER_TEST_F(IsolatedOriginNTPBrowserTest,
195 IsolatedOriginDoesNotInterfereWithNTP) {
197 https_test_server().GetURL("ntp.com", "/instant_extended.html");
199 https_test_server().GetURL("ntp.com", "/instant_extended_ntp.html");
200 InstantTestBase::Init(base_url, ntp_url, false);
202 SetupInstant(browser());
204 // Sanity check that a SiteInstance for a generic ntp.com URL requires a
205 // dedicated process.
206 content::BrowserContext* context = browser()->profile();
207 GURL isolated_url(https_test_server().GetURL("ntp.com", "/title1.html"));
208 scoped_refptr<SiteInstance> site_instance =
209 SiteInstance::CreateForURL(context, isolated_url);
210 EXPECT_TRUE(site_instance->RequiresDedicatedProcess());
212 // The site URL for the NTP URL should resolve to a chrome-search:// URL via
213 // GetEffectiveURL(), even if the NTP URL matches an isolated origin.
214 GURL site_url(content::SiteInstance::GetSiteForURL(context, ntp_url));
215 EXPECT_TRUE(site_url.SchemeIs(chrome::kChromeSearchScheme));
217 // Navigate to the NTP URL and verify that the resulting process is marked as
218 // an Instant process.
219 ui_test_utils::NavigateToURL(browser(), ntp_url);
220 content::WebContents* contents =
221 browser()->tab_strip_model()->GetActiveWebContents();
222 InstantService* instant_service =
223 InstantServiceFactory::GetForProfile(browser()->profile());
224 EXPECT_TRUE(instant_service->IsInstantProcess(
225 contents->GetMainFrame()->GetProcess()->GetID()));
227 // Navigating to a non-NTP URL on ntp.com should not result in an Instant
229 ui_test_utils::NavigateToURL(browser(), isolated_url);
230 EXPECT_FALSE(instant_service->IsInstantProcess(
231 contents->GetMainFrame()->GetProcess()->GetID()));
234 // Helper class to test window creation from NTP.
235 class OpenWindowFromNTPBrowserTest : public InProcessBrowserTest,
236 public InstantTestBase {
238 OpenWindowFromNTPBrowserTest() {}
240 void SetUpCommandLine(base::CommandLine* command_line) override {
241 command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
244 void SetUpOnMainThread() override {
245 InProcessBrowserTest::SetUpOnMainThread();
246 host_resolver()->AddRule("*", "127.0.0.1");
247 ASSERT_TRUE(https_test_server().InitializeAndListen());
248 https_test_server().StartAcceptingConnections();
252 DISALLOW_COPY_AND_ASSIGN(OpenWindowFromNTPBrowserTest);
255 // Test checks that navigations from NTP tab to URLs with same host as NTP but
256 // different path do not reuse NTP SiteInstance. See https://crbug.com/859062
258 IN_PROC_BROWSER_TEST_F(OpenWindowFromNTPBrowserTest,
259 TransferFromNTPCreateNewTab) {
261 https_test_server().GetURL("ntp.com", "/instant_extended.html");
263 https_test_server().GetURL("ntp.com", "/instant_extended_ntp.html");
264 InstantTestBase::Init(search_url, ntp_url, false);
266 SetupInstant(browser());
268 // Navigate to the NTP URL and verify that the resulting process is marked as
269 // an Instant process.
270 ui_test_utils::NavigateToURL(browser(), ntp_url);
271 content::WebContents* ntp_tab =
272 browser()->tab_strip_model()->GetActiveWebContents();
273 InstantService* instant_service =
274 InstantServiceFactory::GetForProfile(browser()->profile());
275 EXPECT_TRUE(instant_service->IsInstantProcess(
276 ntp_tab->GetMainFrame()->GetProcess()->GetID()));
278 // Execute script that creates new window from ntp tab with
279 // ntp.com/title1.html as target url. Host is same as remote-ntp host, yet
280 // path is different.
281 GURL generic_url(https_test_server().GetURL("ntp.com", "/title1.html"));
282 content::TestNavigationObserver opened_tab_observer(nullptr);
283 opened_tab_observer.StartWatchingNewWebContents();
285 ExecuteScript(ntp_tab, "window.open('" + generic_url.spec() + "');"));
286 opened_tab_observer.Wait();
287 ASSERT_EQ(2, browser()->tab_strip_model()->count());
289 content::WebContents* opened_tab =
290 browser()->tab_strip_model()->GetActiveWebContents();
292 // Wait until newly opened tab is fully loaded.
293 EXPECT_TRUE(WaitForLoadStop(opened_tab));
295 EXPECT_NE(opened_tab, ntp_tab);
296 EXPECT_EQ(generic_url, opened_tab->GetLastCommittedURL());
297 // New created tab should not reside in an Instant process.
298 EXPECT_FALSE(instant_service->IsInstantProcess(
299 opened_tab->GetMainFrame()->GetProcess()->GetID()));
302 class PrefersColorSchemeTest : public testing::WithParamInterface<bool>,
303 public InProcessBrowserTest {
305 PrefersColorSchemeTest() : theme_client_(&test_theme_) {
306 feature_list_.InitWithFeatureState(features::kWebUIDarkMode, GetParam());
309 ~PrefersColorSchemeTest() override {
310 CHECK_EQ(&theme_client_, SetBrowserClientForTesting(original_client_));
313 const char* ExpectedColorScheme() const {
314 return GetParam() ? "dark" : "light";
317 void SetUpCommandLine(base::CommandLine* command_line) override {
318 InProcessBrowserTest::SetUpCommandLine(command_line);
319 command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
320 "MediaQueryPrefersColorScheme");
323 void SetUpOnMainThread() override {
324 InProcessBrowserTest::SetUpOnMainThread();
325 original_client_ = SetBrowserClientForTesting(&theme_client_);
329 ui::TestNativeTheme test_theme_;
332 content::ContentBrowserClient* original_client_ = nullptr;
334 class ChromeContentBrowserClientWithWebTheme
335 : public ChromeContentBrowserClient {
337 explicit ChromeContentBrowserClientWithWebTheme(
338 const ui::NativeTheme* theme)
342 const ui::NativeTheme* GetWebTheme() const override { return theme_; }
345 const ui::NativeTheme* const theme_;
348 base::test::ScopedFeatureList feature_list_;
349 ChromeContentBrowserClientWithWebTheme theme_client_;
352 IN_PROC_BROWSER_TEST_P(PrefersColorSchemeTest, PrefersColorScheme) {
353 test_theme_.SetDarkMode(GetParam());
356 ->GetActiveWebContents()
357 ->GetRenderViewHost()
358 ->OnWebkitPreferencesChanged();
359 ui_test_utils::NavigateToURL(
361 ui_test_utils::GetTestUrl(
362 base::FilePath(base::FilePath::kCurrentDirectory),
363 base::FilePath(FILE_PATH_LITERAL("prefers-color-scheme.html"))));
364 base::string16 tab_title;
365 ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(), &tab_title));
366 EXPECT_EQ(base::ASCIIToUTF16(ExpectedColorScheme()), tab_title);
369 IN_PROC_BROWSER_TEST_P(PrefersColorSchemeTest, FeatureOverridesChromeSchemes) {
370 test_theme_.SetDarkMode(true);
372 ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIDownloadsURL));
375 ASSERT_TRUE(ExecuteScriptAndExtractBool(
376 browser()->tab_strip_model()->GetActiveWebContents(),
377 base::StringPrintf("window.domAutomationController.send(window."
378 "matchMedia('(prefers-color-scheme: %s)').matches)",
379 ExpectedColorScheme()),
381 EXPECT_TRUE(matches);
384 #if BUILDFLAG(ENABLE_EXTENSIONS)
385 IN_PROC_BROWSER_TEST_P(PrefersColorSchemeTest, FeatureOverridesPdfUI) {
386 test_theme_.SetDarkMode(true);
388 std::string pdf_extension_url(extensions::kExtensionScheme);
389 pdf_extension_url.append(url::kStandardSchemeSeparator);
390 pdf_extension_url.append(extension_misc::kPdfExtensionId);
391 GURL pdf_index = GURL(pdf_extension_url).Resolve("/index.html");
392 ui_test_utils::NavigateToURL(browser(), pdf_index);
395 ASSERT_TRUE(ExecuteScriptAndExtractBool(
396 browser()->tab_strip_model()->GetActiveWebContents(),
397 base::StringPrintf("window.domAutomationController.send(window."
398 "matchMedia('(prefers-color-scheme: %s)').matches)",
399 ExpectedColorScheme()),
401 EXPECT_TRUE(matches);
405 INSTANTIATE_TEST_SUITE_P(All, PrefersColorSchemeTest, testing::Bool());
407 class ProtocolHandlerTest : public InProcessBrowserTest {
409 ProtocolHandlerTest() = default;
411 void SetUpOnMainThread() override {
412 InProcessBrowserTest::SetUpOnMainThread();
413 host_resolver()->AddRule("*", "127.0.0.1");
414 ASSERT_TRUE(embedded_test_server()->Start());
418 void AddProtocolHandler(const std::string& scheme,
419 const std::string& redirect_template) {
420 protocol_handler_registry()->OnAcceptRegisterProtocolHandler(
421 ProtocolHandler::CreateProtocolHandler(scheme,
422 GURL(redirect_template)));
425 ProtocolHandlerRegistry* protocol_handler_registry() {
426 return ProtocolHandlerRegistryFactory::GetInstance()->GetForBrowserContext(
427 browser()->profile());
431 DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerTest);
434 IN_PROC_BROWSER_TEST_F(ProtocolHandlerTest, CustomHandler) {
435 #if defined(OS_MACOSX)
436 ASSERT_TRUE(test::RegisterAppWithLaunchServices());
438 AddProtocolHandler("news", "https://abc.xyz/?url=%s");
440 ui_test_utils::NavigateToURL(browser(), GURL("news:something"));
442 base::string16 expected_title = base::ASCIIToUTF16("abc.xyz");
443 content::TitleWatcher title_watcher(
444 browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
445 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
448 // This is a regression test for crbug.com/969177.
449 IN_PROC_BROWSER_TEST_F(ProtocolHandlerTest, HandlersIgnoredWhenDisabled) {
450 AddProtocolHandler("bitcoin", "https://abc.xyz/?url=%s");
451 protocol_handler_registry()->Disable();
453 ui_test_utils::NavigateToURL(browser(), GURL("bitcoin:something"));
455 base::string16 tab_title;
456 ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(), &tab_title));
457 EXPECT_EQ(base::ASCIIToUTF16("about:blank"), tab_title);
460 #if defined(OS_CHROMEOS)
461 // Tests that if a protocol handler is registered for a scheme, an external
462 // program (another Chrome tab in this case) is not launched to handle the
463 // navigation. This is a regression test for crbug.com/963133.
464 IN_PROC_BROWSER_TEST_F(ProtocolHandlerTest, ExternalProgramNotLaunched) {
465 ui_test_utils::NavigateToURL(browser(), GURL("mailto:bob@example.com"));
467 // If an external program (Chrome) was launched, it will result in a second
469 EXPECT_EQ(1, browser()->tab_strip_model()->count());
471 // Make sure the protocol handler redirected the navigation.
472 base::string16 expected_title = base::ASCIIToUTF16("mail.google.com");
473 content::TitleWatcher title_watcher(
474 browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
475 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
479 } // namespace content