blob: dfb3f149836453a5c343f4bc26f50b621891005e [file] [log] [blame]
Javier Fernández García-Boente13150a042022-04-04 21:08:221// Copyright (c) 2022 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 <memory>
6#include <string>
7
8#include "base/scoped_observation.h"
9#include "base/strings/utf_string_conversions.h"
10#include "base/test/scoped_feature_list.h"
11#include "components/custom_handlers/protocol_handler.h"
12#include "components/custom_handlers/protocol_handler_registry.h"
13#include "components/custom_handlers/simple_protocol_handler_registry_factory.h"
14#include "content/public/browser/navigation_controller.h"
15#include "content/public/browser/navigation_entry.h"
16#include "content/public/browser/web_contents.h"
17#include "content/public/common/content_client.h"
18#include "content/public/common/content_features.h"
19#include "content/public/test/browser_test.h"
20#include "content/public/test/browser_test_utils.h"
21#include "content/public/test/content_browser_test.h"
22#include "content/public/test/content_browser_test_utils.h"
23#include "content/public/test/fenced_frame_test_util.h"
24#include "content/shell/browser/shell.h"
25#include "net/test/embedded_test_server/embedded_test_server.h"
26
27using content::WebContents;
28
29namespace {
30
31using custom_handlers::ProtocolHandlerRegistry;
32
33class ProtocolHandlerChangeWaiter : public ProtocolHandlerRegistry::Observer {
34 public:
35 explicit ProtocolHandlerChangeWaiter(ProtocolHandlerRegistry* registry) {
36 registry_observation_.Observe(registry);
37 }
38 ProtocolHandlerChangeWaiter(const ProtocolHandlerChangeWaiter&) = delete;
39 ProtocolHandlerChangeWaiter& operator=(const ProtocolHandlerChangeWaiter&) =
40 delete;
41 ~ProtocolHandlerChangeWaiter() override = default;
42
43 void Wait() { run_loop_.Run(); }
44 // ProtocolHandlerRegistry::Observer:
45 void OnProtocolHandlerRegistryChanged() override { run_loop_.Quit(); }
46
47 private:
48 base::ScopedObservation<custom_handlers::ProtocolHandlerRegistry,
49 custom_handlers::ProtocolHandlerRegistry::Observer>
50 registry_observation_{this};
51 base::RunLoop run_loop_;
52};
53
54} // namespace
55
56namespace custom_handlers {
57
58class RegisterProtocolHandlerBrowserTest : public content::ContentBrowserTest {
59 public:
60 RegisterProtocolHandlerBrowserTest() = default;
61
62 void SetUpOnMainThread() override {
63 embedded_test_server()->ServeFilesFromSourceDirectory(
64 "components/test/data/");
65 }
66
67 void AddProtocolHandler(const std::string& protocol, const GURL& url) {
68 ProtocolHandler handler =
69 ProtocolHandler::CreateProtocolHandler(protocol, url);
70 ProtocolHandlerRegistry* registry =
71 SimpleProtocolHandlerRegistryFactory::GetForBrowserContext(
72 browser_context(), true);
73 // Fake that this registration is happening on profile startup. Otherwise
74 // it'll try to register with the OS, which causes DCHECKs on Windows when
75 // running as admin on Windows 7.
76 registry->SetIsLoading(true);
77 registry->OnAcceptRegisterProtocolHandler(handler);
78 registry->SetIsLoading(true);
79 ASSERT_TRUE(registry->IsHandledProtocol(protocol));
80 }
81
82 void RemoveProtocolHandler(const std::string& protocol, const GURL& url) {
83 ProtocolHandler handler =
84 ProtocolHandler::CreateProtocolHandler(protocol, url);
85 ProtocolHandlerRegistry* registry =
86 SimpleProtocolHandlerRegistryFactory::GetForBrowserContext(
87 browser_context(), true);
88 registry->RemoveHandler(handler);
89 ASSERT_FALSE(registry->IsHandledProtocol(protocol));
90 }
91
92 content::WebContents* web_contents() { return shell()->web_contents(); }
93 content::BrowserContext* browser_context() {
94 return web_contents()->GetBrowserContext();
95 }
96
97 protected:
98 content::test::FencedFrameTestHelper& fenced_frame_test_helper() {
99 return fenced_frame_helper_;
100 }
101
102 private:
103 content::test::FencedFrameTestHelper fenced_frame_helper_;
104};
105
106IN_PROC_BROWSER_TEST_F(RegisterProtocolHandlerBrowserTest, CustomHandler) {
107 ASSERT_TRUE(embedded_test_server()->Start());
108 GURL handler_url = embedded_test_server()->GetURL("/custom_handler.html");
109 AddProtocolHandler("news", handler_url);
110
111 ASSERT_TRUE(NavigateToURL(shell(), GURL("news:test"), handler_url));
112
113 ASSERT_EQ(handler_url, web_contents()->GetLastCommittedURL());
114
115 // Also check redirects.
116 GURL redirect_url =
117 embedded_test_server()->GetURL("/server-redirect?news:test");
118 ASSERT_TRUE(NavigateToURL(shell(), redirect_url, handler_url));
119
120 ASSERT_EQ(handler_url, web_contents()->GetLastCommittedURL());
121}
122
123// https://crbug.com/178097: Implement registerProtocolHandler on Android
124#if !BUILDFLAG(IS_ANDROID)
125// FencedFrames can not register to handle any protocols.
126IN_PROC_BROWSER_TEST_F(RegisterProtocolHandlerBrowserTest, FencedFrame) {
127 ASSERT_TRUE(embedded_test_server()->Start());
128 ASSERT_TRUE(
129 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
130
131 // Create a FencedFrame.
132 content::RenderFrameHost* fenced_frame_host =
133 fenced_frame_test_helper().CreateFencedFrame(
134 web_contents()->GetMainFrame(),
135 embedded_test_server()->GetURL("/fenced_frames/title1.html"));
136 ASSERT_TRUE(fenced_frame_host);
137
138 // Ensure the registry is currently empty.
139 GURL url("web+search:testing");
140 ProtocolHandlerRegistry* registry =
141 SimpleProtocolHandlerRegistryFactory::GetForBrowserContext(
142 browser_context(), true);
143 ASSERT_EQ(0u, registry->GetHandlersFor(url.scheme()).size());
144
145 // Attempt to add an entry.
146 ProtocolHandlerChangeWaiter waiter(registry);
147 ASSERT_TRUE(content::ExecuteScript(fenced_frame_host,
148 "navigator.registerProtocolHandler('web+"
149 "search', 'test.html?%s', 'test');"));
150 waiter.Wait();
151
152 // Ensure the registry is still empty.
153 ASSERT_EQ(0u, registry->GetHandlersFor(url.scheme()).size());
154}
155#endif
156
157// https://crbug.com/178097: Implement registerProtocolHandler on Android
158#if !BUILDFLAG(IS_ANDROID)
159class RegisterProtocolHandlerAndServiceWorkerInterceptor
160 : public RegisterProtocolHandlerBrowserTest {
161 public:
162 void SetUpOnMainThread() override {
163 RegisterProtocolHandlerBrowserTest::SetUpOnMainThread();
164
165 ASSERT_TRUE(embedded_test_server()->Start());
166
167 // Navigate to the test page.
168 ASSERT_TRUE(NavigateToURL(
169 shell(), embedded_test_server()->GetURL(
170 "/protocol_handler/service_workers/"
171 "test_protocol_handler_and_service_workers.html")));
172 }
173};
174
175// TODO(crbug.com/1204127): Fix flakiness.
176IN_PROC_BROWSER_TEST_F(RegisterProtocolHandlerAndServiceWorkerInterceptor,
177 DISABLED_RegisterFetchListenerForHTMLHandler) {
178 // Register a service worker intercepting requests to the HTML handler.
179 EXPECT_EQ(true,
180 content::EvalJs(shell(), "registerFetchListenerForHTMLHandler();"));
181
182 {
183 // Register a HTML handler with a user gesture.
184 ProtocolHandlerRegistry* registry =
185 SimpleProtocolHandlerRegistryFactory::GetForBrowserContext(
186 browser_context(), true);
187 ProtocolHandlerChangeWaiter waiter(registry);
188 ASSERT_TRUE(content::ExecJs(shell(), "registerHTMLHandler();"));
189 waiter.Wait();
190 }
191
192 // Verify that a page with the registered scheme is managed by the service
193 // worker, not the HTML handler.
194 EXPECT_EQ(true,
195 content::EvalJs(shell(),
196 "pageWithCustomSchemeHandledByServiceWorker();"));
197}
198#endif
199
200} // namespace custom_handlers