blob: 26d3561af6c3d7c5a73835612ff2a623a5e2f69a [file] [log] [blame]
// Copyright 2019 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/files/file_path.h"
#include "base/files/file_util.h"
#include "base/optional.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "content/browser/web_package/bundled_exchanges_utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.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/shell/browser/shell.h"
#include "net/base/filename_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#if defined(OS_ANDROID)
#include "base/android/content_uri_utils.h"
#endif // OS_ANDROID
namespace content {
namespace {
// "%2F" is treated as an invalid character for file URLs.
constexpr char kInvalidFileUrl[] = "file:///tmp/test%2F/a.wbn";
constexpr char kTestPageUrl[] = "https://test.example.org/";
constexpr char kTestPage1Url[] = "https://test.example.org/page1.html";
constexpr char kTestPage2Url[] = "https://test.example.org/page2.html";
constexpr char kTestPageForHashUrl[] =
"https://test.example.org/hash.html#hello";
base::FilePath GetTestDataPath(base::StringPiece file) {
base::FilePath test_data_dir;
CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
return test_data_dir
.Append(base::FilePath(
FILE_PATH_LITERAL("content/test/data/bundled_exchanges")))
.AppendASCII(file);
}
#if defined(OS_ANDROID)
GURL CopyFileAndGetContentUri(const base::FilePath& file) {
base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath tmp_dir;
CHECK(base::GetTempDir(&tmp_dir));
// The directory name "bundled_exchanges" must be kept in sync with
// content/shell/android/browsertests_apk/res/xml/file_paths.xml
base::FilePath tmp_wbn_dir = tmp_dir.AppendASCII("bundled_exchanges");
CHECK(base::CreateDirectoryAndGetError(tmp_wbn_dir, nullptr));
base::FilePath tmp_dir_in_tmp_wbn_dir;
CHECK(
base::CreateTemporaryDirInDir(tmp_wbn_dir, "", &tmp_dir_in_tmp_wbn_dir));
base::FilePath temp_file = tmp_dir_in_tmp_wbn_dir.Append(file.BaseName());
CHECK(base::CopyFile(file, temp_file));
return GURL(base::GetContentUriFromFilePath(temp_file).value());
}
#endif // OS_ANDROID
class DownloadObserver : public DownloadManager::Observer {
public:
explicit DownloadObserver(DownloadManager* manager) : manager_(manager) {
manager_->AddObserver(this);
}
~DownloadObserver() override { manager_->RemoveObserver(this); }
void WaitUntilDownloadCreated() { run_loop_.Run(); }
const GURL& observed_url() const { return url_; }
// content::DownloadManager::Observer implementation.
void OnDownloadCreated(content::DownloadManager* manager,
download::DownloadItem* item) override {
url_ = item->GetURL();
run_loop_.Quit();
}
private:
DownloadManager* manager_;
base::RunLoop run_loop_;
GURL url_;
DISALLOW_COPY_AND_ASSIGN(DownloadObserver);
};
class BundledExchangesBrowserTestBase : public ContentBrowserTest {
protected:
BundledExchangesBrowserTestBase() = default;
~BundledExchangesBrowserTestBase() override = default;
void NavigateToBundleAndWaitForReady(const GURL& test_data_url,
const GURL& expected_commit_url) {
base::string16 expected_title = base::ASCIIToUTF16("Ready");
TitleWatcher title_watcher(shell()->web_contents(), expected_title);
EXPECT_TRUE(NavigateToURL(shell()->web_contents(), test_data_url,
expected_commit_url));
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
void RunTestScript(const std::string& script) {
EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
"loadScript('" + script + "');"));
base::string16 ok = base::ASCIIToUTF16("OK");
TitleWatcher title_watcher(shell()->web_contents(), ok);
title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16("FAIL"));
EXPECT_EQ(ok, title_watcher.WaitAndGetTitle());
}
void ExecuteScriptAndWaitForTitle(const std::string& script,
const std::string& title) {
base::string16 title16 = base::ASCIIToUTF16(title);
TitleWatcher title_watcher(shell()->web_contents(), title16);
EXPECT_TRUE(ExecuteScript(shell()->web_contents(), script));
EXPECT_EQ(title16, title_watcher.WaitAndGetTitle());
}
void NavigateToURLAndWaitForTitle(const GURL& url, const std::string& title) {
ExecuteScriptAndWaitForTitle(
base::StringPrintf("location.href = '%s';", url.spec().c_str()), title);
}
private:
DISALLOW_COPY_AND_ASSIGN(BundledExchangesBrowserTestBase);
};
class TestBrowserClient : public ContentBrowserClient {
public:
TestBrowserClient() = default;
~TestBrowserClient() override = default;
bool CanAcceptUntrustedExchangesIfNeeded() override { return true; }
private:
DISALLOW_COPY_AND_ASSIGN(TestBrowserClient);
};
class FinishNavigationObserver : public WebContentsObserver {
public:
explicit FinishNavigationObserver(WebContents* contents,
base::OnceClosure done_closure)
: WebContentsObserver(contents), done_closure_(std::move(done_closure)) {}
void DidFinishNavigation(NavigationHandle* navigation_handle) override {
error_code_ = navigation_handle->GetNetErrorCode();
std::move(done_closure_).Run();
}
const base::Optional<net::Error>& error_code() const { return error_code_; }
private:
base::OnceClosure done_closure_;
base::Optional<net::Error> error_code_;
DISALLOW_COPY_AND_ASSIGN(FinishNavigationObserver);
};
ContentBrowserClient* MaybeSetBrowserClientForTesting(
ContentBrowserClient* browser_client) {
#if defined(OS_ANDROID)
// TODO(crbug.com/864403): It seems that we call unsupported Android APIs on
// KitKat when we set a ContentBrowserClient. Don't call such APIs and make
// this test available on KitKat.
int32_t major_version = 0, minor_version = 0, bugfix_version = 0;
base::SysInfo::OperatingSystemVersionNumbers(&major_version, &minor_version,
&bugfix_version);
if (major_version < 5)
return nullptr;
#endif // defined(OS_ANDROID)
return SetBrowserClientForTesting(browser_client);
}
} // namespace
class InvalidTrustableBundledExchangesFileUrlBrowserTest
: public ContentBrowserTest {
protected:
InvalidTrustableBundledExchangesFileUrlBrowserTest() = default;
~InvalidTrustableBundledExchangesFileUrlBrowserTest() override = default;
void SetUpOnMainThread() override {
ContentBrowserTest::SetUpOnMainThread();
original_client_ = MaybeSetBrowserClientForTesting(&browser_client_);
}
void TearDownOnMainThread() override {
ContentBrowserTest::TearDownOnMainThread();
if (original_client_)
SetBrowserClientForTesting(original_client_);
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(switches::kTrustableBundledExchangesFileUrl,
kInvalidFileUrl);
}
ContentBrowserClient* original_client_ = nullptr;
private:
TestBrowserClient browser_client_;
DISALLOW_COPY_AND_ASSIGN(InvalidTrustableBundledExchangesFileUrlBrowserTest);
};
IN_PROC_BROWSER_TEST_F(InvalidTrustableBundledExchangesFileUrlBrowserTest,
NoCrashOnNavigation) {
// Don't run the test if we couldn't override BrowserClient. It happens only
// on Android Kitkat or older systems.
if (!original_client_)
return;
base::RunLoop run_loop;
FinishNavigationObserver finish_navigation_observer(shell()->web_contents(),
run_loop.QuitClosure());
EXPECT_FALSE(NavigateToURL(shell()->web_contents(), GURL(kInvalidFileUrl)));
run_loop.Run();
ASSERT_TRUE(finish_navigation_observer.error_code());
EXPECT_EQ(net::ERR_INVALID_URL, *finish_navigation_observer.error_code());
}
class BundledExchangesTrustableFileBrowserTestBase
: public BundledExchangesBrowserTestBase {
protected:
BundledExchangesTrustableFileBrowserTestBase() = default;
~BundledExchangesTrustableFileBrowserTestBase() override = default;
void SetUp() override { BundledExchangesBrowserTestBase::SetUp(); }
void SetUpOnMainThread() override {
BundledExchangesBrowserTestBase::SetUpOnMainThread();
original_client_ = MaybeSetBrowserClientForTesting(&browser_client_);
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(switches::kTrustableBundledExchangesFileUrl,
test_data_url().spec());
}
void TearDownOnMainThread() override {
BundledExchangesBrowserTestBase::TearDownOnMainThread();
if (original_client_)
SetBrowserClientForTesting(original_client_);
}
const GURL& test_data_url() const { return test_data_url_; }
ContentBrowserClient* original_client_ = nullptr;
GURL test_data_url_;
private:
TestBrowserClient browser_client_;
DISALLOW_COPY_AND_ASSIGN(BundledExchangesTrustableFileBrowserTestBase);
};
enum class TestFilePathMode {
kNormalFilePath,
#if defined(OS_ANDROID)
kContentURI,
#endif // OS_ANDROID
};
class BundledExchangesTrustableFileBrowserTest
: public testing::WithParamInterface<TestFilePathMode>,
public BundledExchangesTrustableFileBrowserTestBase {
protected:
BundledExchangesTrustableFileBrowserTest() {
if (GetParam() == TestFilePathMode::kNormalFilePath) {
test_data_url_ = net::FilePathToFileURL(
GetTestDataPath("bundled_exchanges_browsertest.wbn"));
return;
}
#if defined(OS_ANDROID)
DCHECK_EQ(TestFilePathMode::kContentURI, GetParam());
test_data_url_ = CopyFileAndGetContentUri(
GetTestDataPath("bundled_exchanges_browsertest.wbn"));
#endif // OS_ANDROID
}
~BundledExchangesTrustableFileBrowserTest() override = default;
private:
DISALLOW_COPY_AND_ASSIGN(BundledExchangesTrustableFileBrowserTest);
};
IN_PROC_BROWSER_TEST_P(BundledExchangesTrustableFileBrowserTest,
TrustableBundledExchangesFile) {
// Don't run the test if we couldn't override BrowserClient. It happens only
// on Android Kitkat or older systems.
if (!original_client_)
return;
NavigateToBundleAndWaitForReady(test_data_url(), GURL(kTestPageUrl));
}
IN_PROC_BROWSER_TEST_P(BundledExchangesTrustableFileBrowserTest, RangeRequest) {
// Don't run the test if we couldn't override BrowserClient. It happens only
// on Android Kitkat or older systems.
if (!original_client_)
return;
NavigateToBundleAndWaitForReady(test_data_url(), GURL(kTestPageUrl));
RunTestScript("test-range-request.js");
}
IN_PROC_BROWSER_TEST_P(BundledExchangesTrustableFileBrowserTest, Navigations) {
// Don't run the test if we couldn't override BrowserClient. It happens only
// on Android Kitkat or older systems.
if (!original_client_)
return;
NavigateToBundleAndWaitForReady(test_data_url(), GURL(kTestPageUrl));
// Move to page 1.
NavigateToURLAndWaitForTitle(GURL(kTestPage1Url), "Page 1");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
GURL(kTestPage1Url));
// Move to page 2.
NavigateToURLAndWaitForTitle(GURL(kTestPage2Url), "Page 2");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
GURL(kTestPage2Url));
// Back to page 1.
ExecuteScriptAndWaitForTitle("history.back();", "Page 1");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
GURL(kTestPage1Url));
// Back to the initial page.
ExecuteScriptAndWaitForTitle("history.back();", "Ready");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), GURL(kTestPageUrl));
// Move to page 1.
ExecuteScriptAndWaitForTitle("history.forward();", "Page 1");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
GURL(kTestPage1Url));
// Reload.
ExecuteScriptAndWaitForTitle("document.title = 'reset';", "reset");
ExecuteScriptAndWaitForTitle("location.reload();", "Page 1");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
GURL(kTestPage1Url));
// Move to page 2.
ExecuteScriptAndWaitForTitle("history.forward();", "Page 2");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
GURL(kTestPage2Url));
}
IN_PROC_BROWSER_TEST_P(BundledExchangesTrustableFileBrowserTest,
NavigationWithHash) {
// Don't run the test if we couldn't override BrowserClient. It happens only
// on Android Kitkat or older systems.
if (!original_client_)
return;
NavigateToBundleAndWaitForReady(test_data_url(), GURL(kTestPageUrl));
NavigateToURLAndWaitForTitle(GURL(kTestPageForHashUrl), "#hello");
}
INSTANTIATE_TEST_SUITE_P(BundledExchangesTrustableFileBrowserTests,
BundledExchangesTrustableFileBrowserTest,
testing::Values(TestFilePathMode::kNormalFilePath
#if defined(OS_ANDROID)
,
TestFilePathMode::kContentURI
#endif // OS_ANDROID
));
class BundledExchangesTrustableFileNotFoundBrowserTest
: public BundledExchangesTrustableFileBrowserTestBase {
protected:
BundledExchangesTrustableFileNotFoundBrowserTest() {
base::FilePath test_data_dir;
CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
test_data_url_ =
net::FilePathToFileURL(test_data_dir.AppendASCII("not_found"));
}
~BundledExchangesTrustableFileNotFoundBrowserTest() override = default;
};
IN_PROC_BROWSER_TEST_F(BundledExchangesTrustableFileNotFoundBrowserTest,
NotFound) {
// Don't run the test if we couldn't override BrowserClient. It happens only
// on Android Kitkat or older systems.
if (!original_client_)
return;
WebContents* web_contents = shell()->web_contents();
ConsoleObserverDelegate console_delegate(web_contents, "*");
web_contents->SetDelegate(&console_delegate);
base::RunLoop run_loop;
FinishNavigationObserver finish_navigation_observer(web_contents,
run_loop.QuitClosure());
EXPECT_FALSE(NavigateToURL(web_contents, test_data_url()));
run_loop.Run();
ASSERT_TRUE(finish_navigation_observer.error_code());
EXPECT_EQ(net::ERR_INVALID_BUNDLED_EXCHANGES,
*finish_navigation_observer.error_code());
if (console_delegate.messages().empty())
console_delegate.Wait();
EXPECT_FALSE(console_delegate.messages().empty());
EXPECT_EQ("Failed to read metadata of Web Bundle file: FILE_ERROR_FAILED",
console_delegate.message());
}
class BundledExchangesFileBrowserTest
: public testing::WithParamInterface<TestFilePathMode>,
public BundledExchangesBrowserTestBase {
protected:
BundledExchangesFileBrowserTest() = default;
~BundledExchangesFileBrowserTest() override = default;
void SetUp() override {
feature_list_.InitWithFeatures({features::kWebBundles}, {});
BundledExchangesBrowserTestBase::SetUp();
}
GURL GetTestUrlForFile(base::FilePath file_path) const {
switch (GetParam()) {
case TestFilePathMode::kNormalFilePath:
return net::FilePathToFileURL(file_path);
#if defined(OS_ANDROID)
case TestFilePathMode::kContentURI:
return CopyFileAndGetContentUri(file_path);
#endif // OS_ANDROID
}
}
private:
base::test::ScopedFeatureList feature_list_;
DISALLOW_COPY_AND_ASSIGN(BundledExchangesFileBrowserTest);
};
IN_PROC_BROWSER_TEST_P(BundledExchangesFileBrowserTest, BasicNavigation) {
const GURL test_data_url =
GetTestUrlForFile(GetTestDataPath("bundled_exchanges_browsertest.wbn"));
NavigateToBundleAndWaitForReady(
test_data_url,
bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
test_data_url, GURL(kTestPageUrl)));
}
IN_PROC_BROWSER_TEST_P(BundledExchangesFileBrowserTest, Navigations) {
const GURL test_data_url =
GetTestUrlForFile(GetTestDataPath("bundled_exchanges_browsertest.wbn"));
NavigateToBundleAndWaitForReady(
test_data_url,
bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
test_data_url, GURL(kTestPageUrl)));
// Move to page 1.
NavigateToURLAndWaitForTitle(GURL(kTestPage1Url), "Page 1");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
test_data_url, GURL(kTestPage1Url)));
// Move to page 2.
NavigateToURLAndWaitForTitle(GURL(kTestPage2Url), "Page 2");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
test_data_url, GURL(kTestPage2Url)));
// Back to page 1.
ExecuteScriptAndWaitForTitle("history.back();", "Page 1");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
test_data_url, GURL(kTestPage1Url)));
// Back to the initial page.
ExecuteScriptAndWaitForTitle("history.back();", "Ready");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
test_data_url, GURL(kTestPageUrl)));
// Move to page 1.
ExecuteScriptAndWaitForTitle("history.forward();", "Page 1");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
test_data_url, GURL(kTestPage1Url)));
// Reload.
ExecuteScriptAndWaitForTitle("document.title = 'reset';", "reset");
ExecuteScriptAndWaitForTitle("location.reload();", "Page 1");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
test_data_url, GURL(kTestPage1Url)));
// Move to page 2.
ExecuteScriptAndWaitForTitle("history.forward();", "Page 2");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
test_data_url, GURL(kTestPage2Url)));
}
IN_PROC_BROWSER_TEST_P(BundledExchangesFileBrowserTest, NavigationWithHash) {
const GURL test_data_url =
GetTestUrlForFile(GetTestDataPath("bundled_exchanges_browsertest.wbn"));
NavigateToBundleAndWaitForReady(
test_data_url,
bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
test_data_url, GURL(kTestPageUrl)));
NavigateToURLAndWaitForTitle(GURL(kTestPageForHashUrl), "#hello");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
test_data_url, GURL(kTestPageForHashUrl)));
}
IN_PROC_BROWSER_TEST_P(BundledExchangesFileBrowserTest,
InvalidBundledExchangeFile) {
const GURL test_data_url =
GetTestUrlForFile(GetTestDataPath("invalid_bundled_exchanges.wbn"));
WebContents* web_contents = shell()->web_contents();
ConsoleObserverDelegate console_delegate(web_contents, "*");
web_contents->SetDelegate(&console_delegate);
base::RunLoop run_loop;
FinishNavigationObserver finish_navigation_observer(web_contents,
run_loop.QuitClosure());
EXPECT_FALSE(NavigateToURL(web_contents, test_data_url));
run_loop.Run();
ASSERT_TRUE(finish_navigation_observer.error_code());
EXPECT_EQ(net::ERR_INVALID_BUNDLED_EXCHANGES,
*finish_navigation_observer.error_code());
if (console_delegate.messages().empty())
console_delegate.Wait();
EXPECT_FALSE(console_delegate.messages().empty());
EXPECT_EQ("Failed to read metadata of Web Bundle file: Wrong magic bytes.",
console_delegate.message());
}
IN_PROC_BROWSER_TEST_P(BundledExchangesFileBrowserTest,
ResponseParseErrorInMainResource) {
const GURL test_data_url = GetTestUrlForFile(
GetTestDataPath("broken_bundle_broken_first_entry.wbn"));
WebContents* web_contents = shell()->web_contents();
ConsoleObserverDelegate console_delegate(web_contents, "*");
web_contents->SetDelegate(&console_delegate);
base::RunLoop run_loop;
FinishNavigationObserver finish_navigation_observer(web_contents,
run_loop.QuitClosure());
EXPECT_FALSE(NavigateToURL(web_contents, test_data_url));
run_loop.Run();
ASSERT_TRUE(finish_navigation_observer.error_code());
EXPECT_EQ(net::ERR_INVALID_BUNDLED_EXCHANGES,
*finish_navigation_observer.error_code());
if (console_delegate.messages().empty())
console_delegate.Wait();
EXPECT_FALSE(console_delegate.messages().empty());
EXPECT_EQ(
"Failed to read response header of Web Bundle file: Response headers map "
"must have exactly one pseudo-header, :status.",
console_delegate.message());
}
IN_PROC_BROWSER_TEST_P(BundledExchangesFileBrowserTest,
ResponseParseErrorInSubresource) {
const GURL test_data_url = GetTestUrlForFile(
GetTestDataPath("broken_bundle_broken_script_entry.wbn"));
NavigateToBundleAndWaitForReady(
test_data_url,
bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
test_data_url, GURL(kTestPageUrl)));
WebContents* web_contents = shell()->web_contents();
ConsoleObserverDelegate console_delegate(web_contents, "*");
web_contents->SetDelegate(&console_delegate);
ExecuteScriptAndWaitForTitle(R"(
const script = document.createElement("script");
script.onerror = () => { document.title = "load failed";};
script.src = "script.js";
document.body.appendChild(script);)",
"load failed");
if (console_delegate.messages().empty())
console_delegate.Wait();
EXPECT_FALSE(console_delegate.messages().empty());
EXPECT_EQ(
"Failed to read response header of Web Bundle file: Response headers map "
"must have exactly one pseudo-header, :status.",
console_delegate.message());
}
IN_PROC_BROWSER_TEST_P(BundledExchangesFileBrowserTest, NoLocalFileScheme) {
const GURL test_data_url =
GetTestUrlForFile(GetTestDataPath("bundled_exchanges_browsertest.wbn"));
NavigateToBundleAndWaitForReady(
test_data_url,
bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
test_data_url, GURL(kTestPageUrl)));
auto expected_title = base::ASCIIToUTF16("load failed");
TitleWatcher title_watcher(shell()->web_contents(), expected_title);
title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16("Local Script"));
const GURL script_file_url =
net::FilePathToFileURL(GetTestDataPath("local_script.js"));
const std::string script = base::StringPrintf(R"(
const script = document.createElement("script");
script.onerror = () => { document.title = "load failed";};
script.src = "%s";
document.body.appendChild(script);)",
script_file_url.spec().c_str());
EXPECT_TRUE(ExecuteScript(shell()->web_contents(), script));
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
INSTANTIATE_TEST_SUITE_P(BundledExchangesFileBrowserTest,
BundledExchangesFileBrowserTest,
testing::Values(TestFilePathMode::kNormalFilePath
#if defined(OS_ANDROID)
,
TestFilePathMode::kContentURI
#endif // OS_ANDROID
));
class BundledExchangesNetworkBrowserTest
: public BundledExchangesBrowserTestBase {
protected:
// Keep consistent with NETWORK_TEST_PORT in generate-test-wbns.sh.
static constexpr int kNetworkTestPort = 39600;
BundledExchangesNetworkBrowserTest() = default;
~BundledExchangesNetworkBrowserTest() override = default;
void SetUpOnMainThread() override {
BundledExchangesBrowserTestBase::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
}
void SetUp() override {
feature_list_.InitWithFeatures({features::kWebBundlesFromNetwork}, {});
BundledExchangesBrowserTestBase::SetUp();
}
void RegisterRequestHandler(const std::string& relative_url,
const std::string& headers,
const std::string& contents) {
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
[](const std::string& relative_url, const std::string& headers,
const std::string& contents,
const net::test_server::HttpRequest& request)
-> std::unique_ptr<net::test_server::HttpResponse> {
if (request.relative_url != relative_url)
return nullptr;
return std::make_unique<net::test_server::RawHttpResponse>(headers,
contents);
},
relative_url, headers, contents));
}
std::string GetTestFile(const std::string& file_name) const {
base::ScopedAllowBlockingForTesting allow_blocking;
std::string contents;
base::FilePath src_dir;
CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &src_dir));
base::FilePath test_path = src_dir.Append(
FILE_PATH_LITERAL("content/test/data/bundled_exchanges"));
CHECK(base::ReadFileToString(test_path.AppendASCII(file_name), &contents));
return contents;
}
void TestNavigationFailure(const GURL& url,
const std::string& expected_console_error) {
WebContents* web_contents = shell()->web_contents();
ConsoleObserverDelegate console_delegate(web_contents, "*");
web_contents->SetDelegate(&console_delegate);
base::RunLoop run_loop;
FinishNavigationObserver finish_navigation_observer(web_contents,
run_loop.QuitClosure());
EXPECT_FALSE(NavigateToURL(web_contents, url));
run_loop.Run();
ASSERT_TRUE(finish_navigation_observer.error_code());
EXPECT_EQ(net::ERR_INVALID_BUNDLED_EXCHANGES,
*finish_navigation_observer.error_code());
if (console_delegate.messages().empty())
console_delegate.Wait();
EXPECT_FALSE(console_delegate.messages().empty());
EXPECT_EQ(expected_console_error, console_delegate.message());
}
static GURL GetTestUrl(const std::string& host) {
return GURL(base::StringPrintf("http://%s:%d/bundled_exchanges/test.wbn",
host.c_str(), kNetworkTestPort));
}
private:
base::test::ScopedFeatureList feature_list_;
DISALLOW_COPY_AND_ASSIGN(BundledExchangesNetworkBrowserTest);
};
IN_PROC_BROWSER_TEST_F(BundledExchangesNetworkBrowserTest, Simple) {
const std::string test_bundle =
GetTestFile("bundled_exchanges_browsertest_network.wbn");
RegisterRequestHandler(
"/bundled_exchanges/test.wbn",
base::StringPrintf("HTTP/1.1 200 OK\n"
"Content-Type:application/webbundle\n"
"Content-Length: %" PRIuS "\n",
test_bundle.size()),
test_bundle);
ASSERT_TRUE(embedded_test_server()->Start(kNetworkTestPort));
NavigateToBundleAndWaitForReady(
GetTestUrl("localhost"),
GURL(base::StringPrintf("http://localhost:%d/bundled_exchanges/network/",
kNetworkTestPort)));
}
IN_PROC_BROWSER_TEST_F(BundledExchangesNetworkBrowserTest, Download) {
const std::string test_bundle =
GetTestFile("bundled_exchanges_browsertest_network.wbn");
// Web Bundle file with attachment Content-Disposition must trigger download.
RegisterRequestHandler(
"/bundled_exchanges/test.wbn",
base::StringPrintf("HTTP/1.1 200 OK\n"
"Content-Type:application/webbundle\n"
"Content-Disposition:attachment; filename=test.wbn\n"
"Content-Length: %" PRIuS "\n",
test_bundle.size()),
test_bundle);
ASSERT_TRUE(embedded_test_server()->Start(kNetworkTestPort));
const GURL url = GetTestUrl("localhost");
WebContents* web_contents = shell()->web_contents();
std::unique_ptr<DownloadObserver> download_observer =
std::make_unique<DownloadObserver>(BrowserContext::GetDownloadManager(
web_contents->GetBrowserContext()));
EXPECT_FALSE(NavigateToURL(web_contents, url));
download_observer->WaitUntilDownloadCreated();
EXPECT_EQ(url, download_observer->observed_url());
}
IN_PROC_BROWSER_TEST_F(BundledExchangesNetworkBrowserTest, NoContentLength) {
const std::string test_bundle =
GetTestFile("bundled_exchanges_browsertest_network.wbn");
RegisterRequestHandler("/bundled_exchanges/test.wbn",
"HTTP/1.1 200 OK\n"
"Content-Type:application/webbundle\n",
test_bundle);
ASSERT_TRUE(embedded_test_server()->Start(kNetworkTestPort));
TestNavigationFailure(
GetTestUrl("localhost"),
"Web Bundle response must have valid Content-Length header.");
}
IN_PROC_BROWSER_TEST_F(BundledExchangesNetworkBrowserTest, NonSecureUrl) {
const std::string test_bundle =
GetTestFile("bundled_exchanges_browsertest_network.wbn");
RegisterRequestHandler(
"/bundled_exchanges/test.wbn",
base::StringPrintf("HTTP/1.1 200 OK\n"
"Content-Type:application/webbundle\n"
"Content-Length: %" PRIuS "\n",
test_bundle.size()),
test_bundle);
ASSERT_TRUE(embedded_test_server()->Start(kNetworkTestPort));
TestNavigationFailure(
GetTestUrl("example.com"),
"Web Bundle response must be served from HTTPS or localhost HTTP.");
}
IN_PROC_BROWSER_TEST_F(BundledExchangesNetworkBrowserTest, PrimaryURLNotFound) {
const std::string test_bundle = GetTestFile(
"bundled_exchanges_browsertest_network_primary_url_not_found.wbn");
RegisterRequestHandler(
"/bundled_exchanges/test.wbn",
base::StringPrintf("HTTP/1.1 200 OK\n"
"Content-Type:application/webbundle\n"
"Content-Length: %" PRIuS "\n",
test_bundle.size()),
test_bundle);
ASSERT_TRUE(embedded_test_server()->Start(kNetworkTestPort));
TestNavigationFailure(
GetTestUrl("localhost"),
"The primary URL resource is not found in the web bundle.");
}
IN_PROC_BROWSER_TEST_F(BundledExchangesNetworkBrowserTest, OriginMismatch) {
const std::string test_bundle =
GetTestFile("bundled_exchanges_browsertest_network.wbn");
RegisterRequestHandler(
"/bundled_exchanges/test.wbn",
base::StringPrintf("HTTP/1.1 200 OK\n"
"Content-Type:application/webbundle\n"
"Content-Length: %" PRIuS "\n",
test_bundle.size()),
test_bundle);
ASSERT_TRUE(embedded_test_server()->Start(kNetworkTestPort));
TestNavigationFailure(
GetTestUrl("127.0.0.1"),
"The origin of primary URL doesn't match with the origin of the web "
"bundle.");
}
IN_PROC_BROWSER_TEST_F(BundledExchangesNetworkBrowserTest, InvalidFile) {
const std::string test_bundle = GetTestFile("invalid_bundled_exchanges.wbn");
RegisterRequestHandler(
"/bundled_exchanges/test.wbn",
base::StringPrintf("HTTP/1.1 200 OK\n"
"Content-Type:application/webbundle\n"
"Content-Length: %" PRIuS "\n",
test_bundle.size()),
test_bundle);
ASSERT_TRUE(embedded_test_server()->Start(kNetworkTestPort));
TestNavigationFailure(
GetTestUrl("localhost"),
"Failed to read metadata of Web Bundle file: Wrong magic bytes.");
}
} // namespace content