blob: 18794c13b2c934300c990ae3fde9f380d6332efd [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2022 The Chromium Authors
Javier Fernández García-Boente13150a042022-04-04 21:08:222// 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)
Javier Fernández García-Boentea2b1f18b2022-04-11 22:05:53125IN_PROC_BROWSER_TEST_F(RegisterProtocolHandlerBrowserTest,
126 IgnoreRequestWithoutUserGesture) {
127 ASSERT_TRUE(embedded_test_server()->Start());
128 ASSERT_TRUE(
129 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
130
131 // Ensure the registry is currently empty.
132 GURL url("web+search:testing");
133 ProtocolHandlerRegistry* registry =
134 SimpleProtocolHandlerRegistryFactory::GetForBrowserContext(
135 browser_context(), true);
136 ASSERT_EQ(0u, registry->GetHandlersFor(url.scheme()).size());
137
138 // Attempt to add an entry.
139 ProtocolHandlerChangeWaiter waiter(registry);
140 ASSERT_TRUE(content::ExecuteScriptWithoutUserGesture(
141 web_contents(),
142 "navigator.registerProtocolHandler('web+"
143 "search', 'test.html?%s', 'test');"));
144 waiter.Wait();
145
146 // Verify the registration is ignored if no user gesture involved.
147 ASSERT_EQ(1u, registry->GetHandlersFor(url.scheme()).size());
148 ASSERT_FALSE(registry->IsHandledProtocol(url.scheme()));
149}
150
Javier Fernández García-Boente13150a042022-04-04 21:08:22151// FencedFrames can not register to handle any protocols.
152IN_PROC_BROWSER_TEST_F(RegisterProtocolHandlerBrowserTest, FencedFrame) {
153 ASSERT_TRUE(embedded_test_server()->Start());
154 ASSERT_TRUE(
155 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
156
157 // Create a FencedFrame.
158 content::RenderFrameHost* fenced_frame_host =
159 fenced_frame_test_helper().CreateFencedFrame(
Dave Tapuska88d7b2e72022-06-07 21:00:51160 web_contents()->GetPrimaryMainFrame(),
Javier Fernández García-Boente13150a042022-04-04 21:08:22161 embedded_test_server()->GetURL("/fenced_frames/title1.html"));
162 ASSERT_TRUE(fenced_frame_host);
163
164 // Ensure the registry is currently empty.
165 GURL url("web+search:testing");
166 ProtocolHandlerRegistry* registry =
167 SimpleProtocolHandlerRegistryFactory::GetForBrowserContext(
168 browser_context(), true);
169 ASSERT_EQ(0u, registry->GetHandlersFor(url.scheme()).size());
170
171 // Attempt to add an entry.
172 ProtocolHandlerChangeWaiter waiter(registry);
173 ASSERT_TRUE(content::ExecuteScript(fenced_frame_host,
174 "navigator.registerProtocolHandler('web+"
175 "search', 'test.html?%s', 'test');"));
176 waiter.Wait();
177
178 // Ensure the registry is still empty.
179 ASSERT_EQ(0u, registry->GetHandlersFor(url.scheme()).size());
180}
181#endif
182
183// https://crbug.com/178097: Implement registerProtocolHandler on Android
184#if !BUILDFLAG(IS_ANDROID)
185class RegisterProtocolHandlerAndServiceWorkerInterceptor
186 : public RegisterProtocolHandlerBrowserTest {
187 public:
188 void SetUpOnMainThread() override {
189 RegisterProtocolHandlerBrowserTest::SetUpOnMainThread();
190
191 ASSERT_TRUE(embedded_test_server()->Start());
192
193 // Navigate to the test page.
194 ASSERT_TRUE(NavigateToURL(
195 shell(), embedded_test_server()->GetURL(
196 "/protocol_handler/service_workers/"
197 "test_protocol_handler_and_service_workers.html")));
198 }
199};
200
201// TODO(crbug.com/1204127): Fix flakiness.
202IN_PROC_BROWSER_TEST_F(RegisterProtocolHandlerAndServiceWorkerInterceptor,
203 DISABLED_RegisterFetchListenerForHTMLHandler) {
204 // Register a service worker intercepting requests to the HTML handler.
205 EXPECT_EQ(true,
206 content::EvalJs(shell(), "registerFetchListenerForHTMLHandler();"));
207
208 {
209 // Register a HTML handler with a user gesture.
210 ProtocolHandlerRegistry* registry =
211 SimpleProtocolHandlerRegistryFactory::GetForBrowserContext(
212 browser_context(), true);
213 ProtocolHandlerChangeWaiter waiter(registry);
214 ASSERT_TRUE(content::ExecJs(shell(), "registerHTMLHandler();"));
215 waiter.Wait();
216 }
217
218 // Verify that a page with the registered scheme is managed by the service
219 // worker, not the HTML handler.
220 EXPECT_EQ(true,
221 content::EvalJs(shell(),
222 "pageWithCustomSchemeHandledByServiceWorker();"));
223}
224#endif
225
226} // namespace custom_handlers