Chromium Code Reviews| Index: content/browser/frame_host/data_url_navigation_browsertest.cc |
| diff --git a/content/browser/frame_host/data_url_navigation_browsertest.cc b/content/browser/frame_host/data_url_navigation_browsertest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..cf86b6ff02ce407c2eba8f825a1a44d0b2b7d1f4 |
| --- /dev/null |
| +++ b/content/browser/frame_host/data_url_navigation_browsertest.cc |
| @@ -0,0 +1,906 @@ |
| +// 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/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/strings/pattern.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" |
| +#include "ppapi/features/features.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 a data URL |
| +// navigation is blocked. |
| +const char kDataUrlBlockedPattern[] = |
| + "Not allowed to top-level navigate to resource:*"; |
| + |
| +// The message printed by the data URL when it successfully navigates. |
| +const char kDataUrlSuccessfulMessage[] = "NAVIGATION_SUCCESSFUL"; |
| + |
| +// A "Hello World" PDF encoded as a data URL. Source of this PDF: |
| +// ------------------------- |
| +// %PDF-1.7 |
| +// 1 0 obj << /Type /Page /Parent 3 0 R /Resources 5 0 R /Contents 2 0 R >> |
| +// endobj |
| +// 2 0 obj << /Length 51 >> |
| +// stream BT |
| +// /F1 12 Tf |
| +// 1 0 0 1 100 20 Tm |
| +// (Hello World)Tj |
| +// ET |
| +// endstream |
| +// endobj |
| +// 3 0 obj << /Type /Pages /Kids [ 1 0 R ] /Count 1 /MediaBox [ 0 0 300 50] >> |
| +// endobj |
| +// 4 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont/Arial >> |
| +// endobj |
| +// 5 0 obj << /ProcSet[/PDF/Text] /Font <</F1 4 0 R >> >> |
| +// endobj |
| +// 6 0 obj << /Type /Catalog /Pages 3 0 R >> |
| +// endobj |
| +// trailer << /Root 6 0 R >> |
| +// ------------------------- |
| +const char kPdfUrl[] = |
| + "data:application/pdf;base64,JVBERi0xLjcKMSAwIG9iaiA8PCAvVHlwZSAvUGFnZSAvUG" |
| + "FyZW50IDMgMCBSIC9SZXNvdXJjZXMgNSAwIFIgL0NvbnRlbnRzIDIgMCBSID4+CmVuZG9iagoy" |
| + "IDAgb2JqIDw8IC9MZW5ndGggNTEgPj4KIHN0cmVhbSBCVAogL0YxIDEyIFRmCiAxIDAgMCAxID" |
| + "EwMCAyMCBUbQogKEhlbGxvIFdvcmxkKVRqCiBFVAogZW5kc3RyZWFtCmVuZG9iagozIDAgb2Jq" |
| + "IDw8IC9UeXBlIC9QYWdlcyAvS2lkcyBbIDEgMCBSIF0gL0NvdW50IDEgL01lZGlhQm94IFsgMC" |
| + "AwIDMwMCA1MF0gPj4KZW5kb2JqCjQgMCBvYmogPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5" |
| + "cGUxIC9OYW1lIC9GMSAvQmFzZUZvbnQvQXJpYWwgPj4KZW5kb2JqCjUgMCBvYmogPDwgL1Byb2" |
| + "NTZXRbL1BERi9UZXh0XSAvRm9udCA8PC9GMSA0IDAgUiA+PiA+PgplbmRvYmoKNiAwIG9iaiA8" |
| + "PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMyAwIFIgPj4KZW5kb2JqCnRyYWlsZXIgPDwgL1Jvb3" |
| + "QgNiAwIFIgPj4K"; |
| + |
| +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. |
|
nasko
2017/04/05 23:55:18
nit: I'd rather build that functionality into the
meacer
2017/04/06 01:25:41
Filed https://crbug.com/708831
|
| +class DataURLWarningConsoleObserverDelegate : public WebContentsDelegate { |
| + public: |
| + DataURLWarningConsoleObserverDelegate( |
| + WebContents* web_contents, |
| + ExpectedNavigationStatus expected_navigation_status) |
| + : web_contents_(web_contents), |
| + success_filter_(expected_navigation_status == NAVIGATION_ALLOWED |
| + ? kDataUrlSuccessfulMessage |
| + : kDataUrlBlockedPattern), |
| + fail_filter_(expected_navigation_status == NAVIGATION_ALLOWED |
| + ? kDataUrlBlockedPattern |
| + : kDataUrlSuccessfulMessage), |
| + message_loop_runner_(new MessageLoopRunner()), |
|
nasko
2017/04/05 23:55:18
You need to pass IMMEDIATE for QuitMode, otherwise
meacer
2017/04/06 01:25:42
Done. I suppose we want the same for the other Con
nasko
2017/04/06 16:57:26
Probably, but I'd suggest doing it separately.
meacer
2017/04/06 18:14:19
Will do it as part of https://crbug.com/708831
|
| + saw_failure_message_(false) {} |
| + ~DataURLWarningConsoleObserverDelegate() override {} |
| + |
| + void Wait() { message_loop_runner_->Run(); } |
| + |
| + // 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_)) { |
| + saw_failure_message_ = true; |
| + message_loop_runner_->Quit(); |
| + } |
| + if (base::MatchPattern(ascii_message, success_filter_)) { |
| + message_loop_runner_->Quit(); |
| + } |
| + return false; |
| + } |
| + |
| + // Returns true if the observer encountered a message that matches |
| + // |fail_filter_|. |
| + bool saw_failure_message() const { return saw_failure_message_; } |
| + |
| + private: |
| + WebContents* web_contents_; |
| + const std::string success_filter_; |
| + const std::string fail_filter_; |
| + scoped_refptr<MessageLoopRunner> message_loop_runner_; |
| + bool saw_failure_message_; |
| +}; |
| + |
| +enum TestNavigationType { |
| + NO_PLZNAVIGATE, |
| + PLZNAVIGATE, |
| +}; |
|
nasko
2017/04/05 23:55:18
Why does this enum differ from the one below in te
meacer
2017/04/06 01:25:42
Looks like the extra comma caused clang-format to
nasko
2017/04/06 16:57:26
Can we remove that and make it match the one below
meacer
2017/04/06 18:14:19
I removed the test parameters from the last patchs
|
| + |
| +enum TestSitePerProcessType { NO_SITE_PER_PROCESS, SITE_PER_PROCESS }; |
| + |
| +#if BUILDFLAG(ENABLE_PLUGINS) |
| +// This class registers a fake PDF plugin handler so that data URL 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 DataUrlNavigationBrowserTest |
| + : public ContentBrowserTest, |
| + public testing::WithParamInterface< |
| + std::tuple<TestNavigationType, TestSitePerProcessType>> { |
|
nasko
2017/04/05 23:55:18
I wonder if you need all this complexity in your t
meacer
2017/04/06 01:25:42
I noticed that trybots did these, but I also found
|
| + public: |
| +#if BUILDFLAG(ENABLE_PLUGINS) |
| + DataUrlNavigationBrowserTest() |
| + : scoped_plugin_register_(PluginService::GetInstance()) {} |
| +#else |
| + DataUrlNavigationBrowserTest() {} |
| +#endif // BUILDFLAG(ENABLE_PLUGINS) |
| + |
| + protected: |
| + void SetUpCommandLine(base::CommandLine* command_line) override { |
| + if (IsPlzNavigateTest()) { |
| + command_line->AppendSwitch(switches::kEnableBrowserSideNavigation); |
| + } |
| + if (IsSitePerProcessTest()) { |
| + IsolateAllSitesForTesting(command_line); |
| + } |
| + } |
| + |
| + 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()); |
| + } |
| + |
| + // Adds an iframe to |rfh| pointing to |url|. |
| + void AddIFrame(RenderFrameHost* rfh, const GURL& url) { |
| + const std::string javascript = base::StringPrintf( |
| + "f = document.createElement('iframe'); f.src = '%s';" |
| + "document.body.appendChild(f);", |
| + url.spec().c_str()); |
| + bool iframe_loaded = false; |
| + EXPECT_TRUE(ExecuteScriptAndExtractBool(rfh, javascript, &iframe_loaded)); |
| + EXPECT_TRUE(iframe_loaded); |
|
nasko
2017/04/05 23:55:18
Is the goal of this code to wait until the load ha
meacer
2017/04/06 01:25:42
Done.
|
| + } |
| + |
| + // Runs |javascript| on the first child frame and checks for a navigation. |
| + void TestNavigationFromFrame( |
| + const std::string& javascript, |
| + ExpectedNavigationStatus expected_navigation_status) { |
| + RenderFrameHost* child = |
| + ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| + ASSERT_TRUE(child); |
| + if (IsSitePerProcessTest()) { |
| + ASSERT_TRUE(child->IsCrossProcessSubframe()); |
| + } |
| + ExecuteScriptAndCheckNavigation(child, 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 (IsSitePerProcessTest()) { |
| + ASSERT_TRUE(child->IsCrossProcessSubframe()); |
| + } |
| + ExecuteScriptAndCheckNavigationDownload(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& javascript, |
| + ExpectedNavigationStatus expected_navigation_status) { |
| + RenderFrameHost* child = |
| + ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| + ASSERT_TRUE(child); |
| + if (IsSitePerProcessTest()) { |
| + ASSERT_TRUE(child->IsCrossProcessSubframe()); |
| + } |
| + ExecuteScriptAndCheckPDFNavigation(child, javascript, |
| + expected_navigation_status); |
| + } |
| + |
| + // Same as TestNavigationFromFrame, but instead of navigating, the child frame |
| + // tries to open a new window with a data URL. |
| + void TestWindowOpenFromFrame( |
| + const std::string& javascript, |
| + ExpectedNavigationStatus expected_navigation_status) { |
| + RenderFrameHost* child = |
| + ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); |
| + if (IsSitePerProcessTest()) { |
| + ASSERT_TRUE(child->IsCrossProcessSubframe()); |
| + } |
| + ExecuteScriptAndCheckWindowOpen(child, 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 kDataUrlSuccessfulMessage. |
| + void ExecuteScriptAndCheckNavigation( |
| + RenderFrameHost* rfh, |
| + const std::string& javascript, |
| + ExpectedNavigationStatus expected_navigation_status) { |
| + const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); |
| + const std::string expected_message; |
| + |
| + DataURLWarningConsoleObserverDelegate console_delegate( |
| + shell()->web_contents(), expected_navigation_status); |
| + shell()->web_contents()->SetDelegate(&console_delegate); |
| + |
| + TestNavigationObserver navigation_observer(shell()->web_contents(), 1); |
|
nasko
2017/04/05 23:55:19
nit: I think the default constructor uses 1 naviga
meacer
2017/04/06 01:25:42
Done.
|
| + EXPECT_TRUE(ExecuteScript(rfh, javascript)); |
| + console_delegate.Wait(); |
| + EXPECT_FALSE(console_delegate.saw_failure_message()); |
| + shell()->web_contents()->SetDelegate(nullptr); |
| + |
| + switch (expected_navigation_status) { |
| + case NAVIGATION_ALLOWED: |
| + navigation_observer.Wait(); |
| + // The new page should have a data URL. |
| + EXPECT_TRUE( |
| + shell()->web_contents()->GetURL().SchemeIs(url::kDataScheme)); |
| + EXPECT_TRUE(shell() |
| + ->web_contents() |
| + ->GetController() |
| + .GetLastCommittedEntry() |
| + ->GetURL() |
|
nasko
2017/04/05 23:55:18
All that can be done with WebContents::GetLastComm
meacer
2017/04/06 01:25:42
Done.
|
| + .SchemeIs(url::kDataScheme)); |
| + EXPECT_TRUE(navigation_observer.last_navigation_url().SchemeIs( |
| + url::kDataScheme)); |
| + EXPECT_TRUE(navigation_observer.last_navigation_succeeded()); |
| + break; |
| + |
| + case NAVIGATION_BLOCKED: |
| + // Original page shouldn't navigate away. |
| + EXPECT_EQ(original_url, shell()->web_contents()->GetURL()); |
| + EXPECT_EQ(original_url, shell() |
| + ->web_contents() |
| + ->GetController() |
| + .GetLastCommittedEntry() |
| + ->GetURL()); |
| + EXPECT_FALSE(navigation_observer.last_navigation_succeeded()); |
| + break; |
| + |
| + default: |
| + NOTREACHED(); |
| + } |
| + } |
| + |
| + // 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& 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() |
| + : kDataUrlBlockedPattern; |
| + |
| + 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(), 1); |
| + 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 a data URL. |
| + EXPECT_TRUE( |
| + shell()->web_contents()->GetURL().SchemeIs(url::kDataScheme)); |
| + EXPECT_TRUE(shell() |
| + ->web_contents() |
| + ->GetController() |
| + .GetLastCommittedEntry() |
| + ->GetURL() |
| + .SchemeIs(url::kDataScheme)); |
| + EXPECT_TRUE(navigation_observer.last_navigation_url().SchemeIs( |
| + url::kDataScheme)); |
| + EXPECT_TRUE(navigation_observer.last_navigation_succeeded()); |
| + break; |
| + |
| + case NAVIGATION_BLOCKED: |
| + // Original page shouldn't navigate away. |
| + EXPECT_EQ(original_url, shell()->web_contents()->GetURL()); |
| + EXPECT_EQ(original_url, shell() |
| + ->web_contents() |
| + ->GetController() |
| + .GetLastCommittedEntry() |
| + ->GetURL()); |
| + 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). |
|
nasko
2017/04/05 23:55:18
Why not? Once you get the shell from the observer,
meacer
2017/04/06 01:25:42
Doesn't work unfortunately. The console message ar
nasko
2017/04/06 16:57:26
Hmm, maybe we can follow up here once I'm back, bu
meacer
2017/04/06 18:14:19
An alternative is to print the logging message to
|
| + void ExecuteScriptAndCheckWindowOpen( |
| + RenderFrameHost* rfh, |
| + 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( |
| + url::kDataScheme)); |
| + 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; |
| + EXPECT_TRUE(ExecuteScript(rfh, javascript)); |
| + Shell* new_shell = new_shell_observer.GetShell(); |
| + |
| + DownloadManager* download_manager = BrowserContext::GetDownloadManager( |
| + new_shell->web_contents()->GetBrowserContext()); |
|
nasko
2017/04/05 23:55:18
nit: The BrowserContext is the same across all win
meacer
2017/04/06 01:25:42
Doesn't change the test much, but moved above.
|
| + 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()->GetURL().spec().empty()); |
|
nasko
2017/04/05 23:55:18
nit: GetLastCommittedURL, GetURL is deprecated.
meacer
2017/04/06 01:25:42
Removed GetURL from here and elsewhere.
|
| + // 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()->GetURL()); |
| + EXPECT_EQ(original_url, shell() |
| + ->web_contents() |
| + ->GetController() |
| + .GetLastCommittedEntry() |
| + ->GetURL()); |
| + } |
| + |
| + // Executes |javascript| on |rfh| and waits for a download to be started. |
| + void ExecuteScriptAndCheckNavigationDownload(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()->GetURL()); |
| + EXPECT_EQ(original_url, shell() |
| + ->web_contents() |
| + ->GetController() |
| + .GetLastCommittedEntry() |
| + ->GetURL()); |
| + } |
| + |
| + // data URL form of the file at content/test/data/data_url_navigations.html |
| + GURL data_url() const { return data_url_; } |
| + |
| + base::ScopedTempDir downloads_directory_; |
|
nasko
2017/04/05 23:55:18
Why does this need to be protected, can't it be pr
meacer
2017/04/06 01:25:41
Done.
|
| + |
| + private: |
| + bool IsPlzNavigateTest() const { |
| + return std::get<0>(GetParam()) == PLZNAVIGATE; |
| + } |
| + |
| + bool IsSitePerProcessTest() const { |
| + return std::get<1>(GetParam()) == SITE_PER_PROCESS; |
| + } |
| + |
| +#if BUILDFLAG(ENABLE_PLUGINS) |
| + ScopedPluginRegister scoped_plugin_register_; |
| +#endif // BUILDFLAG(ENABLE_PLUGINS) |
| + |
| + GURL data_url_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DataUrlNavigationBrowserTest); |
| +}; |
| + |
| +INSTANTIATE_TEST_CASE_P( |
| + , |
| + DataUrlNavigationBrowserTest, |
| + ::testing::Combine(::testing::Values(NO_PLZNAVIGATE, PLZNAVIGATE), |
| + ::testing::Values(NO_SITE_PER_PROCESS, |
| + SITE_PER_PROCESS))); |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// data URLs with HTML mimetype |
| +// |
| +// Tests that a direct navigation to a data URL doesn't show a console warning |
|
nasko
2017/04/05 23:55:18
nit: s/direct/browser-initiated/
meacer
2017/04/06 01:25:42
Done.
|
| +// and is not blocked. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, BrowserInitiated_Allow) { |
| + DataURLWarningConsoleObserverDelegate console_delegate( |
| + shell()->web_contents(), NAVIGATION_ALLOWED); |
| + shell()->web_contents()->SetDelegate(&console_delegate); |
| + |
| + NavigateToURL(shell(), GURL("data:text/" |
| + "html,<html><script>console.log('NAVIGATION_" |
| + "SUCCESSFUL');</script>")); |
| + console_delegate.Wait(); |
| + shell()->web_contents()->SetDelegate(nullptr); |
| + |
| + EXPECT_TRUE(shell()->web_contents()->GetURL().SchemeIs(url::kDataScheme)); |
| + EXPECT_TRUE(shell() |
| + ->web_contents() |
| + ->GetController() |
| + .GetLastCommittedEntry() |
| + ->GetURL() |
| + .SchemeIs(url::kDataScheme)); |
| +} |
| + |
| +// Tests that a content initiated navigation to a data URL is blocked. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, HTML_Navigation_Block) { |
| + NavigateToURL(shell(), |
| + embedded_test_server()->GetURL("/data_url_navigations.html")); |
| + ExecuteScriptAndCheckNavigation( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('navigate-top-frame-to-html').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Tests that a content initiated navigation to a data URL is allowed if |
| +// blocking is disabled with a feature flag. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + HTML_Navigation_Allow_FeatureFlag) { |
| + base::test::ScopedFeatureList feature_list; |
| + feature_list.InitAndEnableFeature(features::kAllowInsecureDataUrlNavigations); |
| + NavigateToURL(shell(), |
| + embedded_test_server()->GetURL("/data_url_navigations.html")); |
| + ExecuteScriptAndCheckNavigation( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('navigate-top-frame-to-html').click()", |
| + NAVIGATION_ALLOWED); |
| +} |
| + |
| +// Tests that a window.open to a data URL with HTML mime type is blocked. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, HTML_WindowOpen_Block) { |
| + NavigateToURL(shell(), |
| + embedded_test_server()->GetURL("/data_url_navigations.html")); |
| + ExecuteScriptAndCheckWindowOpen( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('window-open-html').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Tests that a form post to a data URL with HTML mime type is blocked. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, HTML_FormPost_Block) { |
| + NavigateToURL(shell(), |
| + embedded_test_server()->GetURL("/data_url_navigations.html")); |
| + ExecuteScriptAndCheckNavigation( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('form-post-to-html').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Tests that navigating the main frame to a data URL with HTML mimetype from a |
| +// subframe is blocked. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + HTML_NavigationFromFrame_Block) { |
| + // This test fails and is disabled in site-per-process + no plznavigate mode. |
| + // request->originDocument is null in FrameLoader::prepareForRequest, |
| + // allowing the navigation by default. See https://crbug.com/647839 |
| + if (AreAllSitesIsolatedForTesting() && !IsBrowserSideNavigationEnabled()) { |
| + return; |
| + } |
| + |
| + 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")); |
| + |
| + TestNavigationFromFrame( |
| + "document.getElementById('navigate-top-frame-to-html').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Tests that opening a new data URL window from a subframe is blocked. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + HTML_WindowOpenFromFrame_Block) { |
| + 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")); |
| + |
| + TestWindowOpenFromFrame("document.getElementById('window-open-html').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Tests that navigation to a data URL is blocked even if the top frame is |
| +// already a data URL. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + HTML_Navigation_DataToData_Block) { |
| + NavigateToURL(shell(), data_url()); |
| + ExecuteScriptAndCheckNavigation( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('navigate-top-frame-to-html').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Tests that a form post to a data URL with HTML mime type is blocked even if |
| +// the top frame is already a data URL. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + HTML_FormPost_DataToData_Block) { |
| + NavigateToURL(shell(), data_url()); |
| + ExecuteScriptAndCheckNavigation( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('form-post-to-html').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Tests that navigating the top frame to a data URL with HTML mimetype is |
| +// blocked even if the top frame is already a data URL. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + HTML_NavigationFromFrame_TopFrameIsDataURL_Block) { |
| + // This test fails and is disabled in site-per-process + no plznavigate mode. |
| + // request->originDocument is null in FrameLoader::prepareForRequest, |
| + // allowing the navigation by default. See https://crbug.com/647839 |
| + if (AreAllSitesIsolatedForTesting() && !IsBrowserSideNavigationEnabled()) { |
| + return; |
| + } |
| + |
| + const GURL top_url( |
| + base::StringPrintf("data:text/html, <iframe src='%s'></iframe>", |
| + embedded_test_server() |
| + ->GetURL("/data_url_navigations.html") |
| + .spec() |
| + .c_str())); |
| + NavigateToURL(shell(), top_url); |
| + |
| + TestNavigationFromFrame( |
| + "document.getElementById('navigate-top-frame-to-html').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Tests that opening a new window with a data URL with HTML mimetype is blocked |
| +// even if the top frame is already a data URL. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + HTML_WindowOpenFromFrame_TopFrameIsDataURL_Block) { |
| + const GURL top_url( |
| + base::StringPrintf("data:text/html, <iframe src='%s'></iframe>", |
| + embedded_test_server() |
| + ->GetURL("/data_url_navigations.html") |
| + .spec() |
| + .c_str())); |
| + NavigateToURL(shell(), top_url); |
| + |
| + TestWindowOpenFromFrame("document.getElementById('window-open-html').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| +//////////////////////////////////////////////////////////////////////////////// |
|
nasko
2017/04/05 23:55:19
nit: Empty line above this one.
meacer
2017/04/06 01:25:42
Done.
|
| +// data URLs with octet-stream mimetype (binary) |
| +// |
| +// Test that window.open to a data URL results in a download if the URL has a |
| +// binary mime type. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + OctetStream_WindowOpen_Download) { |
| + NavigateToURL(shell(), |
| + embedded_test_server()->GetURL("/data_url_navigations.html")); |
| + ExecuteScriptAndCheckWindowOpenDownload( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('window-open-octetstream').click()"); |
| +} |
| + |
| +// Test that a navigation to a data URL results in a download if the URL has a |
| +// binary mime type. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + OctetStream_Navigation_Download) { |
| + NavigateToURL(shell(), |
| + embedded_test_server()->GetURL("/data_url_navigations.html")); |
| + ExecuteScriptAndCheckNavigationDownload( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('navigate-top-frame-to-octetstream').click()"); |
| +} |
| + |
| +// Test that a form post to a data URL results in a download if the URL has a |
| +// binary mime type. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + OctetStream_FormPost_Download) { |
| + NavigateToURL(shell(), |
| + embedded_test_server()->GetURL("/data_url_navigations.html")); |
| + ExecuteScriptAndCheckNavigationDownload( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('form-post-to-octetstream').click()"); |
| +} |
| + |
| +// Tests that navigating the main frame from a subframe results in a download |
| +// if the URL has a binary mimetype. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + OctetStream_NavigationFromFrame_Download) { |
| + 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()"); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// data URLs with unknown mimetype |
| +// |
| +// Test that window.open to a data URL results in a download if the URL has an |
| +// unknown mime type. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + UnknownMimeType_WindowOpen_Download) { |
| + NavigateToURL(shell(), |
| + embedded_test_server()->GetURL("/data_url_navigations.html")); |
| + ExecuteScriptAndCheckWindowOpenDownload( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('window-open-unknown-mimetype').click()"); |
| +} |
| + |
| +// Test that a navigation to a data URL results in a download if the URL has an |
| +// unknown mime type. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + UnknownMimeType_Navigation_Download) { |
| + NavigateToURL(shell(), |
| + embedded_test_server()->GetURL("/data_url_navigations.html")); |
| + ExecuteScriptAndCheckNavigationDownload( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('navigate-top-" |
| + "frame-to-unknown-mimetype').click()"); |
| +} |
| + |
| +// Test that a form post to a data URL results in a download if the URL has an |
| +// unknown mime type. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + UnknownMimeType_FormPost_Download) { |
| + NavigateToURL(shell(), |
| + embedded_test_server()->GetURL("/data_url_navigations.html")); |
| + ExecuteScriptAndCheckNavigationDownload( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('form-post-to-unknown-mimetype').click()"); |
| +} |
| + |
| +// Tests that navigating the main frame from a subframe results in a download |
| +// if the URL has an unknown mimetype. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + UnknownMimeType_NavigationFromFrame_Download) { |
| + 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(" |
| + ")"); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// data URLs with PDF mimetype |
| +// |
| +// Tests that a direct navigation to a data URL with PDF mime type is allowed. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + PDF_DirectNavigation_Allow) { |
| + TestNavigationObserver observer(shell()->web_contents()); |
| + NavigateToURL(shell(), GURL(kPdfUrl)); |
| + EXPECT_EQ(GURL(kPdfUrl), observer.last_navigation_url()); |
| + EXPECT_TRUE(observer.last_navigation_succeeded()); |
| + EXPECT_TRUE(shell()->web_contents()->GetURL().SchemeIs(url::kDataScheme)); |
| + EXPECT_TRUE(shell() |
| + ->web_contents() |
| + ->GetController() |
| + .GetLastCommittedEntry() |
| + ->GetURL() |
| + .SchemeIs(url::kDataScheme)); |
| +} |
| + |
| +// Tests that a window.open to a data URL is blocked if the data URL has a |
| +// mime type that will be handled by a plugin (PDF in this case). |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, PDF_WindowOpen_Block) { |
| + NavigateToURL(shell(), |
| + embedded_test_server()->GetURL("/data_url_navigations.html")); |
| + ExecuteScriptAndCheckWindowOpen( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('window-open-pdf').click()", NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Test that a navigation to a data URL is blocked if the data URL has a mime |
| +// type that will be handled by a plugin (PDF in this case). |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, PDF_Navigation_Block) { |
| + NavigateToURL(shell(), |
| + embedded_test_server()->GetURL("/data_url_navigations.html")); |
| + ExecuteScriptAndCheckPDFNavigation( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('navigate-top-frame-to-pdf').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Test that a navigation to a data URL is blocked if the data URL has a mime |
|
nasko
2017/04/05 23:55:19
nit: Did you mean s/navigation/form post/?
meacer
2017/04/06 01:25:42
Done.
|
| +// type that will be handled by a plugin (PDF in this case). |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, PDF_FormPost_Block) { |
| + NavigateToURL(shell(), |
| + embedded_test_server()->GetURL("/data_url_navigations.html")); |
| + ExecuteScriptAndCheckPDFNavigation( |
| + shell()->web_contents()->GetMainFrame(), |
| + "document.getElementById('form-post-to-pdf').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Tests that navigating the main frame to a data URL with PDF mimetype from a |
| +// subframe is blocked. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + PDF_NavigationFromFrame_Block) { |
| + 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")); |
| + |
| + TestPDFNavigationFromFrame( |
| + "document.getElementById('navigate-top-frame-to-pdf').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Tests that opening a window with a data URL with PDF mimetype from a |
| +// subframe is blocked. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + PDF_WindowOpenFromFrame_Block) { |
| + 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")); |
| + |
| + TestWindowOpenFromFrame("document.getElementById('window-open-pdf').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Tests that navigating the top frame to a data URL with PDF mimetype from a |
| +// subframe is blocked even if the top frame is already a data URL. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + PDF_NavigationFromFrame_TopFrameIsDataURL_Block) { |
| + const GURL top_url( |
| + base::StringPrintf("data:text/html, <iframe src='%s'></iframe>", |
| + embedded_test_server() |
| + ->GetURL("/data_url_navigations.html") |
| + .spec() |
| + .c_str())); |
| + NavigateToURL(shell(), top_url); |
| + |
| + TestPDFNavigationFromFrame( |
| + "document.getElementById('navigate-top-frame-to-pdf').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +// Tests that opening a window with a data URL with PDF mimetype from a |
| +// subframe is blocked even if the top frame is already a data URL. |
| +IN_PROC_BROWSER_TEST_P(DataUrlNavigationBrowserTest, |
| + PDF_WindowOpenFromFrame_TopFrameIsDataURL_Block) { |
| + const GURL top_url( |
| + base::StringPrintf("data:text/html, <iframe src='%s'></iframe>", |
| + embedded_test_server() |
| + ->GetURL("/data_url_navigations.html") |
| + .spec() |
| + .c_str())); |
| + NavigateToURL(shell(), top_url); |
| + |
| + TestWindowOpenFromFrame("document.getElementById('window-open-pdf').click()", |
| + NAVIGATION_BLOCKED); |
| +} |
| + |
| +} // content |