protocol_handler_registry_browsertest.cc 8.3 KB

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