| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/base64.h" |
| #include "base/command_line.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/macros.h" |
| #include "base/path_service.h" |
| #include "base/run_loop.h" |
| #include "base/strings/pattern.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "build/build_config.h" |
| #include "build/buildflag.h" |
| #include "content/browser/site_per_process_browsertest.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/browser_side_navigation_policy.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_paths.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/content_browser_test.h" |
| #include "content/public/test/content_browser_test_utils.h" |
| #include "content/public/test/download_test_observer.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "content/shell/browser/shell.h" |
| #include "content/shell/browser/shell_download_manager_delegate.h" |
| #include "net/base/escape.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| #include "content/public/browser/plugin_service.h" |
| #include "content/public/common/webplugininfo.h" |
| #endif |
| |
| namespace content { |
| |
| namespace { |
| |
| // The pattern to catch messages printed by the browser when navigation to a |
| // URL is blocked. |
| const char kNavigationBlockedMessage[] = |
| "Not allowed to navigate top frame to %s URL:*"; |
| |
| // The message printed by the data or filesystem URL when it successfully |
| // navigates. |
| const char kNavigationSuccessfulMessage[] = "NAVIGATION_SUCCESSFUL"; |
| |
| // A "Hello World" pdf. |
| const char kPDF[] = |
| "%PDF-1.7\n" |
| "1 0 obj << /Type /Page /Parent 3 0 R /Resources 5 0 R /Contents 2 0 R >>\n" |
| "endobj\n" |
| "2 0 obj << /Length 51 >>\n" |
| " stream BT\n" |
| " /F1 12 Tf\n" |
| " 1 0 0 1 100 20 Tm\n" |
| " (Hello World)Tj\n" |
| " ET\n" |
| " endstream\n" |
| "endobj\n" |
| "3 0 obj << /Type /Pages /Kids [ 1 0 R ] /Count 1 /MediaBox [ 0 0 300 50] " |
| ">>\n" |
| "endobj\n" |
| "4 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont/Arial >>\n" |
| "endobj\n" |
| "5 0 obj << /ProcSet[/PDF/Text] /Font <</F1 4 0 R >> >>\n" |
| "endobj\n" |
| "6 0 obj << /Type /Catalog /Pages 3 0 R >>\n" |
| "endobj\n" |
| "trailer << /Root 6 0 R >>\n"; |
| |
| enum ExpectedNavigationStatus { NAVIGATION_BLOCKED, NAVIGATION_ALLOWED }; |
| |
| // This class is similar to ConsoleObserverDelegate in that it listens and waits |
| // for specific console messages. The difference from ConsoleObserverDelegate is |
| // that this class immediately stops waiting if it sees a message matching |
| // fail_pattern, instead of waiting for a message matching success_pattern. |
| class BlockedURLWarningConsoleObserverDelegate : public WebContentsDelegate { |
| public: |
| enum Status { |
| NO_MESSAGE, |
| SAW_SUCCESS_MESSAGE, |
| SAW_FAILURE_MESSAGE, |
| }; |
| BlockedURLWarningConsoleObserverDelegate(WebContents* web_contents, |
| const std::string& success_filter, |
| const std::string& fail_filter) |
| : web_contents_(web_contents), |
| success_filter_(success_filter), |
| fail_filter_(fail_filter), |
| status_(NO_MESSAGE) {} |
| |
| ~BlockedURLWarningConsoleObserverDelegate() override {} |
| |
| // WebContentsDelegate method: |
| bool DidAddMessageToConsole(WebContents* source, |
| int32_t level, |
| const base::string16& message, |
| int32_t line_no, |
| const base::string16& source_id) override { |
| DCHECK(source == web_contents_); |
| const std::string ascii_message = base::UTF16ToASCII(message); |
| if (base::MatchPattern(ascii_message, fail_filter_)) { |
| status_ = SAW_FAILURE_MESSAGE; |
| run_loop_.Quit(); |
| } |
| if (base::MatchPattern(ascii_message, success_filter_)) { |
| status_ = SAW_SUCCESS_MESSAGE; |
| run_loop_.Quit(); |
| } |
| return false; |
| } |
| |
| void Wait() { run_loop_.Run(); } |
| |
| Status status() const { return status_; } |
| |
| private: |
| WebContents* web_contents_; |
| const std::string success_filter_; |
| const std::string fail_filter_; |
| base::RunLoop run_loop_; |
| Status status_; |
| }; |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| // This class registers a fake PDF plugin handler so that navigations with a PDF |
| // mime type end up with a navigation and don't simply download the file. |
| class ScopedPluginRegister { |
| public: |
| ScopedPluginRegister(content::PluginService* plugin_service) |
| : plugin_service_(plugin_service) { |
| const char kPluginName[] = "PDF"; |
| const char kPdfMimeType[] = "application/pdf"; |
| const char kPdfFileType[] = "pdf"; |
| WebPluginInfo plugin_info; |
| plugin_info.type = WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS; |
| plugin_info.name = base::ASCIIToUTF16(kPluginName); |
| plugin_info.mime_types.push_back( |
| WebPluginMimeType(kPdfMimeType, kPdfFileType, std::string())); |
| plugin_service_->RegisterInternalPlugin(plugin_info, false); |
| plugin_service_->RefreshPlugins(); |
| } |
| |
| ~ScopedPluginRegister() { |
| std::vector<WebPluginInfo> plugins; |
| plugin_service_->GetInternalPlugins(&plugins); |
| EXPECT_EQ(1u, plugins.size()); |
| plugin_service_->UnregisterInternalPlugin(plugins[0].path); |
| plugin_service_->RefreshPlugins(); |
| |
| plugins.clear(); |
| plugin_service_->GetInternalPlugins(&plugins); |
| EXPECT_TRUE(plugins.empty()); |
| } |
| |
| private: |
| content::PluginService* plugin_service_; |
| }; |
| #endif // BUILDFLAG(ENABLE_PLUGINS) |
| |
| } // namespace |
| |
| class BlockedSchemeNavigationBrowserTest |
| : public ContentBrowserTest, |
| public testing::WithParamInterface<const char*> { |
| public: |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| BlockedSchemeNavigationBrowserTest() |
| : scoped_plugin_register_(PluginService::GetInstance()) {} |
| #else |
| BlockedSchemeNavigationBrowserTest() {} |
| #endif // BUILDFLAG(ENABLE_PLUGINS) |
| |
| protected: |
| void SetUpOnMainThread() override { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| base::FilePath path; |
| ASSERT_TRUE(PathService::Get(content::DIR_TEST_DATA, &path)); |
| path = path.AppendASCII("data_url_navigations.html"); |
| ASSERT_TRUE(base::PathExists(path)); |
| |
| std::string contents; |
| ASSERT_TRUE(base::ReadFileToString(path, &contents)); |
| data_url_ = GURL(std::string("data:text/html,") + contents); |
| |
| ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir()); |
| ShellDownloadManagerDelegate* delegate = |
| static_cast<ShellDownloadManagerDelegate*>( |
| shell() |
| ->web_contents() |
| ->GetBrowserContext() |
| ->GetDownloadManagerDelegate()); |
| delegate->SetDownloadBehaviorForTesting(downloads_directory_.GetPath()); |
| } |
| |
| void Navigate(const GURL& url) { |
| content::DOMMessageQueue message_queue; |
| EXPECT_TRUE(NavigateToURL(shell(), url)); |
| std::string message; |
| while (message_queue.WaitForMessage(&message)) { |
| if (message == "\"READY\"") |
| break; |
| } |
| } |
| |
| // Creates a filesystem: URL on the current origin. |
| GURL CreateFileSystemUrl(const std::string& filename, |
| const std::string& content, |
| const std::string& mime_type) { |
| const char kCreateFilesystemUrlScript[] = |
| "var contents = `%s`;" |
| "webkitRequestFileSystem(window.TEMPORARY, 1024, fs => {" |
| " fs.root.getFile('%s', {create: true}, entry => {" |
| " entry.createWriter(w => {" |
| " w.write(new Blob([contents], {type: '%s'}));" |
| " });" |
| " domAutomationController.send(entry.toURL());" |
| " });" |
| "});"; |
| std::string filesystem_url_string; |
| EXPECT_TRUE(ExecuteScriptAndExtractString( |
| shell()->web_contents()->GetMainFrame(), |
| base::StringPrintf(kCreateFilesystemUrlScript, content.c_str(), |
| filename.c_str(), mime_type.c_str()), |
| &filesystem_url_string)); |
| GURL filesystem_url(filesystem_url_string); |
| EXPECT_TRUE(filesystem_url.is_valid()); |
| EXPECT_TRUE(filesystem_url.SchemeIsFileSystem()); |
| return filesystem_url; |
| } |
| |
| bool IsDataURLTest() const { |
| return std::string(url::kDataScheme) == GetParam(); |
| } |
| |
| GURL CreateEmptyURLWithBlockedScheme() { |
| return CreateURLWithBlockedScheme("empty.html", "<html></html>", |
| "text/html"); |
| } |
| |
| GURL CreateURLWithBlockedScheme(const std::string& filename, |
| const std::string& content, |
| const std::string& mimetype) { |
| if (IsDataURLTest()) { |
| return GURL( |
| base::StringPrintf("data:%s,%s", mimetype.c_str(), content.c_str())); |
| } |
| // We need an origin to create a filesystem URL on, so navigate to one. |
| NavigateToURL(shell(), |
| embedded_test_server()->GetURL("a.com", "/simple_page.html")); |
| return CreateFileSystemUrl(filename, content, mimetype); |
| } |
| |
| GURL GetTestURL() { |
| return embedded_test_server()->GetURL( |
| base::StringPrintf("/%s_url_navigations.html", GetParam())); |
| } |
| |
| // Adds an iframe to |rfh| pointing to |url|. |
| void AddIFrame(RenderFrameHost* rfh, const GURL& url) { |
| content::DOMMessageQueue message_queue; |
| const std::string javascript = base::StringPrintf( |
| "f = document.createElement('iframe'); f.src = '%s';" |
| "document.body.appendChild(f);", |
| url.spec().c_str()); |
| TestNavigationObserver observer(shell()->web_contents()); |
| EXPECT_TRUE(ExecuteScript(rfh, javascript)); |
| observer.Wait(); |
| std::string message; |
| while (message_queue.WaitForMessage(&message)) { |
| if (message == "\"READY\"") |
| break; |
| } |
| } |
| |
| // Runs |javascript| on the first child frame and checks for a navigation. |
| void TestNavigationFromFrame( |
| const std::string& scheme, |
| const std::string& javascript, |
| ExpectedNavigationStatus expected_navigation_status) { |
| RenderFrameHost* child = |
| ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| ASSERT_TRUE(child); |
| if (AreAllSitesIsolatedForTesting()) { |
| ASSERT_TRUE(child->IsCrossProcessSubframe()); |
| } |
| ExecuteScriptAndCheckNavigation(shell(), child, scheme, javascript, |
| expected_navigation_status); |
| } |
| |
| // Runs |javascript| on the first child frame and expects a download to occur. |
| void TestDownloadFromFrame(const std::string& javascript) { |
| RenderFrameHost* child = |
| ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| ASSERT_TRUE(child); |
| if (AreAllSitesIsolatedForTesting()) { |
| ASSERT_TRUE(child->IsCrossProcessSubframe()); |
| } |
| ExecuteScriptAndCheckDownload(child, javascript); |
| } |
| |
| // Runs |javascript| on the first child frame and checks for a navigation to |
| // the PDF file pointed by the test case. |
| void TestPDFNavigationFromFrame( |
| const std::string& scheme, |
| const std::string& javascript, |
| ExpectedNavigationStatus expected_navigation_status) { |
| RenderFrameHost* child = |
| ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| ASSERT_TRUE(child); |
| if (AreAllSitesIsolatedForTesting()) { |
| ASSERT_TRUE(child->IsCrossProcessSubframe()); |
| } |
| ExecuteScriptAndCheckPDFNavigation(child, scheme, javascript, |
| expected_navigation_status); |
| } |
| |
| // Same as TestNavigationFromFrame, but instead of navigating, the child frame |
| // tries to open a new window with a blocked URL (data or filesystem) |
| void TestWindowOpenFromFrame( |
| const std::string& scheme, |
| const std::string& javascript, |
| ExpectedNavigationStatus expected_navigation_status) { |
| RenderFrameHost* child = |
| ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| if (AreAllSitesIsolatedForTesting()) { |
| ASSERT_TRUE(child->IsCrossProcessSubframe()); |
| } |
| ExecuteScriptAndCheckWindowOpen(child, scheme, javascript, |
| expected_navigation_status); |
| } |
| |
| // Executes |javascript| on |rfh| and waits for a console message based on |
| // |expected_navigation_status|. |
| // - Blocked navigations should print kDataUrlBlockedPattern. |
| // - Allowed navigations should print kNavigationSuccessfulMessage. |
| void ExecuteScriptAndCheckNavigation( |
| Shell* shell, |
| RenderFrameHost* rfh, |
| const std::string& scheme, |
| const std::string& javascript, |
| ExpectedNavigationStatus expected_navigation_status) { |
| if (expected_navigation_status == NAVIGATION_ALLOWED) |
| ExecuteScriptAndCheckNavigationAllowed(shell, rfh, javascript, scheme); |
| else |
| ExecuteScriptAndCheckNavigationBlocked(shell, rfh, javascript, scheme); |
| } |
| |
| protected: |
| // Similar to ExecuteScriptAndCheckNavigation(), but doesn't wait for a |
| // console message if the navigation is expected to be allowed (this is |
| // because PDF files can't print to the console). |
| void ExecuteScriptAndCheckPDFNavigation( |
| RenderFrameHost* rfh, |
| const std::string& scheme, |
| const std::string& javascript, |
| ExpectedNavigationStatus expected_navigation_status) { |
| const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); |
| |
| const std::string expected_message = |
| (expected_navigation_status == NAVIGATION_ALLOWED) |
| ? std::string() |
| : base::StringPrintf(kNavigationBlockedMessage, scheme.c_str()); |
| |
| std::unique_ptr<ConsoleObserverDelegate> console_delegate; |
| if (!expected_message.empty()) { |
| console_delegate.reset(new ConsoleObserverDelegate( |
| shell()->web_contents(), expected_message)); |
| shell()->web_contents()->SetDelegate(console_delegate.get()); |
| } |
| |
| TestNavigationObserver navigation_observer(shell()->web_contents()); |
| EXPECT_TRUE(ExecuteScript(rfh, javascript)); |
| |
| if (console_delegate) { |
| console_delegate->Wait(); |
| shell()->web_contents()->SetDelegate(nullptr); |
| } |
| |
| switch (expected_navigation_status) { |
| case NAVIGATION_ALLOWED: |
| navigation_observer.Wait(); |
| // The new page should have the expected scheme. |
| EXPECT_TRUE( |
| shell()->web_contents()->GetLastCommittedURL().SchemeIs(scheme)); |
| EXPECT_TRUE(navigation_observer.last_navigation_url().SchemeIs(scheme)); |
| EXPECT_TRUE(navigation_observer.last_navigation_succeeded()); |
| break; |
| |
| case NAVIGATION_BLOCKED: |
| // Original page shouldn't navigate away. |
| EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL()); |
| EXPECT_FALSE(navigation_observer.last_navigation_succeeded()); |
| break; |
| |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| // Executes |javascript| on |rfh| and waits for a new window to be opened. |
| // Does not check for console messages (it's currently not possible to |
| // concurrently wait for a new shell to be created and a console message to be |
| // printed on that new shell). |
| void ExecuteScriptAndCheckWindowOpen( |
| RenderFrameHost* rfh, |
| const std::string& scheme, |
| const std::string& javascript, |
| ExpectedNavigationStatus expected_navigation_status) { |
| ShellAddedObserver new_shell_observer; |
| EXPECT_TRUE(ExecuteScript(rfh, javascript)); |
| |
| Shell* new_shell = new_shell_observer.GetShell(); |
| WaitForLoadStop(new_shell->web_contents()); |
| |
| switch (expected_navigation_status) { |
| case NAVIGATION_ALLOWED: |
| EXPECT_TRUE( |
| new_shell->web_contents()->GetLastCommittedURL().SchemeIs(scheme)); |
| break; |
| |
| case NAVIGATION_BLOCKED: |
| EXPECT_TRUE( |
| new_shell->web_contents()->GetLastCommittedURL().is_empty()); |
| break; |
| |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| // Executes |javascript| on |rfh| and waits for a download to be started by |
| // a window.open call. |
| void ExecuteScriptAndCheckWindowOpenDownload(RenderFrameHost* rfh, |
| const std::string& javascript) { |
| const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); |
| ShellAddedObserver new_shell_observer; |
| DownloadManager* download_manager = BrowserContext::GetDownloadManager( |
| shell()->web_contents()->GetBrowserContext()); |
| |
| EXPECT_TRUE(ExecuteScript(rfh, javascript)); |
| Shell* new_shell = new_shell_observer.GetShell(); |
| |
| DownloadTestObserverTerminal download_observer( |
| download_manager, 1, DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); |
| |
| WaitForLoadStop(new_shell->web_contents()); |
| // If no download happens, this will timeout. |
| download_observer.WaitForFinished(); |
| |
| EXPECT_TRUE( |
| new_shell->web_contents()->GetLastCommittedURL().spec().empty()); |
| // No navigation should commit. |
| EXPECT_FALSE( |
| new_shell->web_contents()->GetController().GetLastCommittedEntry()); |
| // Original page shouldn't navigate away. |
| EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL()); |
| } |
| |
| // Executes |javascript| on |rfh| and waits for a download to be started. |
| void ExecuteScriptAndCheckDownload(RenderFrameHost* rfh, |
| const std::string& javascript) { |
| const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); |
| DownloadManager* download_manager = BrowserContext::GetDownloadManager( |
| shell()->web_contents()->GetBrowserContext()); |
| DownloadTestObserverTerminal download_observer( |
| download_manager, 1, DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); |
| |
| EXPECT_TRUE(ExecuteScript(rfh, javascript)); |
| // If no download happens, this will timeout. |
| download_observer.WaitForFinished(); |
| |
| // Original page shouldn't navigate away. |
| EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL()); |
| } |
| |
| // Initiates a browser initiated navigation to |url| and waits for a download |
| // to be started. |
| void NavigateAndCheckDownload(const GURL& url) { |
| const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); |
| DownloadManager* download_manager = BrowserContext::GetDownloadManager( |
| shell()->web_contents()->GetBrowserContext()); |
| DownloadTestObserverTerminal download_observer( |
| download_manager, 1, DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); |
| NavigateToURL(shell(), url); |
| |
| // If no download happens, this will timeout. |
| download_observer.WaitForFinished(); |
| |
| // Original page shouldn't navigate away. |
| EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL()); |
| } |
| |
| // data URL form of the file at content/test/data/data_url_navigations.html |
| GURL data_url() const { return data_url_; } |
| |
| std::string GetNavigationBlockedMessage() const { |
| return base::StringPrintf(kNavigationBlockedMessage, GetParam()); |
| } |
| |
| private: |
| // Executes |javascript| on |rfh| and waits for a console message that |
| // indicates the navigation has completed. |scheme| is the scheme being |
| // tested. |
| static void ExecuteScriptAndCheckNavigationAllowed( |
| Shell* shell, |
| RenderFrameHost* rfh, |
| const std::string& javascript, |
| const std::string& scheme) { |
| // Should see success message, should never see blocked message. |
| const std::string blocked_message = |
| base::StringPrintf(kNavigationBlockedMessage, scheme.c_str()); |
| BlockedURLWarningConsoleObserverDelegate console_delegate( |
| shell->web_contents(), kNavigationSuccessfulMessage, blocked_message); |
| shell->web_contents()->SetDelegate(&console_delegate); |
| |
| TestNavigationObserver navigation_observer(shell->web_contents()); |
| EXPECT_TRUE(ExecuteScript(rfh, javascript)); |
| console_delegate.Wait(); |
| EXPECT_EQ(BlockedURLWarningConsoleObserverDelegate::SAW_SUCCESS_MESSAGE, |
| console_delegate.status()); |
| shell->web_contents()->SetDelegate(nullptr); |
| navigation_observer.Wait(); |
| |
| // The new page should have the expected scheme. |
| EXPECT_EQ(navigation_observer.last_navigation_url(), |
| shell->web_contents()->GetLastCommittedURL()); |
| EXPECT_TRUE(navigation_observer.last_navigation_succeeded()); |
| } |
| |
| // Similar to ExecuteScriptAndCheckNavigationAllowed. Executes |javascript| on |
| // |rfh| and waits for a console message that indicates the navigation has |
| // been blocked. |scheme| is the scheme being tested. |
| static void ExecuteScriptAndCheckNavigationBlocked( |
| Shell* shell, |
| RenderFrameHost* rfh, |
| const std::string& javascript, |
| const std::string& scheme) { |
| const GURL original_url(shell->web_contents()->GetLastCommittedURL()); |
| |
| // Should see blocked message, should never see success message. |
| const std::string blocked_message = |
| base::StringPrintf(kNavigationBlockedMessage, scheme.c_str()); |
| BlockedURLWarningConsoleObserverDelegate console_delegate( |
| shell->web_contents(), kNavigationSuccessfulMessage, blocked_message); |
| shell->web_contents()->SetDelegate(&console_delegate); |
| |
| TestNavigationObserver navigation_observer(shell->web_contents()); |
| EXPECT_TRUE(ExecuteScript(rfh, javascript)); |
| console_delegate.Wait(); |
| EXPECT_EQ(BlockedURLWarningConsoleObserverDelegate::SAW_FAILURE_MESSAGE, |
| console_delegate.status()); |
| shell->web_contents()->SetDelegate(nullptr); |
| |
| // Original page shouldn't navigate away. |
| EXPECT_EQ(original_url, shell->web_contents()->GetLastCommittedURL()); |
| EXPECT_FALSE(navigation_observer.last_navigation_succeeded()); |
| } |
| |
| base::ScopedTempDir downloads_directory_; |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| ScopedPluginRegister scoped_plugin_register_; |
| #endif // BUILDFLAG(ENABLE_PLUGINS) |
| |
| GURL data_url_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BlockedSchemeNavigationBrowserTest); |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(, |
| BlockedSchemeNavigationBrowserTest, |
| ::testing::Values(url::kDataScheme, |
| url::kFileSystemScheme)); |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Blocked schemes with HTML mimetype |
| // |
| // Tests that a browser initiated navigation to a blocked scheme doesn't show a |
| // console warning and is not blocked. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| BrowserInitiated_Allow) { |
| const GURL kUrl(CreateURLWithBlockedScheme( |
| "test.html", |
| "<html><script>console.log('NAVIGATION_SUCCESSFUL');</script></html>", |
| "text/html")); |
| if (IsDataURLTest()) { |
| BlockedURLWarningConsoleObserverDelegate console_delegate( |
| shell()->web_contents(), kNavigationSuccessfulMessage, |
| GetNavigationBlockedMessage()); |
| |
| shell()->web_contents()->SetDelegate(&console_delegate); |
| EXPECT_TRUE(NavigateToURL(shell(), kUrl)); |
| console_delegate.Wait(); |
| shell()->web_contents()->SetDelegate(nullptr); |
| EXPECT_TRUE( |
| shell()->web_contents()->GetLastCommittedURL().SchemeIs(GetParam())); |
| |
| } else { |
| // Navigate to a.com and create a filesystem URL on it. |
| // For filesystem: tests we create a new shell and navigate that shell to |
| // the filesystem: URL created above. Navigating the a tab away from the |
| // original page may clear all filesystem: URLs associated with that origin, |
| // so we keep the origin around in the original shell. |
| ShellAddedObserver new_shell_observer; |
| EXPECT_TRUE( |
| ExecuteScript(shell()->web_contents(), "window.open('about:blank');")); |
| Shell* new_shell = new_shell_observer.GetShell(); |
| WaitForLoadStop(new_shell->web_contents()); |
| |
| BlockedURLWarningConsoleObserverDelegate console_delegate( |
| new_shell->web_contents(), kNavigationSuccessfulMessage, |
| GetNavigationBlockedMessage()); |
| new_shell->web_contents()->SetDelegate(&console_delegate); |
| EXPECT_TRUE(NavigateToURL(new_shell, kUrl)); |
| |
| console_delegate.Wait(); |
| new_shell->web_contents()->SetDelegate(nullptr); |
| EXPECT_TRUE( |
| new_shell->web_contents()->GetLastCommittedURL().SchemeIs(GetParam())); |
| } |
| } |
| |
| // Tests that a content initiated navigation to a blocked scheme is blocked. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| HTML_Navigation_Block) { |
| Navigate(GetTestURL()); |
| ExecuteScriptAndCheckNavigation( |
| shell(), shell()->web_contents()->GetMainFrame(), GetParam(), |
| "document.getElementById('navigate-top-frame-to-html').click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| class DataUrlNavigationBrowserTestWithFeatureFlag |
| : public BlockedSchemeNavigationBrowserTest { |
| public: |
| DataUrlNavigationBrowserTestWithFeatureFlag() { |
| scoped_feature_list_.InitAndEnableFeature( |
| features::kAllowContentInitiatedDataUrlNavigations); |
| } |
| ~DataUrlNavigationBrowserTestWithFeatureFlag() override {} |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DataUrlNavigationBrowserTestWithFeatureFlag); |
| }; |
| |
| // Tests that a content initiated navigation to a data URL is allowed if |
| // blocking is disabled with a feature flag. |
| IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTestWithFeatureFlag, |
| HTML_Navigation_Allow_FeatureFlag) { |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("/data_url_navigations.html"))); |
| ExecuteScriptAndCheckNavigation( |
| shell(), shell()->web_contents()->GetMainFrame(), url::kDataScheme, |
| "document.getElementById('navigate-top-frame-to-html').click()", |
| NAVIGATION_ALLOWED); |
| } |
| |
| // Tests that a window.open to a blocked scheme with HTML mime type is blocked. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| HTML_WindowOpen_Block) { |
| Navigate(GetTestURL()); |
| ExecuteScriptAndCheckWindowOpen( |
| shell()->web_contents()->GetMainFrame(), GetParam(), |
| "document.getElementById('window-open-html').click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| // Tests that a form post to a blocked scheme with HTML mime type is blocked. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| HTML_FormPost_Block) { |
| Navigate(GetTestURL()); |
| ExecuteScriptAndCheckNavigation( |
| shell(), shell()->web_contents()->GetMainFrame(), GetParam(), |
| "document.getElementById('form-post-to-html').click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| // Tests that clicking <a download> link downloads the URL even with a blocked |
| // scheme. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, HTML_Download) { |
| Navigate(GetTestURL()); |
| ExecuteScriptAndCheckDownload( |
| shell()->web_contents()->GetMainFrame(), |
| "document.getElementById('download-link').click()"); |
| } |
| |
| // Tests that navigating the main frame to a blocked scheme with HTML mimetype |
| // from a subframe is blocked. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| HTML_NavigationFromFrame_Block) { |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); |
| AddIFrame( |
| shell()->web_contents()->GetMainFrame(), |
| embedded_test_server()->GetURL( |
| "b.com", base::StringPrintf("/%s_url_navigations.html", GetParam()))); |
| |
| TestNavigationFromFrame( |
| GetParam(), |
| "document.getElementById('navigate-top-frame-to-html').click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| // Tests that opening a new window with a blocked scheme from a subframe is |
| // blocked. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| HTML_WindowOpenFromFrame_Block) { |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); |
| AddIFrame( |
| shell()->web_contents()->GetMainFrame(), |
| embedded_test_server()->GetURL( |
| "b.com", base::StringPrintf("/%s_url_navigations.html", GetParam()))); |
| |
| TestWindowOpenFromFrame(GetParam(), |
| "document.getElementById('window-open-html').click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| // Tests that navigation to a blocked scheme is blocked even if the top frame is |
| // already has a blocked scheme. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| HTML_Navigation_SameScheme_Block) { |
| if (IsDataURLTest()) { |
| EXPECT_TRUE(NavigateToURL(shell(), data_url())); |
| ExecuteScriptAndCheckNavigation( |
| shell(), shell()->web_contents()->GetMainFrame(), url::kDataScheme, |
| "document.getElementById('navigate-top-frame-to-html').click()", |
| NAVIGATION_BLOCKED); |
| } else { |
| // We need an origin to create a filesystem URL on, so navigate to one. |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); |
| const GURL kFilesystemURL1( |
| CreateFileSystemUrl("empty1.html", "empty1", "text/html")); |
| const GURL kFilesystemURL2( |
| CreateFileSystemUrl("empty2.html", "empty2", "text/html")); |
| |
| // Create a new shell and navigate that shell to the filesystem: URL created |
| // above. Navigating the a tab away from the |
| // original page may clear all filesystem: URLs associated with that origin, |
| // so we keep the origin around in the original shell. |
| ShellAddedObserver new_shell_observer; |
| EXPECT_TRUE( |
| ExecuteScript(shell()->web_contents(), "window.open('about:blank');")); |
| Shell* new_shell = new_shell_observer.GetShell(); |
| WaitForLoadStop(new_shell->web_contents()); |
| |
| EXPECT_TRUE(NavigateToURL(new_shell, kFilesystemURL1)); |
| ExecuteScriptAndCheckNavigation( |
| new_shell, new_shell->web_contents()->GetMainFrame(), |
| url::kFileSystemScheme, |
| base::StringPrintf("window.location='%s';", |
| kFilesystemURL2.spec().c_str()), |
| NAVIGATION_BLOCKED); |
| } |
| } |
| |
| // Tests that a form post to a blocked scheme with HTML mime type is blocked |
| // even if the top frame is already a blocked scheme. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| HTML_FormPost_SameScheme_Block) { |
| if (IsDataURLTest()) { |
| EXPECT_TRUE(NavigateToURL(shell(), data_url())); |
| ExecuteScriptAndCheckNavigation( |
| shell(), shell()->web_contents()->GetMainFrame(), url::kDataScheme, |
| "document.getElementById('form-post-to-html').click()", |
| NAVIGATION_BLOCKED); |
| } else { |
| // We need an origin to create a filesystem URL on, so navigate to one. |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); |
| const GURL kFilesystemURL1( |
| CreateFileSystemUrl("target.html", "form target", "text/html")); |
| const GURL kFilesystemURL2(CreateFileSystemUrl( |
| "form.html", |
| base::StringPrintf("<html><form id=f method=post action='%s'><input " |
| "type=submit " |
| "onclick=document.getElementById('f').click() " |
| "id=btn-submit></form></html>", |
| kFilesystemURL1.spec().c_str()), |
| "text/html")); |
| |
| // Create a new shell and navigate that shell to the filesystem: URL created |
| // above. Navigating the a tab away from the |
| // original page may clear all filesystem: URLs associated with that origin, |
| // so we keep the origin around in the original shell. |
| ShellAddedObserver new_shell_observer; |
| // TODO(crbug/811558): about:blank might commit without needing to wait. |
| // Remove the wait. |
| EXPECT_TRUE( |
| ExecuteScript(shell()->web_contents(), "window.open('about:blank');")); |
| Shell* new_shell = new_shell_observer.GetShell(); |
| WaitForLoadStop(new_shell->web_contents()); |
| |
| EXPECT_TRUE(NavigateToURL(new_shell, kFilesystemURL2)); |
| ExecuteScriptAndCheckNavigation( |
| new_shell, new_shell->web_contents()->GetMainFrame(), |
| url::kFileSystemScheme, "document.getElementById('btn-submit').click()", |
| NAVIGATION_BLOCKED); |
| } |
| } |
| |
| // Tests that navigating the top frame to a blocked scheme with HTML mimetype is |
| // blocked even if the top frame already has a blocked scheme. |
| IN_PROC_BROWSER_TEST_P( |
| BlockedSchemeNavigationBrowserTest, |
| HTML_NavigationFromFrame_TopFrameHasBlockedScheme_Block) { |
| EXPECT_TRUE(NavigateToURL(shell(), CreateEmptyURLWithBlockedScheme())); |
| AddIFrame(shell()->web_contents()->GetMainFrame(), GetTestURL()); |
| |
| TestNavigationFromFrame( |
| GetParam(), |
| "document.getElementById('navigate-top-frame-to-html').click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| // Tests that opening a new window with a blocked scheme with HTML mimetype is |
| // blocked even if the top frame already has a blocked scheme. |
| IN_PROC_BROWSER_TEST_P( |
| BlockedSchemeNavigationBrowserTest, |
| HTML_WindowOpenFromFrame_TopFrameHasBlockedScheme_Block) { |
| // Create an empty URL with a blocked scheme, navigate to it, and add a frame. |
| EXPECT_TRUE(NavigateToURL(shell(), CreateEmptyURLWithBlockedScheme())); |
| AddIFrame(shell()->web_contents()->GetMainFrame(), GetTestURL()); |
| |
| TestWindowOpenFromFrame(GetParam(), |
| "document.getElementById('window-open-html').click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Blocked schemes with octet-stream mimetype (binary) |
| // |
| // Test direct navigations to a binary mime types. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| OctetStream_BrowserInitiated) { |
| const GURL kUrl(CreateURLWithBlockedScheme("test.html", "test", |
| "application/octet-stream")); |
| |
| if (IsDataURLTest()) { |
| // Navigations to data URLs with unknown mime types should end up as |
| // downloads. |
| NavigateAndCheckDownload(kUrl); |
| } else { |
| // Navigations to filesystem URLs never end up as downloads. |
| EXPECT_TRUE(NavigateToURL(shell(), kUrl)); |
| EXPECT_EQ(kUrl, shell()->web_contents()->GetLastCommittedURL()); |
| } |
| } |
| |
| #if defined(OS_ANDROID) |
| // Flaky on android: https://crbug.com/734563 |
| #define MAYBE_DataUrl_OctetStream_WindowOpen \ |
| DISABLED_DataUrl_OctetStream_WindowOpen |
| #else |
| #define MAYBE_DataUrl_OctetStream_WindowOpen DataUrl_OctetStream_WindowOpen |
| #endif |
| |
| // Test window.open to a data URL with binary mimetype. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| DataUrl_OctetStream_WindowOpen) { |
| Navigate(embedded_test_server()->GetURL( |
| base::StringPrintf("/data_url_navigations.html"))); |
| // Navigations to data URLs with unknown mime types should end up as |
| // downloads. |
| ExecuteScriptAndCheckWindowOpenDownload( |
| shell()->web_contents()->GetMainFrame(), |
| "document.getElementById('window-open-octetstream').click()"); |
| } |
| |
| // Test window.open to a filesystem URL with binary mimetype. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| FilesystemUrl_OctetStream_WindowOpen) { |
| Navigate(embedded_test_server()->GetURL( |
| base::StringPrintf("/filesystem_url_navigations.html"))); |
| // Navigations to filesystem URLs never end up as downloads. |
| ExecuteScriptAndCheckWindowOpen( |
| shell()->web_contents()->GetMainFrame(), url::kFileSystemScheme, |
| "document.getElementById('window-open-octetstream').click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| // Test navigation to a data URL with binary mimetype. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| DataUrl_OctetStream_Navigation) { |
| Navigate(embedded_test_server()->GetURL( |
| base::StringPrintf("/data_url_navigations.html"))); |
| // Navigations to data URLs with unknown mime types should end up as |
| // downloads. |
| ExecuteScriptAndCheckDownload( |
| shell()->web_contents()->GetMainFrame(), |
| "document.getElementById('navigate-top-frame-to-octetstream').click()"); |
| } |
| |
| // Test navigation to a filesystem URL with binary mimetype. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| FilesystemUrl_OctetStream_Navigation) { |
| Navigate(embedded_test_server()->GetURL( |
| base::StringPrintf("/filesystem_url_navigations.html"))); |
| // Navigations to filesystem URLs never end up as downloads. |
| ExecuteScriptAndCheckNavigation( |
| shell(), shell()->web_contents()->GetMainFrame(), url::kFileSystemScheme, |
| "document.getElementById('navigate-top-frame-to-octetstream').click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| // Test form post to a data URL with binary mimetype. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| DataUrl_OctetStream_FormPost) { |
| Navigate(embedded_test_server()->GetURL( |
| base::StringPrintf("/data_url_navigations.html"))); |
| // Form posts to data URLs with unknown mime types should end up as |
| // downloads. |
| ExecuteScriptAndCheckDownload( |
| shell()->web_contents()->GetMainFrame(), |
| "document.getElementById('form-post-to-octetstream').click()"); |
| } |
| |
| // Test form post to a filesystem URL with binary mimetype. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| FilesystemUrl_OctetStream_FormPost) { |
| Navigate(embedded_test_server()->GetURL( |
| base::StringPrintf("/filesystem_url_navigations.html"))); |
| // Navigations to filesystem URLs never end up as downloads. |
| ExecuteScriptAndCheckNavigation( |
| shell(), shell()->web_contents()->GetMainFrame(), url::kFileSystemScheme, |
| "document.getElementById('form-post-to-octetstream').click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| // Tests navigation of the main frame to a data URL with a binary mimetype |
| // from a subframe. These navigations should end up as downloads. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| DataUrl_OctetStream_NavigationFromFrame) { |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); |
| AddIFrame( |
| shell()->web_contents()->GetMainFrame(), |
| embedded_test_server()->GetURL("b.com", "/data_url_navigations.html")); |
| TestDownloadFromFrame( |
| "document.getElementById('navigate-top-frame-to-octetstream').click()"); |
| } |
| |
| // Tests navigation of the main frame to a filesystem URL with a binary mimetype |
| // from a subframe. Navigations to filesystem URLs never end up as downloads. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| FilesystemUrl_OctetStream_NavigationFromFrame) { |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); |
| AddIFrame(shell()->web_contents()->GetMainFrame(), |
| embedded_test_server()->GetURL("b.com", |
| "/filesystem_url_navigations.html")); |
| |
| TestNavigationFromFrame( |
| url::kFileSystemScheme, |
| "document.getElementById('navigate-top-frame-to-octetstream').click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // URLs with unknown mimetype |
| // |
| // Test direct navigation to an unknown mime type. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| UnknownMimeType_BrowserInitiated_Download) { |
| const GURL kUrl( |
| CreateURLWithBlockedScheme("test.html", "test", "unknown/mimetype")); |
| |
| if (IsDataURLTest()) { |
| // Navigations to data URLs with unknown mime types should end up as |
| // downloads. |
| NavigateAndCheckDownload(kUrl); |
| } else { |
| // Navigations to filesystem URLs never end up as downloads. |
| EXPECT_TRUE(NavigateToURL(shell(), kUrl)); |
| EXPECT_EQ(kUrl, shell()->web_contents()->GetLastCommittedURL()); |
| } |
| } |
| |
| #if defined(OS_ANDROID) |
| // Flaky on android: https://crbug.com/734563 |
| #define MAYBE_UnknownMimeType_WindowOpen DISABLED_UnknownMimeType_WindowOpen |
| #else |
| #define MAYBE_UnknownMimeType_WindowOpen UnknownMimeType_WindowOpen |
| #endif |
| |
| // Test window.open to a blocked scheme with an unknown mime type. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| MAYBE_UnknownMimeType_WindowOpen) { |
| Navigate(GetTestURL()); |
| if (IsDataURLTest()) { |
| // Navigations to data URLs with unknown mime types should end up as |
| // downloads. |
| ExecuteScriptAndCheckWindowOpenDownload( |
| shell()->web_contents()->GetMainFrame(), |
| "document.getElementById('window-open-unknown-mimetype').click()"); |
| } else { |
| // Navigations to filesystem URLs never end up as downloads. |
| ExecuteScriptAndCheckWindowOpen( |
| shell()->web_contents()->GetMainFrame(), GetParam(), |
| "document.getElementById('window-open-unknown-mimetype').click()", |
| NAVIGATION_BLOCKED); |
| } |
| } |
| |
| // Test navigation to a data URL with an unknown mime type. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| DataUrl_UnknownMimeType_Navigation) { |
| Navigate(embedded_test_server()->GetURL( |
| base::StringPrintf("/data_url_navigations.html"))); |
| // Navigations to data URLs with unknown mime types should end up as |
| // downloads. |
| ExecuteScriptAndCheckDownload(shell()->web_contents()->GetMainFrame(), |
| "document.getElementById('navigate-top-frame-" |
| "to-unknown-mimetype').click()"); |
| } |
| |
| // Test navigation to a filesystem URL with an unknown mime type. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| FilesystemUrl_UnknownMimeType_Navigation) { |
| Navigate(embedded_test_server()->GetURL( |
| base::StringPrintf("/filesystem_url_navigations.html"))); |
| // Navigations to filesystem URLs never end up as downloads. |
| ExecuteScriptAndCheckNavigation( |
| shell(), shell()->web_contents()->GetMainFrame(), url::kFileSystemScheme, |
| "document.getElementById('navigate-top-frame-to-unknown-mimetype')." |
| "click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| // Test form post to a data URL with an unknown mime type. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| DataUrl_UnknownMimeType_FormPost) { |
| Navigate(embedded_test_server()->GetURL( |
| base::StringPrintf("/data_url_navigations.html"))); |
| // Form posts to data URLs with unknown mime types should end up as |
| // downloads. |
| ExecuteScriptAndCheckDownload( |
| shell()->web_contents()->GetMainFrame(), |
| "document.getElementById('form-post-to-unknown-mimetype').click()"); |
| } |
| |
| // Test form post to a filesystem URL with an unknown mime type. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| FilesystemUrl_UnknownMimeType_FormPost) { |
| Navigate(embedded_test_server()->GetURL( |
| base::StringPrintf("/filesystem_url_navigations.html"))); |
| // Navigations to filesystem URLs never end up as downloads. |
| ExecuteScriptAndCheckNavigation( |
| shell(), shell()->web_contents()->GetMainFrame(), url::kFileSystemScheme, |
| "document.getElementById('form-post-to-unknown-mimetype').click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| // Test navigation of the main frame to a data URL with an unknown mimetype from |
| // a subframe. These navigations should end up as downloads. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| DataUrl_UnknownMimeType_NavigationFromFrame) { |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); |
| AddIFrame( |
| shell()->web_contents()->GetMainFrame(), |
| embedded_test_server()->GetURL("b.com", "/data_url_navigations.html")); |
| |
| TestDownloadFromFrame( |
| "document.getElementById('navigate-top-frame-to-unknown-mimetype')." |
| "click()"); |
| } |
| |
| // Test navigation of the main frame to a filesystem URL with an unknown |
| // mimetype from a subframe. Navigations to filesystem URLs don't end up as |
| // downloads. |
| IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, |
| FilesystemUrl_UnknownMimeType_NavigationFromFrame) { |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); |
| AddIFrame(shell()->web_contents()->GetMainFrame(), |
| embedded_test_server()->GetURL("b.com", |
| "/filesystem_url_navigations.html")); |
| |
| TestNavigationFromFrame(url::kFileSystemScheme, |
| "document.getElementById('navigate-top-frame-to-" |
| "unknown-mimetype').click()", |
| NAVIGATION_BLOCKED); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // URLs with PDF mimetype |
| // |
| // Tests that a browser initiated navigation to a blocked scheme URL with PDF |
| // mime type is allowed, or initiates a download on Android. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| PDF_BrowserInitiatedNavigation_Allow) { |
| std::string pdf_base64; |
| base::Base64Encode(kPDF, &pdf_base64); |
| const GURL kPDFUrl(CreateURLWithBlockedScheme( |
| "test.pdf", IsDataURLTest() ? pdf_base64 : kPDF, "application/pdf")); |
| |
| #if !defined(OS_ANDROID) |
| TestNavigationObserver observer(shell()->web_contents()); |
| EXPECT_TRUE(NavigateToURL(shell(), kPDFUrl)); |
| EXPECT_EQ(kPDFUrl, observer.last_navigation_url()); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_TRUE( |
| shell()->web_contents()->GetLastCommittedURL().SchemeIs(GetParam())); |
| #else |
| NavigateAndCheckDownload(kPDFUrl); |
| #endif |
| } |
| |
| // Tests that a window.open to a blocked scheme is blocked if the URL has a |
| // mime type that will be handled by a plugin (PDF in this case). |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| PDF_WindowOpen_Block) { |
| Navigate(GetTestURL()); |
| |
| #if !defined(OS_ANDROID) |
| ExecuteScriptAndCheckWindowOpen( |
| shell()->web_contents()->GetMainFrame(), GetParam(), |
| "document.getElementById('window-open-pdf').click()", NAVIGATION_BLOCKED); |
| #else |
| if (IsDataURLTest()) { |
| // On Android, data URL PDFs are downloaded upon navigation. |
| ExecuteScriptAndCheckDownload( |
| shell()->web_contents()->GetMainFrame(), |
| "document.getElementById('window-open-pdf').click()"); |
| } else { |
| // On Android, filesystem PDF URLs are navigated and should be blocked. |
| ExecuteScriptAndCheckWindowOpen( |
| shell()->web_contents()->GetMainFrame(), GetParam(), |
| "document.getElementById('window-open-pdf').click()", |
| NAVIGATION_BLOCKED); |
| } |
| #endif |
| } |
| |
| // Test that a navigation to a blocked scheme URL is blocked if the URL has a |
| // mime type that will be handled by a plugin (PDF in this case). |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| PDF_Navigation_Block) { |
| Navigate(GetTestURL()); |
| |
| #if !defined(OS_ANDROID) |
| ExecuteScriptAndCheckPDFNavigation( |
| shell()->web_contents()->GetMainFrame(), GetParam(), |
| "document.getElementById('navigate-top-frame-to-pdf').click()", |
| NAVIGATION_BLOCKED); |
| #else |
| if (IsDataURLTest()) { |
| // On Android, data URL PDFs are downloaded upon navigation. |
| ExecuteScriptAndCheckDownload( |
| shell()->web_contents()->GetMainFrame(), |
| "document.getElementById('navigate-top-frame-to-pdf').click()"); |
| } else { |
| // On Android, filesystem PDF URLs are navigated and should be blocked. |
| ExecuteScriptAndCheckPDFNavigation( |
| shell()->web_contents()->GetMainFrame(), GetParam(), |
| "document.getElementById('navigate-top-frame-to-pdf').click()", |
| NAVIGATION_BLOCKED); |
| } |
| #endif |
| } |
| |
| // Test that a form post to a blocked scheme is blocked if the URL has a mime |
| // type that will be handled by a plugin (PDF in this case). |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, PDF_FormPost_Block) { |
| Navigate(GetTestURL()); |
| |
| #if !defined(OS_ANDROID) |
| ExecuteScriptAndCheckPDFNavigation( |
| shell()->web_contents()->GetMainFrame(), GetParam(), |
| "document.getElementById('form-post-to-pdf').click()", |
| NAVIGATION_BLOCKED); |
| #else |
| if (IsDataURLTest()) { |
| // On Android, data URL PDFs are downloaded upon navigation. |
| ExecuteScriptAndCheckDownload( |
| shell()->web_contents()->GetMainFrame(), |
| "document.getElementById('form-post-to-pdf').click()"); |
| } else { |
| // On Android, filesystem PDF URLs are navigated and should be blocked. |
| ExecuteScriptAndCheckPDFNavigation( |
| shell()->web_contents()->GetMainFrame(), GetParam(), |
| "document.getElementById('form-post-to-pdf').click()", |
| NAVIGATION_BLOCKED); |
| } |
| #endif |
| } |
| |
| // Tests that navigating the main frame to a blocked scheme with PDF mimetype |
| // from a subframe is blocked, or is downloaded on Android. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| PDF_NavigationFromFrame_Block) { |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); |
| AddIFrame( |
| shell()->web_contents()->GetMainFrame(), |
| embedded_test_server()->GetURL( |
| "b.com", base::StringPrintf("/%s_url_navigations.html", GetParam()))); |
| |
| #if !defined(OS_ANDROID) |
| TestPDFNavigationFromFrame( |
| GetParam(), |
| "document.getElementById('navigate-top-frame-to-pdf').click()", |
| NAVIGATION_BLOCKED); |
| #else |
| if (IsDataURLTest()) { |
| // On Android, data URL PDFs are downloaded upon navigation. |
| RenderFrameHost* child = |
| ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| ASSERT_TRUE(child); |
| if (AreAllSitesIsolatedForTesting()) { |
| ASSERT_TRUE(child->IsCrossProcessSubframe()); |
| } |
| ExecuteScriptAndCheckDownload( |
| child, "document.getElementById('navigate-top-frame-to-pdf').click()"); |
| } else { |
| // On Android, filesystem PDF URLs are navigated and should be blocked. |
| TestPDFNavigationFromFrame( |
| GetParam(), |
| "document.getElementById('navigate-top-frame-to-pdf').click()", |
| NAVIGATION_BLOCKED); |
| } |
| #endif |
| } |
| |
| // Tests that opening a window with a blocked scheme with PDF mimetype from a |
| // subframe is blocked, or is downloaded on Android. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| PDF_WindowOpenFromFrame_Block) { |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); |
| AddIFrame(shell()->web_contents()->GetMainFrame(), |
| embedded_test_server()->GetURL( |
| base::StringPrintf("/%s_url_navigations.html", GetParam()))); |
| |
| #if !defined(OS_ANDROID) |
| TestWindowOpenFromFrame(GetParam(), |
| "document.getElementById('window-open-pdf').click()", |
| NAVIGATION_BLOCKED); |
| #else |
| if (IsDataURLTest()) { |
| // On Android, data URL PDFs are downloaded upon navigation. |
| RenderFrameHost* child = |
| ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| ASSERT_TRUE(child); |
| if (AreAllSitesIsolatedForTesting()) { |
| ASSERT_TRUE(child->IsCrossProcessSubframe()); |
| } |
| ExecuteScriptAndCheckDownload( |
| child, "document.getElementById('window-open-pdf').click()"); |
| } else { |
| // On Android, filesystem PDF URLs are navigated and should be blocked. |
| TestWindowOpenFromFrame( |
| GetParam(), "document.getElementById('window-open-pdf').click()", |
| NAVIGATION_BLOCKED); |
| } |
| #endif |
| } |
| |
| // Tests that navigating the top frame to a blocked scheme with PDF mimetype |
| // from a subframe is blocked even if the top frame already has a blocked |
| // scheme. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| PDF_NavigationFromFrame_TopFrameHasBlockedScheme_Block) { |
| EXPECT_TRUE(NavigateToURL(shell(), CreateEmptyURLWithBlockedScheme())); |
| AddIFrame(shell()->web_contents()->GetMainFrame(), GetTestURL()); |
| |
| #if !defined(OS_ANDROID) |
| TestPDFNavigationFromFrame( |
| GetParam(), |
| "document.getElementById('navigate-top-frame-to-pdf').click()", |
| NAVIGATION_BLOCKED); |
| #else |
| if (IsDataURLTest()) { |
| // On Android, data URL PDFs are downloaded upon navigation. |
| RenderFrameHost* child = |
| ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| ASSERT_TRUE(child); |
| if (AreAllSitesIsolatedForTesting()) { |
| ASSERT_TRUE(child->IsCrossProcessSubframe()); |
| } |
| ExecuteScriptAndCheckDownload( |
| child, "document.getElementById('navigate-top-frame-to-pdf').click()"); |
| } else { |
| // On Android, filesystem PDF URLs are navigated and should be blocked. |
| TestPDFNavigationFromFrame( |
| GetParam(), |
| "document.getElementById('navigate-top-frame-to-pdf').click()", |
| NAVIGATION_BLOCKED); |
| } |
| #endif |
| } |
| |
| // Tests that opening a window with a blocked scheme with PDF mimetype from a |
| // subframe is blocked even if the top frame already has a blocked scheme. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| PDF_WindowOpenFromFrame_TopFrameHasBlockedScheme_Block) { |
| EXPECT_TRUE(NavigateToURL(shell(), CreateEmptyURLWithBlockedScheme())); |
| AddIFrame(shell()->web_contents()->GetMainFrame(), GetTestURL()); |
| |
| #if !defined(OS_ANDROID) |
| TestWindowOpenFromFrame(GetParam(), |
| "document.getElementById('window-open-pdf').click()", |
| NAVIGATION_BLOCKED); |
| #else |
| if (IsDataURLTest()) { |
| // On Android, data URL PDFs are downloaded upon navigation. |
| RenderFrameHost* child = |
| ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| ASSERT_TRUE(child); |
| if (AreAllSitesIsolatedForTesting()) { |
| ASSERT_TRUE(child->IsCrossProcessSubframe()); |
| } |
| ExecuteScriptAndCheckDownload( |
| child, "document.getElementById('window-open-pdf').click()"); |
| } else { |
| // On Android, filesystem PDF URLs are navigated to and should be blocked. |
| TestWindowOpenFromFrame( |
| GetParam(), "document.getElementById('window-open-pdf').click()", |
| NAVIGATION_BLOCKED); |
| } |
| #endif |
| } |
| |
| // Test case to verify that redirects to blocked schemes are properly |
| // disallowed, even when invoked through history navigations. See |
| // https://crbug.com/723796. |
| IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, |
| WindowOpenRedirectAndBack) { |
| Navigate(GetTestURL()); |
| |
| // This test will need to navigate the newly opened window. |
| ShellAddedObserver new_shell_observer; |
| EXPECT_TRUE( |
| ExecuteScript(shell()->web_contents(), |
| "document.getElementById('window-open-redirect').click()")); |
| Shell* new_shell = new_shell_observer.GetShell(); |
| NavigationController* controller = |
| &new_shell->web_contents()->GetController(); |
| WaitForLoadStop(new_shell->web_contents()); |
| |
| // The window.open() should have resulted in an error page. The blocked |
| // URL should be in both the actual and the virtual URL. |
| { |
| EXPECT_EQ(0, controller->GetLastCommittedEntryIndex()); |
| NavigationEntry* entry = controller->GetLastCommittedEntry(); |
| EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType()); |
| EXPECT_FALSE(entry->GetURL().SchemeIs(url::kDataScheme)); |
| EXPECT_FALSE(entry->GetURL().SchemeIs(url::kFileSystemScheme)); |
| EXPECT_TRUE(base::StartsWith( |
| entry->GetURL().spec(), |
| embedded_test_server()->GetURL("/server-redirect?").spec(), |
| base::CompareCase::SENSITIVE)); |
| EXPECT_EQ(entry->GetURL(), entry->GetVirtualURL()); |
| } |
| |
| // Navigate forward and then go back to ensure the navigation to data: or |
| // filesystem: URL is blocked. Use a browser-initiated back navigation, |
| // equivalent to user pressing the back button. |
| EXPECT_TRUE( |
| NavigateToURL(new_shell, embedded_test_server()->GetURL("/title1.html"))); |
| EXPECT_EQ(1, controller->GetLastCommittedEntryIndex()); |
| { |
| TestNavigationObserver observer(new_shell->web_contents()); |
| controller->GoBack(); |
| observer.Wait(); |
| |
| NavigationEntry* entry = controller->GetLastCommittedEntry(); |
| EXPECT_EQ(0, controller->GetLastCommittedEntryIndex()); |
| EXPECT_FALSE(entry->GetURL().SchemeIs(url::kDataScheme)); |
| EXPECT_FALSE(entry->GetURL().SchemeIs(url::kFileSystemScheme)); |
| EXPECT_TRUE(base::StartsWith( |
| entry->GetURL().spec(), |
| embedded_test_server()->GetURL("/server-redirect?").spec(), |
| base::CompareCase::SENSITIVE)); |
| EXPECT_EQ(entry->GetURL(), entry->GetVirtualURL()); |
| } |
| |
| // Do another new navigation, but then use JavaScript to navigate back, |
| // equivalent to document executing JS. |
| EXPECT_TRUE( |
| NavigateToURL(new_shell, embedded_test_server()->GetURL("/title1.html"))); |
| EXPECT_EQ(1, controller->GetLastCommittedEntryIndex()); |
| { |
| TestNavigationObserver observer(new_shell->web_contents()); |
| EXPECT_TRUE(ExecuteScript(new_shell, "history.go(-1)")); |
| observer.Wait(); |
| |
| NavigationEntry* entry = controller->GetLastCommittedEntry(); |
| EXPECT_EQ(0, controller->GetLastCommittedEntryIndex()); |
| EXPECT_FALSE(entry->GetURL().SchemeIs(url::kDataScheme)); |
| EXPECT_FALSE(entry->GetURL().SchemeIs(url::kFileSystemScheme)); |
| EXPECT_TRUE(base::StartsWith( |
| entry->GetURL().spec(), |
| embedded_test_server()->GetURL("/server-redirect?").spec(), |
| base::CompareCase::SENSITIVE)); |
| EXPECT_EQ(entry->GetURL(), entry->GetVirtualURL()); |
| } |
| } |
| |
| } // namespace content |