blob: 8421201ab86fbff667efd67a87c0590270ba8cd0 [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/format_macros.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/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "components/web_package/test_support/web_bundle_builder.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/web_package/web_bundle_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/browser/navigation_type.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.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.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"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#include "third_party/abseil-cpp/absl/types/optional.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 kDefaultHeaders[] =
"HTTP/1.1 200 OK\n"
"Content-Type: application/webbundle\n"
"X-Content-Type-Options: nosniff\n";
constexpr char kHeadersForHtml[] =
"HTTP/1.1 200 OK\n"
"Content-Type: text/html\n";
constexpr char kHeadersForJavaScript[] =
"HTTP/1.1 200 OK\n"
"Content-Type: application/javascript\n";
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/web_bundle")))
.AppendASCII(file);
}
#if defined(OS_ANDROID)
void CopyFileAndGetContentUri(const base::FilePath& file,
GURL* content_uri,
base::FilePath* new_file_path) {
DCHECK(content_uri);
base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath tmp_dir;
ASSERT_TRUE(base::GetTempDir(&tmp_dir));
// The directory name "web_bundle" must be kept in sync with
// content/shell/android/browsertests_apk/res/xml/file_paths.xml
base::FilePath tmp_wbn_dir = tmp_dir.AppendASCII("web_bundle");
ASSERT_TRUE(base::CreateDirectoryAndGetError(tmp_wbn_dir, nullptr));
base::FilePath tmp_dir_in_tmp_wbn_dir;
ASSERT_TRUE(
base::CreateTemporaryDirInDir(tmp_wbn_dir, "", &tmp_dir_in_tmp_wbn_dir));
base::FilePath temp_file = tmp_dir_in_tmp_wbn_dir.Append(file.BaseName());
ASSERT_TRUE(base::CopyFile(file, temp_file));
if (new_file_path)
*new_file_path = temp_file;
*content_uri = GURL(base::GetContentUriFromFilePath(temp_file).value());
}
#endif // OS_ANDROID
std::string ExecuteAndGetString(const ToRenderFrameHost& adapter,
const std::string& script) {
return EvalJs(adapter, script).ExtractString();
}
void NavigateAndWaitForTitle(content::WebContents* web_contents,
const GURL& test_data_url,
const GURL& expected_commit_url,
base::StringPiece ascii_title) {
std::u16string expected_title = base::ASCIIToUTF16(ascii_title);
TitleWatcher title_watcher(web_contents, expected_title);
EXPECT_TRUE(NavigateToURL(web_contents, test_data_url, expected_commit_url));
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
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 MockParserFactory;
class MockParser final : public web_package::mojom::WebBundleParser {
public:
using Index = base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr>;
MockParser(
MockParserFactory* factory,
mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver,
const Index& index,
const GURL& primary_url,
bool simulate_parse_metadata_crash,
bool simulate_parse_response_crash)
: factory_(factory),
receiver_(this, std::move(receiver)),
index_(index),
primary_url_(primary_url),
simulate_parse_metadata_crash_(simulate_parse_metadata_crash),
simulate_parse_response_crash_(simulate_parse_response_crash) {}
~MockParser() override = default;
private:
// web_package::mojom::WebBundleParser implementation.
void ParseMetadata(ParseMetadataCallback callback) override;
void ParseResponse(uint64_t response_offset,
uint64_t response_length,
ParseResponseCallback callback) override;
MockParserFactory* factory_;
mojo::Receiver<web_package::mojom::WebBundleParser> receiver_;
const Index& index_;
const GURL primary_url_;
const bool simulate_parse_metadata_crash_;
const bool simulate_parse_response_crash_;
DISALLOW_COPY_AND_ASSIGN(MockParser);
};
class MockParserFactory final
: public web_package::mojom::WebBundleParserFactory {
public:
MockParserFactory(std::vector<GURL> urls,
const base::FilePath& response_body_file)
: primary_url_(urls[0]) {
base::ScopedAllowBlockingForTesting allow_blocking;
int64_t response_body_file_size;
EXPECT_TRUE(
base::GetFileSize(response_body_file, &response_body_file_size));
for (const auto& url : urls) {
web_package::mojom::BundleIndexValuePtr item =
web_package::mojom::BundleIndexValue::New();
item->response_locations.push_back(
web_package::mojom::BundleResponseLocation::New(
0u, response_body_file_size));
index_.insert({url, std::move(item)});
}
in_process_data_decoder_.service()
.SetWebBundleParserFactoryBinderForTesting(
base::BindRepeating(&MockParserFactory::BindWebBundleParserFactory,
base::Unretained(this)));
}
MockParserFactory(
const std::vector<std::pair<GURL, const std::string&>> items)
: primary_url_(items[0].first) {
uint64_t offset = 0;
for (const auto& item : items) {
web_package::mojom::BundleIndexValuePtr index_value =
web_package::mojom::BundleIndexValue::New();
index_value->response_locations.push_back(
web_package::mojom::BundleResponseLocation::New(
offset, item.second.length()));
offset += item.second.length();
index_.insert({item.first, std::move(index_value)});
}
in_process_data_decoder_.service()
.SetWebBundleParserFactoryBinderForTesting(
base::BindRepeating(&MockParserFactory::BindWebBundleParserFactory,
base::Unretained(this)));
}
~MockParserFactory() override = default;
int GetParserCreationCount() const { return parser_creation_count_; }
void SimulateParserDisconnect() { parser_ = nullptr; }
void SimulateParseMetadataCrash() { simulate_parse_metadata_crash_ = true; }
void SimulateParseResponseCrash() { simulate_parse_response_crash_ = true; }
private:
void BindWebBundleParserFactory(
mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>
receiver) {
receivers_.Add(this, std::move(receiver));
}
// web_package::mojom::WebBundleParserFactory implementation.
void GetParserForFile(
mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver,
base::File file) override {
{
base::ScopedAllowBlockingForTesting allow_blocking;
file.Close();
}
DCHECK(!parser_);
parser_ = std::make_unique<MockParser>(
this, std::move(receiver), index_, primary_url_,
simulate_parse_metadata_crash_, simulate_parse_response_crash_);
parser_creation_count_++;
}
void GetParserForDataSource(
mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver,
mojo::PendingRemote<web_package::mojom::BundleDataSource> data_source)
override {
DCHECK(!parser_);
parser_ = std::make_unique<MockParser>(
this, std::move(receiver), index_, primary_url_,
simulate_parse_metadata_crash_, simulate_parse_response_crash_);
parser_creation_count_++;
}
data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
mojo::ReceiverSet<web_package::mojom::WebBundleParserFactory> receivers_;
bool simulate_parse_metadata_crash_ = false;
bool simulate_parse_response_crash_ = false;
std::unique_ptr<MockParser> parser_;
int parser_creation_count_ = 0;
base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr> index_;
const GURL primary_url_;
DISALLOW_COPY_AND_ASSIGN(MockParserFactory);
};
void MockParser::ParseMetadata(ParseMetadataCallback callback) {
if (simulate_parse_metadata_crash_) {
factory_->SimulateParserDisconnect();
return;
}
base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr> items;
for (const auto& item : index_) {
items.insert({item.first, item.second.Clone()});
}
web_package::mojom::BundleMetadataPtr metadata =
web_package::mojom::BundleMetadata::New();
metadata->primary_url = primary_url_;
metadata->requests = std::move(items);
std::move(callback).Run(std::move(metadata), nullptr);
}
void MockParser::ParseResponse(uint64_t response_offset,
uint64_t response_length,
ParseResponseCallback callback) {
if (simulate_parse_response_crash_) {
factory_->SimulateParserDisconnect();
return;
}
web_package::mojom::BundleResponsePtr response =
web_package::mojom::BundleResponse::New();
response->response_code = 200;
response->response_headers.insert({"content-type", "text/html"});
response->payload_offset = response_offset;
response->payload_length = response_length;
std::move(callback).Run(std::move(response), nullptr);
}
class TestBrowserClient : public ContentBrowserClient {
public:
TestBrowserClient() = default;
~TestBrowserClient() override = default;
bool CanAcceptUntrustedExchangesIfNeeded() override { return true; }
std::string GetAcceptLangs(BrowserContext* context) override {
return accept_langs_;
}
void SetAcceptLangs(const std::string langs) { accept_langs_ = langs; }
private:
std::string accept_langs_ = "en";
DISALLOW_COPY_AND_ASSIGN(TestBrowserClient);
};
class WebBundleBrowserTestBase : public ContentBrowserTest {
protected:
WebBundleBrowserTestBase() = default;
~WebBundleBrowserTestBase() override = default;
void SetUpOnMainThread() override {
ContentBrowserTest::SetUpOnMainThread();
original_client_ = SetBrowserClientForTesting(&browser_client_);
}
void TearDownOnMainThread() override {
ContentBrowserTest::TearDownOnMainThread();
SetBrowserClientForTesting(original_client_);
}
void SetAcceptLangs(const std::string langs) {
browser_client_.SetAcceptLangs(langs);
}
void NavigateToBundleAndWaitForReady(const GURL& test_data_url,
const GURL& expected_commit_url) {
NavigateAndWaitForTitle(shell()->web_contents(), test_data_url,
expected_commit_url, "Ready");
}
void RunTestScript(const std::string& script) {
EXPECT_TRUE(
ExecJs(shell()->web_contents(), "loadScript('" + script + "');"));
std::u16string ok = u"OK";
TitleWatcher title_watcher(shell()->web_contents(), ok);
title_watcher.AlsoWaitForTitle(u"FAIL");
EXPECT_EQ(ok, title_watcher.WaitAndGetTitle());
}
void ExecuteScriptAndWaitForTitle(const std::string& script,
const std::string& title) {
std::u16string title16 = base::ASCIIToUTF16(title);
TitleWatcher title_watcher(shell()->web_contents(), title16);
EXPECT_TRUE(ExecJs(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);
}
void CreateTemporaryWebBundleFile(const std::string& content,
base::FilePath* file_path) {
base::ScopedAllowBlockingForTesting allow_blocking;
if (!temp_dir_.IsValid()) {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
}
base::FilePath tmp_file_path;
ASSERT_TRUE(
base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &tmp_file_path));
if (!content.empty())
ASSERT_TRUE(base::WriteFile(tmp_file_path, content));
*file_path = tmp_file_path.AddExtension(FILE_PATH_LITERAL(".wbn"));
ASSERT_TRUE(base::Move(tmp_file_path, *file_path));
}
private:
ContentBrowserClient* original_client_ = nullptr;
TestBrowserClient browser_client_;
base::ScopedTempDir temp_dir_;
DISALLOW_COPY_AND_ASSIGN(WebBundleBrowserTestBase);
};
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 {
navigation_types_.push_back(
NavigationRequest::From(navigation_handle)->navigation_type());
error_code_ = navigation_handle->GetNetErrorCode();
--navigations_remaining_;
if (navigations_remaining_ == 0)
std::move(done_closure_).Run();
}
void set_navigations_remaining(int navigations_remaining) {
navigations_remaining_ = navigations_remaining;
}
const absl::optional<net::Error>& error_code() const { return error_code_; }
const std::vector<NavigationType>& navigation_types() const {
return navigation_types_;
}
private:
base::OnceClosure done_closure_;
absl::optional<net::Error> error_code_;
int navigations_remaining_ = 1;
std::vector<NavigationType> navigation_types_;
DISALLOW_COPY_AND_ASSIGN(FinishNavigationObserver);
};
std::string ExpectNavigationFailureAndReturnConsoleMessage(
content::WebContents* web_contents,
const GURL& url) {
WebContentsConsoleObserver console_observer(web_contents);
base::RunLoop run_loop;
FinishNavigationObserver finish_navigation_observer(web_contents,
run_loop.QuitClosure());
EXPECT_FALSE(NavigateToURL(web_contents, url));
run_loop.Run();
if (!finish_navigation_observer.error_code()) {
ADD_FAILURE() << "Unexpected navigation success: " << url;
return std::string();
}
EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
*finish_navigation_observer.error_code());
if (console_observer.messages().empty())
console_observer.Wait();
if (console_observer.messages().empty()) {
ADD_FAILURE() << "Could not find a console message.";
return std::string();
}
return base::UTF16ToUTF8(console_observer.messages()[0].message);
}
FrameTreeNode* GetFirstChild(WebContents* web_contents) {
return static_cast<WebContentsImpl*>(web_contents)
->GetFrameTree()
->root()
->child_at(0);
}
std::string CreateSimpleWebBundle(const GURL& primary_url) {
web_package::test::WebBundleBuilder builder(primary_url.spec(), "");
builder.AddExchange(primary_url.spec(),
{{":status", "200"}, {"content-type", "text/html"}},
"<title>Ready</title>");
std::vector<uint8_t> bundle = builder.CreateBundle();
return std::string(bundle.begin(), bundle.end());
}
void AddHtmlFile(web_package::test::WebBundleBuilder* builder,
const GURL& base_url,
const std::string& path,
const std::string& content) {
builder->AddExchange(base_url.Resolve(path).spec(),
{{":status", "200"}, {"content-type", "text/html"}},
content);
}
void AddScriptFile(web_package::test::WebBundleBuilder* builder,
const GURL& base_url,
const std::string& path,
const std::string& content) {
builder->AddExchange(
base_url.Resolve(path).spec(),
{{":status", "200"}, {"content-type", "application/javascript"}},
content);
}
std::string CreatePathTestWebBundle(const GURL& base_url) {
const std::string primary_url_path = "/web_bundle/path_test/in_scope/";
web_package::test::WebBundleBuilder builder(
base_url.Resolve(primary_url_path).spec(), "");
AddHtmlFile(&builder, base_url, primary_url_path, "<title>Ready</title>");
AddHtmlFile(
&builder, base_url, "/web_bundle/path_test/in_scope/page.html",
"<script>const page_info = 'In scope page in Web Bundle';</script>"
"<script src=\"page.js\"></script>");
AddScriptFile(
&builder, base_url, "/web_bundle/path_test/in_scope/page.js",
"document.title = page_info + ' / in scope script in Web Bundle';");
AddHtmlFile(
&builder, base_url, "/web_bundle/path_test/out_scope/page.html",
"<script>const page_info = 'Out scope page in Web Bundle';</script>"
"<script src=\"page.js\"></script>");
AddScriptFile(
&builder, base_url, "/web_bundle/path_test/out_scope/page.js",
"document.title = page_info + ' / out scope script in Web Bundle';");
std::vector<uint8_t> bundle = builder.CreateBundle();
return std::string(bundle.begin(), bundle.end());
}
std::string CreateSubPageHtml(const std::string& page_info) {
return base::StringPrintf(R"(
<body><script>
const page_info = '%s';
let script = document.createElement('script');
script.src = location.hash.substr(1);
document.body.appendChild(script);
</script></body>)",
page_info.c_str());
}
std::string CreateScriptForSubPageTest(const std::string& script_info) {
return base::StringPrintf(
R"(
if (window.opener) {
window.opener.postMessage(page_info + ' %s', '*');
} else {
window.parent.window.postMessage(page_info + ' %s', '*');
}
)",
script_info.c_str(), script_info.c_str());
}
void RegisterRequestHandlerForSubPageTest(net::EmbeddedTestServer* server,
const std::string& prefix) {
server->RegisterRequestHandler(base::BindRepeating(
[](const std::string& prefix,
const net::test_server::HttpRequest& request)
-> std::unique_ptr<net::test_server::HttpResponse> {
if (base::EndsWith(request.relative_url, "subpage",
base::CompareCase::SENSITIVE)) {
return std::make_unique<net::test_server::RawHttpResponse>(
kHeadersForHtml, CreateSubPageHtml(prefix + "server-page"));
}
if (base::EndsWith(request.relative_url, "script",
base::CompareCase::SENSITIVE)) {
return std::make_unique<net::test_server::RawHttpResponse>(
kHeadersForJavaScript,
CreateScriptForSubPageTest(prefix + "server-script"));
}
return nullptr;
},
prefix));
}
// Sets up |primary_server| and |third_party_server| to return server generated
// sub page HTML files and JavaScript files:
// - |primary_server| will return a sub page file created by
// CreateSubPageHtml("") for all URL which ends with "subpage", and returns a
// script file created by CreateScriptForSubPageTest("") for all URL which
// ends with "script".
// - |third_party_server| will return a sub page file created by
// CreateSubPageHtml("third-party:") for all URL which ends with "subpage",
// and returns a script file created by
// CreateScriptForSubPageTest("third-party:") for all URL which ends with
// "script".
// And generates a web bundle file which contains the following files:
// - in |primary_server|'s origin:
// - /top : web bundle file's primary URL.
// - /subpage : returns CreateSubPageHtml("wbn-page").
// - /script : returns CreateScriptForSubPageTest("wbn-script").
// - in |third_party_server|'s origin:
// - /subpage : returns CreateSubPageHtml("third-party:wbn-page").
// - /script : returns CreateScriptForSubPageTest("third-party:wbn-script").
// When the sub page is loaded using iframe or window.open(), a script of the
// URL hash of the sub page is loaded. And domAutomationController.send() will
// be called via postMessage(). So we can know whether the sub page and the
// script are loaded from the web bundle file or the server.
void SetUpSubPageTest(net::EmbeddedTestServer* primary_server,
net::EmbeddedTestServer* third_party_server,
GURL* primary_url_origin,
GURL* third_party_origin,
std::string* web_bundle_content) {
RegisterRequestHandlerForSubPageTest(primary_server, "");
RegisterRequestHandlerForSubPageTest(third_party_server, "third-party:");
ASSERT_TRUE(primary_server->Start());
ASSERT_TRUE(third_party_server->Start());
*primary_url_origin = primary_server->GetURL("/");
*third_party_origin = third_party_server->GetURL("/");
web_package::test::WebBundleBuilder builder(
primary_url_origin->Resolve("/top").spec(), "");
AddHtmlFile(&builder, *primary_url_origin, "/top", R"(
<script>
window.addEventListener('message',
event => domAutomationController.send(event.data),
false);
document.title = 'Ready';
</script>
)");
AddHtmlFile(&builder, *primary_url_origin, "/subpage",
CreateSubPageHtml("wbn-page"));
AddScriptFile(&builder, *primary_url_origin, "/script",
CreateScriptForSubPageTest("wbn-script"));
AddHtmlFile(&builder, *third_party_origin, "/subpage",
CreateSubPageHtml("third-party:wbn-page"));
AddScriptFile(&builder, *third_party_origin, "/script",
CreateScriptForSubPageTest("third-party:wbn-script"));
std::vector<uint8_t> bundle = builder.CreateBundle();
*web_bundle_content = std::string(bundle.begin(), bundle.end());
}
std::string AddIframeAndWaitForMessage(const ToRenderFrameHost& adapter,
const GURL& url) {
return EvalJs(adapter,
JsReplace(
R"(
(function(){
const iframe = document.createElement('iframe');
iframe.src = $1;
document.body.appendChild(iframe);
})();
)",
url),
EXECUTE_SCRIPT_USE_MANUAL_REPLY)
.ExtractString();
}
std::string WindowOpenAndWaitForMessage(const ToRenderFrameHost& adapter,
const GURL& url) {
return EvalJs(adapter,
JsReplace(R"(
if (document.last_win) {
// Close the latest window to avoid OOM-killer on Android.
document.last_win.close();
}
document.last_win = window.open($1, '_blank');
)",
url),
EXECUTE_SCRIPT_USE_MANUAL_REPLY)
.ExtractString();
}
// Runs tests for subpages (iframe / window.open()). This function calls
// |test_func| to create an iframe (AddIframeAndWaitForMessage) or to open a new
// window (WindowOpenAndWaitForMessage).
// |support_third_party_wbn_page| must be true when third party pages should be
// served from servers even if they are in the web bundle.
void RunSubPageTest(const ToRenderFrameHost& adapter,
const GURL& primary_url_origin,
const GURL& third_party_origin,
std::string (*test_func)(const content::ToRenderFrameHost&,
const GURL&),
bool support_third_party_wbn_page) {
EXPECT_EQ(
"wbn-page wbn-script",
(*test_func)(adapter,
primary_url_origin.Resolve("/subpage").Resolve("#/script")));
EXPECT_EQ("wbn-page server-script",
(*test_func)(adapter, primary_url_origin.Resolve("/subpage")
.Resolve("#/not-in-wbn-script")));
EXPECT_EQ(
support_third_party_wbn_page ? "wbn-page third-party:wbn-script"
: "wbn-page third-party:server-script",
(*test_func)(adapter,
primary_url_origin.Resolve("/subpage")
.Resolve(std::string("#") +
third_party_origin.Resolve("/script").spec())));
EXPECT_EQ(
"wbn-page third-party:server-script",
(*test_func)(
adapter,
primary_url_origin.Resolve("/subpage")
.Resolve(
std::string("#") +
third_party_origin.Resolve("/not-in-wbn-script").spec())));
EXPECT_EQ(
"server-page server-script",
(*test_func)(adapter, primary_url_origin.Resolve("/not-in-wbn-subpage")
.Resolve("#/script")));
EXPECT_EQ(
support_third_party_wbn_page
? "third-party:wbn-page third-party:wbn-script"
: "third-party:server-page third-party:server-script",
(*test_func)(adapter,
third_party_origin.Resolve("/subpage").Resolve("#script")));
EXPECT_EQ(
"third-party:server-page third-party:server-script",
(*test_func)(adapter, third_party_origin.Resolve("/not-in-wbn-subpage")
.Resolve("#script")));
}
std::string CreateHtmlForNavigationTest(const std::string& page_info,
const std::string& additional_html) {
return base::StringPrintf(
R"(
<body><script>
document.page_info = '%s';
document.title='Ready';
</script>%s</body>
)",
page_info.c_str(), additional_html.c_str());
}
std::string CreateScriptForNavigationTest(const std::string& script_info) {
return base::StringPrintf("document.script_info = '%s';",
script_info.c_str());
}
void AddHtmlAndScriptForNavigationTest(
web_package::test::WebBundleBuilder* builder,
const GURL& base_url,
const std::string& path,
const std::string& additional_html) {
AddHtmlFile(builder, base_url, path,
CreateHtmlForNavigationTest(path + " from wbn", additional_html));
AddScriptFile(builder, base_url, path + "script",
CreateScriptForNavigationTest(path + "script from wbn"));
}
std::string GetLoadResultForNavigationTest(const ToRenderFrameHost& adapter) {
std::string script = R"(
(async () => {
const script = document.createElement('script');
script.src = './script';
script.addEventListener('load', () => {
domAutomationController.send(
document.page_info + ', ' + document.script_info);
}, false);
script.addEventListener('error', () => {
domAutomationController.send(
document.page_info + ' failed to load script');
}, false);
if (!document.body) {
await new Promise((resolve) => {
document.addEventListener('DOMContentLoaded', resolve);
});
}
document.body.appendChild(script);
})()
)";
return EvalJs(adapter, script, EXECUTE_SCRIPT_USE_MANUAL_REPLY)
.ExtractString();
}
// Sets up |server| to return server generated page HTML files and JavaScript
// files. |server| will returns a page file created by
// CreateHtmlForNavigationTest(relative_url + " from server") for all URL which
// ends with "page/", and returns a script file created by
// CreateScriptForNavigationTest(relative_url + " from server") for all URL
// which ends with "script".
void SetUpNavigationTestServer(net::EmbeddedTestServer* server,
GURL* url_origin) {
server->RegisterRequestHandler(base::BindRepeating(
[](const net::test_server::HttpRequest& request)
-> std::unique_ptr<net::test_server::HttpResponse> {
if (base::EndsWith(request.relative_url, "page/",
base::CompareCase::SENSITIVE)) {
return std::make_unique<net::test_server::RawHttpResponse>(
kHeadersForHtml, CreateHtmlForNavigationTest(
request.relative_url + " from server", ""));
}
if (base::EndsWith(request.relative_url, "script",
base::CompareCase::SENSITIVE)) {
return std::make_unique<net::test_server::RawHttpResponse>(
kHeadersForJavaScript,
CreateScriptForNavigationTest(request.relative_url +
" from server"));
}
return nullptr;
}));
ASSERT_TRUE(server->Start());
*url_origin = server->GetURL("/");
}
void RunScriptAndObserveNavigation(
const std::string& message,
WebContents* web_contents,
const ToRenderFrameHost& execution_target,
const std::string& script,
const std::vector<NavigationType> expected_navigation_types,
const GURL& expected_last_comitted_url,
const GURL& expected_last_inner_url,
const std::string& expected_load_result) {
SCOPED_TRACE(message);
base::RunLoop run_loop;
FinishNavigationObserver finish_navigation_observer(web_contents,
run_loop.QuitClosure());
finish_navigation_observer.set_navigations_remaining(
expected_navigation_types.size());
EXPECT_TRUE(ExecJs(execution_target, script));
run_loop.Run();
EXPECT_EQ(finish_navigation_observer.navigation_types(),
expected_navigation_types);
EXPECT_EQ(web_contents->GetLastCommittedURL(), expected_last_comitted_url);
EXPECT_EQ(expected_load_result, GetLoadResultForNavigationTest(web_contents));
EXPECT_EQ(ExecuteAndGetString(web_contents, "window.location.href"),
expected_last_inner_url);
EXPECT_EQ(ExecuteAndGetString(web_contents, "document.location.href"),
expected_last_inner_url);
EXPECT_EQ(ExecuteAndGetString(web_contents, "document.URL"),
expected_last_inner_url);
}
void SetUpSharedNavigationsTest(net::EmbeddedTestServer* server,
const std::vector<std::string>& pathes,
GURL* url_origin,
std::string* web_bundle_content) {
SetUpNavigationTestServer(server, url_origin);
web_package::test::WebBundleBuilder builder(
url_origin->Resolve("/top-page/").spec(), "");
for (const auto& path : pathes)
AddHtmlAndScriptForNavigationTest(&builder, *url_origin, path, "");
std::vector<uint8_t> bundle = builder.CreateBundle();
*web_bundle_content = std::string(bundle.begin(), bundle.end());
}
void SetUpBasicNavigationTest(net::EmbeddedTestServer* server,
GURL* url_origin,
std::string* web_bundle_content) {
SetUpSharedNavigationsTest(server, {"/top-page/", "/1-page/", "/2-page/"},
url_origin, web_bundle_content);
}
// Runs test for basic history navigations (back/forward/reload).
void RunBasicNavigationTest(
WebContents* web_contents,
const GURL& web_bundle_url,
const GURL& url_origin,
base::RepeatingCallback<GURL(const GURL&)> get_url_for_bundle) {
NavigateAndWaitForTitle(
web_contents, web_bundle_url,
get_url_for_bundle.Run(url_origin.Resolve("/top-page/")), "Ready");
RunScriptAndObserveNavigation(
"Navigate to /1-page/", web_contents, web_contents /* execution_target */,
"location.href = '/1-page/';", {NAVIGATION_TYPE_NEW_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/1-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Navigate to /2-page/", web_contents, web_contents /* execution_target */,
"location.href = '/2-page/';", {NAVIGATION_TYPE_NEW_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/2-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/2-page/") /* expected_last_inner_url */,
"/2-page/ from wbn, /2-page/script from wbn");
RunScriptAndObserveNavigation(
"Back navigate to /1-page/", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/1-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Back navigate to /top-page/", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/top-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/top-page/") /* expected_last_inner_url */,
"/top-page/ from wbn, /top-page/script from wbn");
RunScriptAndObserveNavigation(
"Forward navigate to /1-page/", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/1-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Reload /1-page/", web_contents, web_contents /* execution_target */,
"location.reload();", {NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/1-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Forward navigate to /2-page/", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/2-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/2-page/") /* expected_last_inner_url */,
"/2-page/ from wbn, /2-page/script from wbn");
}
void SetUpBrowserInitiatedOutOfBundleNavigationTest(
net::EmbeddedTestServer* server,
GURL* url_origin,
std::string* web_bundle_content) {
SetUpSharedNavigationsTest(
server, {"/top-page/", "/1-page/", "/2-page/", "/3-page/", "/4-page/"},
url_origin, web_bundle_content);
}
// Runs test for history navigations after browser initiated navigation going
// out of the web bundle.
void RunBrowserInitiatedOutOfBundleNavigationTest(
WebContents* web_contents,
const GURL& web_bundle_url,
const GURL& url_origin,
base::RepeatingCallback<GURL(const GURL&)> get_url_for_bundle) {
NavigateAndWaitForTitle(
web_contents, web_bundle_url,
get_url_for_bundle.Run(url_origin.Resolve("/top-page/")), "Ready");
RunScriptAndObserveNavigation(
"Navigate to /1-page/", web_contents, web_contents /* execution_target */,
"location.href = '/1-page/';", {NAVIGATION_TYPE_NEW_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/1-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Navigate to /2-page/", web_contents, web_contents /* execution_target */,
"location.href = '/2-page/';", {NAVIGATION_TYPE_NEW_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/2-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/2-page/") /* expected_last_inner_url */,
"/2-page/ from wbn, /2-page/script from wbn");
{
SCOPED_TRACE("Browser initiated navigation to /3-page/");
EXPECT_TRUE(NavigateToURL(web_contents, url_origin.Resolve("/3-page/")));
EXPECT_EQ(web_contents->GetLastCommittedURL(),
url_origin.Resolve("/3-page/"));
// Browser initiated navigation must be loaded from the server even if the
// page is in the web bundle.
EXPECT_EQ("/3-page/ from server, /3-page/script from server",
GetLoadResultForNavigationTest(web_contents));
}
// Navigation from the out of web bundle page must be loaded from the server
// even if the page is in the web bundle.
RunScriptAndObserveNavigation(
"Navigate to /4-page/", web_contents, web_contents /* execution_target */,
"location.href = '/4-page/';", {NAVIGATION_TYPE_NEW_ENTRY},
url_origin.Resolve("/4-page/") /* expected_last_comitted_url */,
url_origin.Resolve("/4-page/") /* expected_last_inner_url */,
"/4-page/ from server, /4-page/script from server");
RunScriptAndObserveNavigation(
"Back navigate to /3-page/", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
url_origin.Resolve("/3-page/") /* expected_last_comitted_url */,
url_origin.Resolve("/3-page/") /* expected_last_inner_url */,
"/3-page/ from server, /3-page/script from server");
RunScriptAndObserveNavigation(
"Back navigate to /2-page/", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/2-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/2-page/") /* expected_last_inner_url */,
"/2-page/ from wbn, /2-page/script from wbn");
RunScriptAndObserveNavigation(
"Back navigate to /1-page/", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/1-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Forward navigate to /2-page/", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/2-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/2-page/") /* expected_last_inner_url */,
"/2-page/ from wbn, /2-page/script from wbn");
RunScriptAndObserveNavigation(
"Forward navigate to /3-page/", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
url_origin.Resolve("/3-page/") /* expected_last_comitted_url */,
url_origin.Resolve("/3-page/") /* expected_last_inner_url */,
"/3-page/ from server, /3-page/script from server");
RunScriptAndObserveNavigation(
"Forward navigate to /4-page/", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
url_origin.Resolve("/4-page/") /* expected_last_comitted_url */,
url_origin.Resolve("/4-page/") /* expected_last_inner_url */,
"/4-page/ from server, /4-page/script from server");
}
void SetUpRendererInitiatedOutOfBundleNavigationTest(
net::EmbeddedTestServer* server,
GURL* url_origin,
std::string* web_bundle_content) {
SetUpSharedNavigationsTest(server,
{"/top-page/", "/1-page/", "/2-page/", "/3-page/"},
url_origin, web_bundle_content);
}
// Runs test for history navigations after renderer initiated navigation going
// out of the web bundle.
void RunRendererInitiatedOutOfBundleNavigationTest(
WebContents* web_contents,
const GURL& web_bundle_url,
const GURL& url_origin,
base::RepeatingCallback<GURL(const GURL&)> get_url_for_bundle) {
NavigateAndWaitForTitle(
web_contents, web_bundle_url,
get_url_for_bundle.Run(url_origin.Resolve("/top-page/")), "Ready");
RunScriptAndObserveNavigation(
"Navigate to /1-page/", web_contents, web_contents /* execution_target */,
"location.href = '/1-page/';", {NAVIGATION_TYPE_NEW_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/1-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Navigate to /2-page/", web_contents, web_contents /* execution_target */,
"location.href = '/2-page/';", {NAVIGATION_TYPE_NEW_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/2-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/2-page/") /* expected_last_inner_url */,
"/2-page/ from wbn, /2-page/script from wbn");
RunScriptAndObserveNavigation(
"Navigate to /server-page/", web_contents,
web_contents /* execution_target */, "location.href = '/server-page/';",
{NAVIGATION_TYPE_NEW_ENTRY},
url_origin.Resolve("/server-page/") /* expected_last_comitted_url */,
url_origin.Resolve("/server-page/") /* expected_last_inner_url */,
"/server-page/ from server, /server-page/script from server");
// Navigation from the out of web bundle page must be loaded from the server
// even if the page is in the web bundle.
RunScriptAndObserveNavigation(
"Navigate to /3-page/", web_contents, web_contents /* execution_target */,
"location.href = '/3-page/';", {NAVIGATION_TYPE_NEW_ENTRY},
url_origin.Resolve("/3-page/") /* expected_last_comitted_url */,
url_origin.Resolve("/3-page/") /* expected_last_inner_url */,
"/3-page/ from server, /3-page/script from server");
RunScriptAndObserveNavigation(
"Back navigate to /server-page/", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
url_origin.Resolve("/server-page/") /* expected_last_comitted_url */,
url_origin.Resolve("/server-page/") /* expected_last_inner_url */,
"/server-page/ from server, /server-page/script from server");
RunScriptAndObserveNavigation(
"Back navigate to /2-page/", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/2-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/2-page/") /* expected_last_inner_url */,
"/2-page/ from wbn, /2-page/script from wbn");
RunScriptAndObserveNavigation(
"Back navigate to /1-page/", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/1-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Forward navigate to /2-page/", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/2-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/2-page/") /* expected_last_inner_url */,
"/2-page/ from wbn, /2-page/script from wbn");
RunScriptAndObserveNavigation(
"Forward navigate to /server-page/", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
url_origin.Resolve("/server-page/") /* expected_last_comitted_url */,
url_origin.Resolve("/server-page/") /* expected_last_inner_url */,
"/server-page/ from server, /server-page/script from server");
RunScriptAndObserveNavigation(
"Forward navigate to /3-page/", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
url_origin.Resolve("/3-page/") /* expected_last_comitted_url */,
url_origin.Resolve("/3-page/") /* expected_last_inner_url */,
"/3-page/ from server, /3-page/script from server");
}
void SetUpSameDocumentNavigationTest(net::EmbeddedTestServer* server,
GURL* url_origin,
std::string* web_bundle_content) {
SetUpSharedNavigationsTest(server, {"/top-page/", "/1-page/", "/2-page/"},
url_origin, web_bundle_content);
}
// Runs test for history navigations after same document navigations.
void RunSameDocumentNavigationTest(
WebContents* web_contents,
const GURL& web_bundle_url,
const GURL& url_origin,
base::RepeatingCallback<GURL(const GURL&)> get_url_for_bundle) {
NavigateAndWaitForTitle(
web_contents, web_bundle_url,
get_url_for_bundle.Run(url_origin.Resolve("/top-page/")), "Ready");
RunScriptAndObserveNavigation(
"Navigate to /1-page/", web_contents, web_contents /* execution_target */,
"location.href = '/1-page/';", {NAVIGATION_TYPE_NEW_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/1-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Navigate to /1-page/#hash1", web_contents,
web_contents /* execution_target */, "location.href = '#hash1';",
{NAVIGATION_TYPE_NEW_ENTRY},
get_url_for_bundle.Run(url_origin.Resolve(
"/1-page/#hash1")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/#hash1") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Navigate to /1-page/#hash2", web_contents,
web_contents /* execution_target */, "location.href = '#hash2';",
{NAVIGATION_TYPE_NEW_ENTRY},
get_url_for_bundle.Run(url_origin.Resolve(
"/1-page/#hash2")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/#hash2") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Navigate to /2-page/", web_contents, web_contents /* execution_target */,
"location.href = '/2-page/';", {NAVIGATION_TYPE_NEW_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/2-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/2-page/") /* expected_last_inner_url */,
"/2-page/ from wbn, /2-page/script from wbn");
RunScriptAndObserveNavigation(
"Back navigate to /1-page/#hash2", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(url_origin.Resolve(
"/1-page/#hash2")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/#hash2") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Back navigate to /1-page/#hash1", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(url_origin.Resolve(
"/1-page/#hash1")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/#hash1") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Back navigate to /1-page/", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/1-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Forward navigate to /1-page/#hash1", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(url_origin.Resolve(
"/1-page/#hash1")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/#hash1") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Forward navigate to /1-page/#hash2", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(url_origin.Resolve(
"/1-page/#hash2")) /* expected_last_comitted_url */,
url_origin.Resolve("/1-page/#hash2") /* expected_last_inner_url */,
"/1-page/ from wbn, /1-page/script from wbn");
RunScriptAndObserveNavigation(
"Forward navigate to /2-page/", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/2-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/2-page/") /* expected_last_inner_url */,
"/2-page/ from wbn, /2-page/script from wbn");
}
void SetUpIframeNavigationTest(net::EmbeddedTestServer* server,
GURL* url_origin,
std::string* web_bundle_content) {
SetUpNavigationTestServer(server, url_origin);
web_package::test::WebBundleBuilder builder(
url_origin->Resolve("/top-page/").spec(), "");
const std::vector<std::string> pathes = {"/top-page/", "/1-page/",
"/2-page/"};
for (const auto& path : pathes)
AddHtmlAndScriptForNavigationTest(&builder, *url_origin, path, "");
AddHtmlAndScriptForNavigationTest(&builder, *url_origin, "/iframe-test-page/",
"<iframe src=\"/1-page/\" />");
std::vector<uint8_t> bundle = builder.CreateBundle();
*web_bundle_content = std::string(bundle.begin(), bundle.end());
}
// Runs test for history navigations with an iframe.
void RunIframeNavigationTest(
WebContents* web_contents,
const GURL& web_bundle_url,
const GURL& url_origin,
base::RepeatingCallback<GURL(const GURL&)> get_url_for_bundle) {
NavigateAndWaitForTitle(
web_contents, web_bundle_url,
get_url_for_bundle.Run(url_origin.Resolve("/top-page/")), "Ready");
RunScriptAndObserveNavigation(
"Navigate to /iframe-test-page/", web_contents,
web_contents /* execution_target */,
"location.href = '/iframe-test-page/';",
{NAVIGATION_TYPE_NEW_ENTRY, NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
RunScriptAndObserveNavigation(
"Navigate the iframe to /2-page/", web_contents,
GetFirstChild(web_contents) /* execution_target */,
"location.href = '/2-page/';", {NAVIGATION_TYPE_NEW_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/2-page/ from wbn, /2-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
RunScriptAndObserveNavigation(
"Back navigate the iframe to /1-page/", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
RunScriptAndObserveNavigation(
"Back navigate to /top-page/", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/top-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/top-page/") /* expected_last_inner_url */,
"/top-page/ from wbn, /top-page/script from wbn");
RunScriptAndObserveNavigation(
"Forward navigate to /iframe-test-page/", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_EXISTING_ENTRY, NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
RunScriptAndObserveNavigation(
"Forward navigate the iframe to /2-page/", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/2-page/ from wbn, /2-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
}
// Runs test for navigations in an iframe after going out of the web bundle by
// changing location.href inside the iframe.
void RunIframeOutOfBundleNavigationTest(
WebContents* web_contents,
const GURL& web_bundle_url,
const GURL& url_origin,
base::RepeatingCallback<GURL(const GURL&)> get_url_for_bundle) {
NavigateAndWaitForTitle(
web_contents, web_bundle_url,
get_url_for_bundle.Run(url_origin.Resolve("/top-page/")), "Ready");
RunScriptAndObserveNavigation(
"Navigate to /iframe-test-page/", web_contents,
web_contents /* execution_target */,
"location.href = '/iframe-test-page/';",
{NAVIGATION_TYPE_NEW_ENTRY, NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
// The web bundle doesn't contain /server-page/. So the server returns the
// page and script.
RunScriptAndObserveNavigation(
"Navigate the iframe to /server-page/", web_contents,
GetFirstChild(web_contents) /* execution_target */,
"location.href = /server-page/;", {NAVIGATION_TYPE_NEW_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/server-page/ from server, /server-page/script from server",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
// Even if location.href is changed by /server-page/, /1-page/ is loaded from
// the bundle.
RunScriptAndObserveNavigation(
"Navigate the iframe to /1-page/", web_contents,
GetFirstChild(web_contents) /* execution_target */,
"location.href = /1-page/;", {NAVIGATION_TYPE_NEW_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
}
// Runs test for navigations in an iframe after going out of the web bundle by
// changing iframe.src from the parent frame.
void RunIframeParentInitiatedOutOfBundleNavigationTest(
WebContents* web_contents,
const GURL& web_bundle_url,
const GURL& url_origin,
base::RepeatingCallback<GURL(const GURL&)> get_url_for_bundle) {
NavigateAndWaitForTitle(
web_contents, web_bundle_url,
get_url_for_bundle.Run(url_origin.Resolve("/top-page/")), "Ready");
RunScriptAndObserveNavigation(
"Navigate to /iframe-test-page/", web_contents,
web_contents /* execution_target */,
"location.href = '/iframe-test-page/';",
{NAVIGATION_TYPE_NEW_ENTRY, NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
// The web bundle doesn't contain /server-page/. So the server returns the
// page and script.
RunScriptAndObserveNavigation(
"Navigate the iframe to /server-page/", web_contents,
web_contents /* execution_target */,
"document.querySelector('iframe').src = /server-page/;",
{NAVIGATION_TYPE_NEW_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/server-page/ from server, /server-page/script from server",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
FrameTreeNode* iframe_node = GetFirstChild(web_contents);
bool no_proxy_to_parent =
iframe_node->render_manager()->GetProxyToParent() == nullptr;
RunScriptAndObserveNavigation(
"Navigate the iframe to /1-page/", web_contents,
web_contents /* execution_target */,
"document.querySelector('iframe').src = /1-page/;",
{NAVIGATION_TYPE_NEW_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
// TODO(crbug.com/1040800): Currently the remote iframe can't load the page
// from web bundle. To support this case we need to change
// NavigationControllerImpl::NavigateFromFrameProxy() to correctly handle
// the WebBundleHandleTracker.
EXPECT_EQ(no_proxy_to_parent
? "/1-page/ from wbn, /1-page/script from wbn"
: "/1-page/ from server, /1-page/script from server",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
}
// Runs test for history navigations in an iframe after same document
// navigation.
void RunIframeSameDocumentNavigationTest(
WebContents* web_contents,
const GURL& web_bundle_url,
const GURL& url_origin,
base::RepeatingCallback<GURL(const GURL&)> get_url_for_bundle) {
NavigateAndWaitForTitle(
web_contents, web_bundle_url,
get_url_for_bundle.Run(url_origin.Resolve("/top-page/")), "Ready");
NavigateAndWaitForTitle(
web_contents, web_bundle_url,
get_url_for_bundle.Run(url_origin.Resolve("/top-page/")), "Ready");
RunScriptAndObserveNavigation(
"Navigate to /iframe-test-page/", web_contents,
web_contents /* execution_target */,
"location.href = '/iframe-test-page/';",
{NAVIGATION_TYPE_NEW_ENTRY, NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
RunScriptAndObserveNavigation(
"Navigate the iframe to /1-page/#hash1", web_contents,
GetFirstChild(web_contents) /* execution_target */,
"location.href = '#hash1';", {NAVIGATION_TYPE_NEW_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
EXPECT_EQ(ExecuteAndGetString(GetFirstChild(web_contents), "location.href"),
url_origin.Resolve("/1-page/#hash1"));
RunScriptAndObserveNavigation(
"Navigate the iframe to /1-page/#hash2", web_contents,
GetFirstChild(web_contents) /* execution_target */,
"location.href = '#hash2';", {NAVIGATION_TYPE_NEW_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
EXPECT_EQ(ExecuteAndGetString(GetFirstChild(web_contents), "location.href"),
url_origin.Resolve("/1-page/#hash2"));
RunScriptAndObserveNavigation(
"Navigate the iframe to /2-page/", web_contents,
GetFirstChild(web_contents) /* execution_target */,
"location.href = '/2-page/';", {NAVIGATION_TYPE_NEW_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/2-page/ from wbn, /2-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
EXPECT_EQ(ExecuteAndGetString(GetFirstChild(web_contents), "location.href"),
url_origin.Resolve("/2-page/"));
RunScriptAndObserveNavigation(
"Back navigate the iframe to /1-page/#hash2", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
EXPECT_EQ(ExecuteAndGetString(GetFirstChild(web_contents), "location.href"),
url_origin.Resolve("/1-page/#hash2"));
RunScriptAndObserveNavigation(
"Back navigate the iframe to /1-page/#hash1", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
EXPECT_EQ(ExecuteAndGetString(GetFirstChild(web_contents), "location.href"),
url_origin.Resolve("/1-page/#hash1"));
RunScriptAndObserveNavigation(
"Back navigate the iframe to /1-page/", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
EXPECT_EQ(ExecuteAndGetString(GetFirstChild(web_contents), "location.href"),
url_origin.Resolve("/1-page/"));
RunScriptAndObserveNavigation(
"Back navigate to /top-page/", web_contents,
web_contents /* execution_target */, "history.back();",
{NAVIGATION_TYPE_EXISTING_ENTRY},
get_url_for_bundle.Run(
url_origin.Resolve("/top-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/top-page/") /* expected_last_inner_url */,
"/top-page/ from wbn, /top-page/script from wbn");
RunScriptAndObserveNavigation(
"Forward navigate to /iframe-test-page/", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_EXISTING_ENTRY, NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
EXPECT_EQ(ExecuteAndGetString(GetFirstChild(web_contents), "location.href"),
url_origin.Resolve("/1-page/"));
RunScriptAndObserveNavigation(
"Forward navigate the iframe to /1-page/#hash1", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
EXPECT_EQ(ExecuteAndGetString(GetFirstChild(web_contents), "location.href"),
url_origin.Resolve("/1-page/#hash1"));
RunScriptAndObserveNavigation(
"Forward navigate the iframe to /1-page/#hash2", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/1-page/ from wbn, /1-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
EXPECT_EQ(ExecuteAndGetString(GetFirstChild(web_contents), "location.href"),
url_origin.Resolve("/1-page/#hash2"));
RunScriptAndObserveNavigation(
"Forward navigate the iframe to /2-page/", web_contents,
web_contents /* execution_target */, "history.forward();",
{NAVIGATION_TYPE_AUTO_SUBFRAME},
get_url_for_bundle.Run(url_origin.Resolve(
"/iframe-test-page/")) /* expected_last_comitted_url */,
url_origin.Resolve("/iframe-test-page/") /* expected_last_inner_url */,
"/iframe-test-page/ from wbn, /iframe-test-page/script from wbn");
EXPECT_EQ("/2-page/ from wbn, /2-page/script from wbn",
GetLoadResultForNavigationTest(GetFirstChild(web_contents)));
}
} // namespace
class InvalidTrustableWebBundleFileUrlBrowserTest : public ContentBrowserTest {
protected:
InvalidTrustableWebBundleFileUrlBrowserTest() = default;
~InvalidTrustableWebBundleFileUrlBrowserTest() override = default;
void SetUpOnMainThread() override {
ContentBrowserTest::SetUpOnMainThread();
original_client_ = SetBrowserClientForTesting(&browser_client_);
}
void TearDownOnMainThread() override {
ContentBrowserTest::TearDownOnMainThread();
SetBrowserClientForTesting(original_client_);
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(switches::kTrustableWebBundleFileUrl,
kInvalidFileUrl);
}
private:
ContentBrowserClient* original_client_ = nullptr;
TestBrowserClient browser_client_;
DISALLOW_COPY_AND_ASSIGN(InvalidTrustableWebBundleFileUrlBrowserTest);
};
IN_PROC_BROWSER_TEST_F(InvalidTrustableWebBundleFileUrlBrowserTest,
NoCrashOnNavigation) {
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());
}
enum class TestFilePathMode {
kNormalFilePath,
#if defined(OS_ANDROID)
kContentURI,
#endif // OS_ANDROID
};
#if defined(OS_ANDROID)
#define TEST_FILE_PATH_MODE_PARAMS \
testing::Values(TestFilePathMode::kNormalFilePath, \
TestFilePathMode::kContentURI)
#else
#define TEST_FILE_PATH_MODE_PARAMS \
testing::Values(TestFilePathMode::kNormalFilePath)
#endif // OS_ANDROID
class WebBundleTrustableFileBrowserTest
: public testing::WithParamInterface<TestFilePathMode>,
public WebBundleBrowserTestBase {
protected:
WebBundleTrustableFileBrowserTest() = default;
~WebBundleTrustableFileBrowserTest() override = default;
void SetUp() override {
InitializeTestDataUrl();
SetEmptyPageUrl();
WebBundleBrowserTestBase::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(switches::kTrustableWebBundleFileUrl,
test_data_url().spec());
}
void WriteWebBundleFile(const std::string& contents) {
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_FALSE(contents.empty());
ASSERT_TRUE(base::WriteFile(test_data_file_path_, contents.data(),
contents.size()) > 0);
}
void WriteCommonWebBundleFile() {
std::string contents;
{
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(base::ReadFileToString(
GetTestDataPath("web_bundle_browsertest.wbn"), &contents));
}
WriteWebBundleFile(contents);
}
const GURL& test_data_url() const { return test_data_url_; }
const GURL& empty_page_url() const { return empty_page_url_; }
void RunSharedNavigationTest(
void (*setup_func)(net::EmbeddedTestServer*, GURL*, std::string*),
void (*run_test_func)(WebContents*,
const GURL&,
const GURL&,
base::RepeatingCallback<GURL(const GURL&)>)) {
GURL url_origin;
std::string web_bundle_content;
(*setup_func)(embedded_test_server(), &url_origin, &web_bundle_content);
WriteWebBundleFile(web_bundle_content);
(*run_test_func)(shell()->web_contents(), test_data_url(), url_origin,
base::BindRepeating([](const GURL& url) { return url; }));
}
private:
void InitializeTestDataUrl() {
base::FilePath file_path;
CreateTemporaryWebBundleFile("", &file_path);
if (GetParam() == TestFilePathMode::kNormalFilePath) {
test_data_file_path_ = file_path;
test_data_url_ = net::FilePathToFileURL(file_path);
return;
}
#if defined(OS_ANDROID)
DCHECK_EQ(TestFilePathMode::kContentURI, GetParam());
CopyFileAndGetContentUri(file_path, &test_data_url_, &test_data_file_path_);
#endif // OS_ANDROID
}
void SetEmptyPageUrl() {
if (GetParam() == TestFilePathMode::kNormalFilePath) {
empty_page_url_ =
net::FilePathToFileURL(GetTestDataPath("empty_page.html"));
return;
}
#if defined(OS_ANDROID)
DCHECK_EQ(TestFilePathMode::kContentURI, GetParam());
CopyFileAndGetContentUri(GetTestDataPath("empty_page.html"),
&empty_page_url_, nullptr /* new_file_path */);
#endif // OS_ANDROID
}
GURL test_data_url_;
base::FilePath test_data_file_path_;
GURL empty_page_url_;
DISALLOW_COPY_AND_ASSIGN(WebBundleTrustableFileBrowserTest);
};
IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest,
TrustableWebBundleFile) {
WriteCommonWebBundleFile();
NavigateToBundleAndWaitForReady(test_data_url(), GURL(kTestPageUrl));
}
IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, RangeRequest) {
WriteCommonWebBundleFile();
NavigateToBundleAndWaitForReady(test_data_url(), GURL(kTestPageUrl));
RunTestScript("test-range-request.js");
}
IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, BasicNavigation) {
RunSharedNavigationTest(&SetUpBasicNavigationTest, &RunBasicNavigationTest);
}
IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest,
BrowserInitiatedOutOfBundleNavigation) {
RunSharedNavigationTest(&SetUpBrowserInitiatedOutOfBundleNavigationTest,
&RunBrowserInitiatedOutOfBundleNavigationTest);
}
IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest,
RendererInitiatedOutOfBundleNavigation) {
RunSharedNavigationTest(&SetUpRendererInitiatedOutOfBundleNavigationTest,
&RunRendererInitiatedOutOfBundleNavigationTest);
}
IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest,
SameDocumentNavigation) {
RunSharedNavigationTest(&SetUpSameDocumentNavigationTest,
&RunSameDocumentNavigationTest);
}
#if defined(OS_ANDROID)
#define MAYBE_IframeNavigation DISABLED_IframeNavigation
#else
#define MAYBE_IframeNavigation IframeNavigation
#endif
IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest,
MAYBE_IframeNavigation) {
RunSharedNavigationTest(&SetUpIframeNavigationTest, &RunIframeNavigationTest);
}
IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest,
IframeOutOfBundleNavigation) {
RunSharedNavigationTest(&SetUpIframeNavigationTest,
&RunIframeOutOfBundleNavigationTest);
}
IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest,
IframeParentInitiatedOutOfBundleNavigation) {
RunSharedNavigationTest(&SetUpIframeNavigationTest,
&RunIframeParentInitiatedOutOfBundleNavigationTest);
}
#if defined(OS_ANDROID)
#define MAYBE_IframeSameDocumentNavigation DISABLED_IframeSameDocumentNavigation
#else
#define MAYBE_IframeSameDocumentNavigation IframeSameDocumentNavigation
#endif
IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest,
MAYBE_IframeSameDocumentNavigation) {
RunSharedNavigationTest(&SetUpIframeNavigationTest,
&RunIframeSameDocumentNavigationTest);
}
IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, BaseURI) {
WriteCommonWebBundleFile();
NavigateToBundleAndWaitForReady(test_data_url(), GURL(kTestPageUrl));
EXPECT_EQ(ExecuteAndGetString(shell()->web_contents(),
"(new Request('./foo/bar')).url"),
"https://test.example.org/foo/bar");
EXPECT_EQ(ExecuteAndGetString(shell()->web_contents(), R"(
(() => {
const base_element = document.createElement('base');
base_element.href = 'https://example.org/piyo/';
document.body.appendChild(base_element);
return document.baseURI;
})()
)"),
"https://example.org/piyo/");
EXPECT_EQ(ExecuteAndGetString(shell()->web_contents(),
"(new Request('./foo/bar')).url"),
"https://example.org/piyo/foo/bar");
}
IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, Iframe) {
net::EmbeddedTestServer third_party_server;
GURL primary_url_origin;
GURL third_party_origin;
std::string web_bundle_content;
SetUpSubPageTest(embedded_test_server(), &third_party_server,
&primary_url_origin, &third_party_origin,
&web_bundle_content);
WriteWebBundleFile(web_bundle_content);
NavigateToBundleAndWaitForReady(test_data_url(),
primary_url_origin.Resolve("/top"));
RunSubPageTest(shell()->web_contents(), primary_url_origin,
third_party_origin, &AddIframeAndWaitForMessage,
true /* support_third_party_wbn_page */);
}
IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, WindowOpen) {
net::EmbeddedTestServer third_party_server;
GURL primary_url_origin;
GURL third_party_origin;
std::string web_bundle_content;
SetUpSubPageTest(embedded_test_server(), &third_party_server,
&primary_url_origin, &third_party_origin,
&web_bundle_content);
WriteWebBundleFile(web_bundle_content);
NavigateToBundleAndWaitForReady(test_data_url(),
primary_url_origin.Resolve("/top"));
RunSubPageTest(shell()->web_contents(), primary_url_origin,
third_party_origin, &WindowOpenAndWaitForMessage,
true /* support_third_party_wbn_page */);
}
INSTANTIATE_TEST_SUITE_P(WebBundleTrustableFileBrowserTest,
WebBundleTrustableFileBrowserTest,
TEST_FILE_PATH_MODE_PARAMS);
class WebBundleTrustableFileNotFoundBrowserTest
: public WebBundleBrowserTestBase {
protected:
WebBundleTrustableFileNotFoundBrowserTest() = default;
~WebBundleTrustableFileNotFoundBrowserTest() override = default;
void SetUp() override {
test_data_url_ = net::FilePathToFileURL(GetTestDataPath("not_found"));
WebBundleBrowserTestBase::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(switches::kTrustableWebBundleFileUrl,
test_data_url().spec());
}
const GURL& test_data_url() const { return test_data_url_; }
private:
GURL test_data_url_;
DISALLOW_COPY_AND_ASSIGN(WebBundleTrustableFileNotFoundBrowserTest);
};
// TODO(https://crbug.com/1227439): flaky
#if defined(OS_LINUX)
#define MAYBE_NotFound DISABLED_NotFound
#else
#define MAYBE_NotFound NotFound
#endif
IN_PROC_BROWSER_TEST_F(WebBundleTrustableFileNotFoundBrowserTest, MAYBE_NotFound) {
std::string console_message = ExpectNavigationFailureAndReturnConsoleMessage(
shell()->web_contents(), test_data_url());
EXPECT_EQ("Failed to read metadata of Web Bundle file: FILE_ERROR_FAILED",
console_message);
}
class WebBundleFileBrowserTest
: public testing::WithParamInterface<TestFilePathMode>,
public WebBundleBrowserTestBase {
protected:
WebBundleFileBrowserTest() = default;
~WebBundleFileBrowserTest() override = default;
void SetUp() override {
feature_list_.InitWithFeatures({features::kWebBundles}, {});
WebBundleBrowserTestBase::SetUp();
}
GURL GetTestUrlForFile(base::FilePath file_path) const {
GURL content_uri;
if (GetParam() == TestFilePathMode::kNormalFilePath) {
content_uri = net::FilePathToFileURL(file_path);
} else {
#if defined(OS_ANDROID)
DCHECK_EQ(TestFilePathMode::kContentURI, GetParam());
CopyFileAndGetContentUri(file_path, &content_uri,
nullptr /* new_file_path */);
#endif // OS_ANDROID
}
return content_uri;
}
void RunSharedNavigationTest(
void (*setup_func)(net::EmbeddedTestServer*, GURL*, std::string*),
void (*run_test_func)(WebContents*,
const GURL&,
const GURL&,
base::RepeatingCallback<GURL(const GURL&)>)) {
GURL url_origin;
std::string web_bundle_content;
(*setup_func)(embedded_test_server(), &url_origin, &web_bundle_content);
base::FilePath file_path;
CreateTemporaryWebBundleFile(web_bundle_content, &file_path);
const GURL test_data_url = GetTestUrlForFile(file_path);
(*run_test_func)(
shell()->web_contents(), test_data_url, url_origin,
base::BindRepeating(&web_bundle_utils::GetSynthesizedUrlForWebBundle,
test_data_url));
}
private:
base::test::ScopedFeatureList feature_list_;
DISALLOW_COPY_AND_ASSIGN(WebBundleFileBrowserTest);
};
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, BasicNavigation) {
RunSharedNavigationTest(&SetUpBasicNavigationTest, &RunBasicNavigationTest);
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest,
BrowserInitiatedOutOfBundleNavigation) {
RunSharedNavigationTest(&SetUpBrowserInitiatedOutOfBundleNavigationTest,
&RunBrowserInitiatedOutOfBundleNavigationTest);
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest,
RendererInitiatedOutOfBundleNavigation) {
RunSharedNavigationTest(&SetUpRendererInitiatedOutOfBundleNavigationTest,
&RunRendererInitiatedOutOfBundleNavigationTest);
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, SameDocumentNavigation) {
RunSharedNavigationTest(&SetUpSameDocumentNavigationTest,
&RunSameDocumentNavigationTest);
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, IframeNavigation) {
RunSharedNavigationTest(&SetUpIframeNavigationTest, &RunIframeNavigationTest);
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, IframeOutOfBundleNavigation) {
RunSharedNavigationTest(&SetUpIframeNavigationTest,
&RunIframeOutOfBundleNavigationTest);
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest,
IframeParentInitiatedOutOfBundleNavigation) {
RunSharedNavigationTest(&SetUpIframeNavigationTest,
&RunIframeParentInitiatedOutOfBundleNavigationTest);
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, IframeSameDocumentNavigation) {
RunSharedNavigationTest(&SetUpIframeNavigationTest,
&RunIframeSameDocumentNavigationTest);
}
// TODO(https://crbug.com/1225178): flaky
#if defined(OS_LINUX)
#define MAYBE_InvalidWebBundleFile DISABLED_InvalidWebBundleFile
#else
#define MAYBE_InvalidWebBundleFile InvalidWebBundleFile
#endif
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, MAYBE_InvalidWebBundleFile) {
const GURL test_data_url =
GetTestUrlForFile(GetTestDataPath("invalid_web_bundle.wbn"));
std::string console_message = ExpectNavigationFailureAndReturnConsoleMessage(
shell()->web_contents(), test_data_url);
EXPECT_EQ("Failed to read metadata of Web Bundle file: Wrong magic bytes.",
console_message);
}
// TODO(https://crbug.com/1225178): flaky
#if defined(OS_LINUX)
#define MAYBE_ResponseParseErrorInMainResource \
DISABLED_ResponseParseErrorInMainResource
#else
#define MAYBE_ResponseParseErrorInMainResource ResponseParseErrorInMainResource
#endif
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest,
MAYBE_ResponseParseErrorInMainResource) {
const GURL test_data_url = GetTestUrlForFile(
GetTestDataPath("broken_bundle_broken_first_entry.wbn"));
std::string console_message = ExpectNavigationFailureAndReturnConsoleMessage(
shell()->web_contents(), test_data_url);
EXPECT_EQ(
"Failed to read response header of Web Bundle file: Response headers map "
"must have exactly one pseudo-header, :status.",
console_message);
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest,
ResponseParseErrorInSubresource) {
const GURL test_data_url = GetTestUrlForFile(
GetTestDataPath("broken_bundle_broken_script_entry.wbn"));
NavigateToBundleAndWaitForReady(
test_data_url, web_bundle_utils::GetSynthesizedUrlForWebBundle(
test_data_url, GURL(kTestPageUrl)));
WebContents* web_contents = shell()->web_contents();
WebContentsConsoleObserver console_observer(web_contents);
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_observer.messages().empty())
console_observer.Wait();
ASSERT_FALSE(console_observer.messages().empty());
EXPECT_EQ(
"Failed to read response header of Web Bundle file: Response headers map "
"must have exactly one pseudo-header, :status.",
base::UTF16ToUTF8(console_observer.messages()[0].message));
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, NoLocalFileScheme) {
const GURL test_data_url =
GetTestUrlForFile(GetTestDataPath("web_bundle_browsertest.wbn"));
NavigateToBundleAndWaitForReady(
test_data_url, web_bundle_utils::GetSynthesizedUrlForWebBundle(
test_data_url, GURL(kTestPageUrl)));
auto* expected_title = u"load failed";
TitleWatcher title_watcher(shell()->web_contents(), expected_title);
title_watcher.AlsoWaitForTitle(u"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(ExecJs(shell()->web_contents(), script));
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, DataDecoderRestart) {
constexpr char kTestPage1Url[] = "https://test.example.org/page1.html";
constexpr char kTestPage2Url[] = "https://test.example.org/page2.html";
// The content of this file will be read as response body of any exchange.
base::FilePath test_file_path = GetTestDataPath("mocked.wbn");
MockParserFactory mock_factory(
{GURL(kTestPageUrl), GURL(kTestPage1Url), GURL(kTestPage2Url)},
test_file_path);
const GURL test_data_url = GetTestUrlForFile(test_file_path);
NavigateAndWaitForTitle(shell()->web_contents(), test_data_url,
web_bundle_utils::GetSynthesizedUrlForWebBundle(
test_data_url, GURL(kTestPageUrl)),
kTestPageUrl);
EXPECT_EQ(1, mock_factory.GetParserCreationCount());
mock_factory.SimulateParserDisconnect();
NavigateToURLAndWaitForTitle(GURL(kTestPage1Url), kTestPage1Url);
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
web_bundle_utils::GetSynthesizedUrlForWebBundle(
test_data_url, GURL(kTestPage1Url)));
EXPECT_EQ(2, mock_factory.GetParserCreationCount());
mock_factory.SimulateParserDisconnect();
NavigateToURLAndWaitForTitle(GURL(kTestPage2Url), kTestPage2Url);
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
web_bundle_utils::GetSynthesizedUrlForWebBundle(
test_data_url, GURL(kTestPage2Url)));
EXPECT_EQ(3, mock_factory.GetParserCreationCount());
}
// TODO(https://crbug.com/1225178): flaky
#if defined(OS_LINUX)
#define MAYBE_ParseMetadataCrash DISABLED_ParseMetadataCrash
#else
#define MAYBE_ParseMetadataCrash ParseMetadataCrash
#endif
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, MAYBE_ParseMetadataCrash) {
base::FilePath test_file_path = GetTestDataPath("mocked.wbn");
MockParserFactory mock_factory({GURL(kTestPageUrl)}, test_file_path);
mock_factory.SimulateParseMetadataCrash();
std::string console_message = ExpectNavigationFailureAndReturnConsoleMessage(
shell()->web_contents(), GetTestUrlForFile(test_file_path));
EXPECT_EQ(
"Failed to read metadata of Web Bundle file: Cannot connect to the "
"remote parser service",
console_message);
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, ParseResponseCrash) {
base::FilePath test_file_path = GetTestDataPath("mocked.wbn");
MockParserFactory mock_factory({GURL(kTestPageUrl)}, test_file_path);
mock_factory.SimulateParseResponseCrash();
std::string console_message = ExpectNavigationFailureAndReturnConsoleMessage(
shell()->web_contents(), GetTestUrlForFile(test_file_path));
EXPECT_EQ(
"Failed to read response header of Web Bundle file: Cannot connect to "
"the remote parser service",
console_message);
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, Variants) {
SetAcceptLangs("ja,en");
const GURL test_data_url =
GetTestUrlForFile(GetTestDataPath("variants_test.wbn"));
NavigateAndWaitForTitle(shell()->web_contents(), test_data_url,
web_bundle_utils::GetSynthesizedUrlForWebBundle(
test_data_url, GURL(kTestPageUrl)),
"lang=ja");
SetAcceptLangs("en,ja");
NavigateAndWaitForTitle(shell()->web_contents(), test_data_url,
web_bundle_utils::GetSynthesizedUrlForWebBundle(
test_data_url, GURL(kTestPageUrl)),
"lang=en");
ExecuteScriptAndWaitForTitle(R"(
(async function() {
const headers = {Accept: 'application/octet-stream'};
const resp = await fetch('/type', {headers});
const data = await resp.json();
document.title = data.text;
})();)",
"octet-stream");
ExecuteScriptAndWaitForTitle(R"(
(async function() {
const headers = {Accept: 'application/json'};
const resp = await fetch('/type', {headers});
const data = await resp.json();
document.title = data.text;
})();)",
"json");
ExecuteScriptAndWaitForTitle(R"(
(async function() {
const headers = {Accept: 'foo/bar'};
const resp = await fetch('/type', {headers});
const data = await resp.json();
document.title = data.text;
})();)",
"octet-stream");
ExecuteScriptAndWaitForTitle(R"(
(async function() {
const resp = await fetch('/lang');
const data = await resp.json();
document.title = data.text;
})();)",
"ja");
// If Accept-Language header is explicitly set, respect it.
ExecuteScriptAndWaitForTitle(R"(
(async function() {
const headers = {'Accept-Language': 'fr'};
const resp = await fetch('/lang', {headers});
const data = await resp.json();
document.title = data.text;
})();)",
"fr");
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, IframeNavigationNoCrash) {
// Regression test for crbug.com/1058721. There was a bug that navigation of
// OOPIF's remote iframe in Web Bundle file cause crash.
const GURL test_data_url =
GetTestUrlForFile(GetTestDataPath("web_bundle_browsertest.wbn"));
NavigateToBundleAndWaitForReady(
test_data_url, web_bundle_utils::GetSynthesizedUrlForWebBundle(
test_data_url, GURL(kTestPageUrl)));
const std::string empty_page_path = "/web_bundle/empty_page.html";
ASSERT_TRUE(embedded_test_server()->Start());
const GURL empty_page_url = embedded_test_server()->GetURL(empty_page_path);
ExecuteScriptAndWaitForTitle(
base::StringPrintf(R"(
(async function() {
const empty_page_url = '%s';
const iframe = document.createElement('iframe');
const onload = () => {
iframe.removeEventListener('load', onload);
document.title = 'Iframe loaded';
}
iframe.addEventListener('load', onload);
iframe.src = empty_page_url;
document.body.appendChild(iframe);
})();)",
empty_page_url.spec().c_str()),
"Iframe loaded");
ExecuteScriptAndWaitForTitle(R"(
(async function() {
const iframe = document.querySelector("iframe");
const onload = () => {
document.title = 'Iframe loaded again';
}
iframe.addEventListener('load', onload);
iframe.src = iframe.src + '?';
})();)",
"Iframe loaded again");
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, Iframe) {
net::EmbeddedTestServer third_party_server;
GURL primary_url_origin;
GURL third_party_origin;
std::string web_bundle_content;
SetUpSubPageTest(embedded_test_server(), &third_party_server,
&primary_url_origin, &third_party_origin,
&web_bundle_content);
base::FilePath file_path;
CreateTemporaryWebBundleFile(web_bundle_content, &file_path);
const GURL test_data_url = GetTestUrlForFile(file_path);
NavigateToBundleAndWaitForReady(
test_data_url, web_bundle_utils::GetSynthesizedUrlForWebBundle(
test_data_url, primary_url_origin.Resolve("/top")));
RunSubPageTest(shell()->web_contents(), primary_url_origin,
third_party_origin, &AddIframeAndWaitForMessage,
true /* support_third_party_wbn_page */);
}
IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, WindowOpen) {
net::EmbeddedTestServer third_party_server;
GURL primary_url_origin;
GURL third_party_origin;
std::string web_bundle_content;
SetUpSubPageTest(embedded_test_server(), &third_party_server,
&primary_url_origin, &third_party_origin,
&web_bundle_content);
base::FilePath file_path;
CreateTemporaryWebBundleFile(web_bundle_content, &file_path);
const GURL test_data_url = GetTestUrlForFile(file_path);
NavigateToBundleAndWaitForReady(
test_data_url, web_bundle_utils::GetSynthesizedUrlForWebBundle(
test_data_url, primary_url_origin.Resolve("/top")));
RunSubPageTest(shell()->web_contents(), primary_url_origin,
third_party_origin, &WindowOpenAndWaitForMessage,
true /* support_third_party_wbn_page */);
}
INSTANTIATE_TEST_SUITE_P(WebBundleFileBrowserTest,
WebBundleFileBrowserTest,
TEST_FILE_PATH_MODE_PARAMS);
class WebBundleNetworkBrowserTest : public WebBundleBrowserTestBase {
protected:
WebBundleNetworkBrowserTest() = default;
~WebBundleNetworkBrowserTest() override = default;
void SetUpOnMainThread() override {
WebBundleBrowserTestBase::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
}
void TearDownOnMainThread() override {
// Shutdown the server to avoid the data race of |headers_| and |contents_|
// caused by page reload on error.
EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
WebBundleBrowserTestBase::TearDownOnMainThread();
}
void SetUp() override {
feature_list_.InitWithFeatures({features::kWebBundlesFromNetwork}, {});
WebBundleBrowserTestBase::SetUp();
}
void RegisterRequestHandler(const std::string& relative_url) {
embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting(
[this, relative_url](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_);
}));
}
void TestNavigationFailure(const GURL& url,
const std::string& expected_console_error) {
std::string console_message =
ExpectNavigationFailureAndReturnConsoleMessage(shell()->web_contents(),
url);
EXPECT_EQ(expected_console_error, console_message);
}
void HistoryBackAndWaitUntilConsoleError(
const std::string& expected_error_message) {
WebContents* web_contents = shell()->web_contents();
WebContentsConsoleObserver console_observer(web_contents);
base::RunLoop run_loop;
FinishNavigationObserver finish_navigation_observer(web_contents,
run_loop.QuitClosure());
EXPECT_TRUE(ExecJs(web_contents, "history.back();"));
run_loop.Run();
ASSERT_TRUE(finish_navigation_observer.error_code());
EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
*finish_navigation_observer.error_code());
if (console_observer.messages().empty())
console_observer.Wait();
ASSERT_FALSE(console_observer.messages().empty());
EXPECT_EQ(expected_error_message,
base::UTF16ToUTF8(console_observer.messages()[0].message));
}
void SetHeaders(const std::string& headers) { headers_ = headers; }
void AddHeaders(const std::string& headers) { headers_ += headers; }
void SetContents(const std::string& contents) { contents_ = contents; }
const std::string& contents() const { return contents_; }
void RunSharedNavigationTest(
void (*setup_func)(net::EmbeddedTestServer*, GURL*, std::string*),
void (*run_test_func)(WebContents*,
const GURL&,
const GURL&,
base::RepeatingCallback<GURL(const GURL&)>)) {
const std::string wbn_path = "/test.wbn";
RegisterRequestHandler(wbn_path);
GURL url_origin;
std::string web_bundle_content;
(*setup_func)(embedded_test_server(), &url_origin, &web_bundle_content);
SetContents(web_bundle_content);
(*run_test_func)(shell()->web_contents(), url_origin.Resolve(wbn_path),
url_origin,
base::BindRepeating([](const GURL& url) { return url; }));
}
private:
base::test::ScopedFeatureList feature_list_;
std::string headers_ = kDefaultHeaders;
std::string contents_;
DISALLOW_COPY_AND_ASSIGN(WebBundleNetworkBrowserTest);
};
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, Simple) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/web_bundle/test.html";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url = embedded_test_server()->GetURL(primary_url_path);
SetContents(CreateSimpleWebBundle(primary_url));
NavigateToBundleAndWaitForReady(wbn_url, primary_url);
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, SimpleWithScript) {
const std::string wbn_path = "/web_bundle/test.wbn";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url =
embedded_test_server()->GetURL("/web_bundle/test.html");
const GURL script_url =
embedded_test_server()->GetURL("/web_bundle/script.js");
web_package::test::WebBundleBuilder builder(primary_url.spec(), "");
builder.AddExchange(primary_url.spec(),
{{":status", "200"}, {"content-type", "text/html"}},
"<script src=\"script.js\"></script>");
builder.AddExchange(
script_url.spec(),
{{":status", "200"}, {"content-type", "application/javascript"}},
"document.title = 'Ready';");
std::vector<uint8_t> bundle = builder.CreateBundle();
SetContents(std::string(bundle.begin(), bundle.end()));
NavigateToBundleAndWaitForReady(wbn_url, primary_url);
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, Download) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/web_bundle/test.html";
RegisterRequestHandler(wbn_path);
AddHeaders("Content-Disposition:attachment; filename=test.wbn\n");
ASSERT_TRUE(embedded_test_server()->Start());
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url = embedded_test_server()->GetURL(primary_url_path);
SetContents(CreateSimpleWebBundle(primary_url));
WebContents* web_contents = shell()->web_contents();
std::unique_ptr<DownloadObserver> download_observer =
std::make_unique<DownloadObserver>(
web_contents->GetBrowserContext()->GetDownloadManager());
EXPECT_FALSE(NavigateToURL(web_contents, wbn_url));
download_observer->WaitUntilDownloadCreated();
EXPECT_EQ(wbn_url, download_observer->observed_url());
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, ContentLength) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/web_bundle/test.html";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url = embedded_test_server()->GetURL(primary_url_path);
SetContents(CreateSimpleWebBundle(primary_url));
AddHeaders(
base::StringPrintf("Content-Length: %" PRIuS "\n", contents().size()));
NavigateToBundleAndWaitForReady(wbn_url, primary_url);
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, NonSecureUrl) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/web_bundle/test.html";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL wbn_url = embedded_test_server()->GetURL("example.com", wbn_path);
const GURL primary_url =
embedded_test_server()->GetURL("example.com", primary_url_path);
SetContents(CreateSimpleWebBundle(primary_url));
TestNavigationFailure(
wbn_url,
"Web Bundle response must be served from HTTPS or localhost HTTP.");
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, MissingNosniff) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/web_bundle/test.html";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url = embedded_test_server()->GetURL(primary_url_path);
SetContents(CreateSimpleWebBundle(primary_url));
SetHeaders(
"HTTP/1.1 200 OK\n"
"Content-Type: application/webbundle\n");
TestNavigationFailure(wbn_url,
"Web Bundle response must have "
"\"X-Content-Type-Options: nosniff\" header.");
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, PrimaryURLNotFound) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/web_bundle/test.html";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url = embedded_test_server()->GetURL(primary_url_path);
const GURL inner_url =
embedded_test_server()->GetURL("/web_bundle/inner.html");
web_package::test::WebBundleBuilder builder(primary_url.spec(), "");
builder.AddExchange(inner_url.spec(),
{{":status", "200"}, {"content-type", "text/html"}},
"<title>Ready</title>");
std::vector<uint8_t> bundle = builder.CreateBundle();
SetContents(std::string(bundle.begin(), bundle.end()));
TestNavigationFailure(
wbn_url, "The primary URL resource is not found in the web bundle.");
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, OriginMismatch) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/web_bundle/test.html";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL primary_url =
embedded_test_server()->GetURL("localhost", primary_url_path);
SetContents(CreateSimpleWebBundle(primary_url));
TestNavigationFailure(
embedded_test_server()->GetURL("127.0.0.1", wbn_path),
"The origin of primary URL doesn't match with the origin of the web "
"bundle.");
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, InvalidFile) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/web_bundle/test.html";
RegisterRequestHandler(wbn_path);
SetContents("This is an invalid Web Bundle file.");
ASSERT_TRUE(embedded_test_server()->Start());
TestNavigationFailure(
embedded_test_server()->GetURL(wbn_path),
"Failed to read metadata of Web Bundle file: Wrong magic bytes.");
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, DataDecoderRestart) {
const std::string wbn_path = "/web_bundle/test.wbn";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL primary_url =
embedded_test_server()->GetURL("/web_bundle/test.html");
const GURL script_url =
embedded_test_server()->GetURL("/web_bundle/script.js");
const std::string primary_url_content = "<title>Ready</title>";
const std::string script_url_content = "document.title = 'OK'";
SetContents(primary_url_content + script_url_content);
std::vector<std::pair<GURL, const std::string&>> items = {
{primary_url, primary_url_content}, {script_url, script_url_content}};
MockParserFactory mock_factory(items);
NavigateToBundleAndWaitForReady(embedded_test_server()->GetURL(wbn_path),
primary_url);
EXPECT_EQ(1, mock_factory.GetParserCreationCount());
mock_factory.SimulateParserDisconnect();
ExecuteScriptAndWaitForTitle(R"(
const script = document.createElement("script");
script.src = "script.js";
document.body.appendChild(script);)",
"OK");
EXPECT_EQ(2, mock_factory.GetParserCreationCount());
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, ParseMetadataCrash) {
const std::string wbn_path = "/web_bundle/test.wbn";
RegisterRequestHandler(wbn_path);
SetContents("<title>Ready</title>");
ASSERT_TRUE(embedded_test_server()->Start());
const GURL primary_url =
embedded_test_server()->GetURL("/web_bundle/test.html");
std::vector<std::pair<GURL, const std::string&>> items = {
{primary_url, contents()}};
MockParserFactory mock_factory(items);
mock_factory.SimulateParseMetadataCrash();
TestNavigationFailure(embedded_test_server()->GetURL(wbn_path),
"Failed to read metadata of Web Bundle file: Cannot "
"connect to the remote parser service");
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, ParseResponseCrash) {
const std::string wbn_path = "/web_bundle/test.wbn";
RegisterRequestHandler(wbn_path);
SetContents("<title>Ready</title>");
ASSERT_TRUE(embedded_test_server()->Start());
const GURL primary_url =
embedded_test_server()->GetURL("/web_bundle/test.html");
std::vector<std::pair<GURL, const std::string&>> items = {
{primary_url, contents()}};
MockParserFactory mock_factory(items);
mock_factory.SimulateParseResponseCrash();
TestNavigationFailure(embedded_test_server()->GetURL(wbn_path),
"Failed to read response header of Web Bundle file: "
"Cannot connect to the remote parser service");
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, PathMismatch) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/other_dir/test.html";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url = embedded_test_server()->GetURL(primary_url_path);
SetContents(CreateSimpleWebBundle(primary_url));
TestNavigationFailure(
wbn_url,
base::StringPrintf("Path restriction mismatch: Can't navigate to %s in "
"the web bundle served from %s.",
primary_url.spec().c_str(), wbn_url.spec().c_str()));
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, BasicNavigation) {
RunSharedNavigationTest(&SetUpBasicNavigationTest, &RunBasicNavigationTest);
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest,
BrowserInitiatedOutOfBundleNavigation) {
RunSharedNavigationTest(&SetUpBrowserInitiatedOutOfBundleNavigationTest,
&RunBrowserInitiatedOutOfBundleNavigationTest);
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest,
RendererInitiatedOutOfBundleNavigation) {
RunSharedNavigationTest(&SetUpRendererInitiatedOutOfBundleNavigationTest,
&RunRendererInitiatedOutOfBundleNavigationTest);
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, SameDocumentNavigation) {
RunSharedNavigationTest(&SetUpSameDocumentNavigationTest,
&RunSameDocumentNavigationTest);
}
// https://crbug.com/1219373 fails with BFCache field trial testing config.
#if defined(OS_ANDROID)
#define MAYBE_IframeNavigation DISABLED_IframeNavigation
#else
#define MAYBE_IframeNavigation IframeNavigation
#endif
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, MAYBE_IframeNavigation) {
RunSharedNavigationTest(&SetUpIframeNavigationTest, &RunIframeNavigationTest);
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest,
IframeOutOfBundleNavigation) {
RunSharedNavigationTest(&SetUpIframeNavigationTest,
&RunIframeOutOfBundleNavigationTest);
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest,
IframeParentInitiatedOutOfBundleNavigation) {
RunSharedNavigationTest(&SetUpIframeNavigationTest,
&RunIframeParentInitiatedOutOfBundleNavigationTest);
}
// https://crbug.com/1219373 fails with BFCache field trial testing config.
#if defined(OS_ANDROID)
#define MAYBE_IframeSameDocumentNavigation DISABLED_IframeSameDocumentNavigation
#else
#define MAYBE_IframeSameDocumentNavigation IframeSameDocumentNavigation
#endif
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest,
MAYBE_IframeSameDocumentNavigation) {
RunSharedNavigationTest(&SetUpIframeNavigationTest,
&RunIframeSameDocumentNavigationTest);
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, CrossScopeNavigations) {
const std::string wbn_path = "/web_bundle/path_test/in_scope/path_test.wbn";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
SetContents(CreatePathTestWebBundle(embedded_test_server()->base_url()));
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url =
embedded_test_server()->GetURL("/web_bundle/path_test/in_scope/");
NavigateToBundleAndWaitForReady(wbn_url, primary_url);
NavigateToURLAndWaitForTitle(
embedded_test_server()->GetURL(
"/web_bundle/path_test/in_scope/page.html"),
"In scope page in Web Bundle / in scope script in Web Bundle");
NavigateToURLAndWaitForTitle(
embedded_test_server()->GetURL(
"/web_bundle/path_test/out_scope/page.html"),
"Out scope page from server / out scope script from server");
NavigateToURLAndWaitForTitle(
embedded_test_server()->GetURL(
"/web_bundle/path_test/in_scope/page.html"),
"In scope page from server / in scope script from server");
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest,
CrossScopeHistoryNavigations) {
const std::string wbn_path = "/web_bundle/path_test/in_scope/path_test.wbn";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
SetContents(CreatePathTestWebBundle(embedded_test_server()->base_url()));
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url =
embedded_test_server()->GetURL("/web_bundle/path_test/in_scope/");
NavigateToBundleAndWaitForReady(wbn_url, primary_url);
NavigateToURLAndWaitForTitle(
embedded_test_server()->GetURL(
"/web_bundle/path_test/in_scope/page.html"),
"In scope page in Web Bundle / in scope script in Web Bundle");
NavigateToURLAndWaitForTitle(
embedded_test_server()->GetURL("/web_bundle/path_test/in_scope/"),
"Ready");
ExecuteScriptAndWaitForTitle(
"history.back();",
"In scope page in Web Bundle / in scope script in Web Bundle");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
embedded_test_server()->GetURL(
"/web_bundle/path_test/in_scope/page.html"));
NavigateToURLAndWaitForTitle(
embedded_test_server()->GetURL(
"/web_bundle/path_test/out_scope/page.html"),
"Out scope page from server / out scope script from server");
ExecuteScriptAndWaitForTitle(
"history.back();",
"In scope page in Web Bundle / in scope script in Web Bundle");
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(),
embedded_test_server()->GetURL(
"/web_bundle/path_test/in_scope/page.html"));
}
#if defined(OS_ANDROID)
#define MAYBE_HistoryNavigationError_UnexpectedContentType \
DISABLED_HistoryNavigationError_UnexpectedContentType
#else
#define MAYBE_HistoryNavigationError_UnexpectedContentType \
HistoryNavigationError_UnexpectedContentType
#endif
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest,
MAYBE_HistoryNavigationError_UnexpectedContentType) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/web_bundle/test.html";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url = embedded_test_server()->GetURL(primary_url_path);
SetHeaders(
"HTTP/1.1 200 OK\n"
"Cache-Control:no-store\n"
"Content-Type:application/webbundle\n"
"X-Content-Type-Options: nosniff\n");
SetContents(CreateSimpleWebBundle(primary_url));
NavigateToBundleAndWaitForReady(wbn_url, primary_url);
NavigateToURLAndWaitForTitle(
embedded_test_server()->GetURL("/web_bundle/empty_page.html"),
"Empty Page");
SetHeaders(
"HTTP/1.1 200 OK\n"
"Cache-Control:no-store\n"
"Content-Type:application/foo_bar\n"
"X-Content-Type-Options: nosniff\n");
HistoryBackAndWaitUntilConsoleError("Unexpected content type.");
}
#if defined(OS_ANDROID)
#define MAYBE_HistoryNavigationError_MissingNosniff \
DISABLED_HistoryNavigationError_MissingNosniff
#else
#define MAYBE_HistoryNavigationError_MissingNosniff \
HistoryNavigationError_MissingNosniff
#endif
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest,
MAYBE_HistoryNavigationError_MissingNosniff) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/web_bundle/test.html";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url = embedded_test_server()->GetURL(primary_url_path);
SetHeaders(
"HTTP/1.1 200 OK\n"
"Cache-Control:no-store\n"
"Content-Type:application/webbundle\n"
"X-Content-Type-Options: nosniff\n");
SetContents(CreateSimpleWebBundle(primary_url));
NavigateToBundleAndWaitForReady(wbn_url, primary_url);
NavigateToURLAndWaitForTitle(
embedded_test_server()->GetURL("/web_bundle/empty_page.html"),
"Empty Page");
SetHeaders(
"HTTP/1.1 200 OK\n"
"Cache-Control:no-store\n"
"Content-Type:application/webbundle\n");
HistoryBackAndWaitUntilConsoleError(
"Web Bundle response must have \"X-Content-Type-Options: nosniff\" "
"header.");
}
#if defined(OS_ANDROID)
#define MAYBE_HistoryNavigationError_UnexpectedRedirect \
DISABLED_HistoryNavigationError_UnexpectedRedirect
#else
#define MAYBE_HistoryNavigationError_UnexpectedRedirect \
HistoryNavigationError_UnexpectedRedirect
#endif
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest,
MAYBE_HistoryNavigationError_UnexpectedRedirect) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/web_bundle/test.html";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url = embedded_test_server()->GetURL(primary_url_path);
SetHeaders(
"HTTP/1.1 200 OK\n"
"Cache-Control:no-store\n"
"Content-Type:application/webbundle\n"
"X-Content-Type-Options: nosniff\n");
SetContents(CreateSimpleWebBundle(primary_url));
NavigateToBundleAndWaitForReady(wbn_url, primary_url);
NavigateToURLAndWaitForTitle(
embedded_test_server()->GetURL("/web_bundle/empty_page.html"),
"Empty Page");
SetHeaders(
"HTTP/1.1 302 OK\n"
"Location:/web_bundle/empty_page.html\n"
"X-Content-Type-Options: nosniff\n");
SetContents("");
HistoryBackAndWaitUntilConsoleError("Unexpected redirect.");
}
#if defined(OS_ANDROID)
#define MAYBE_HistoryNavigationError_ReadMetadataFailure \
DISABLED_HistoryNavigationError_ReadMetadataFailure
#else
#define MAYBE_HistoryNavigationError_ReadMetadataFailure \
HistoryNavigationError_ReadMetadataFailure
#endif
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest,
MAYBE_HistoryNavigationError_ReadMetadataFailure) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/web_bundle/test.html";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url = embedded_test_server()->GetURL(primary_url_path);
AddHeaders("Cache-Control:no-store\n");
SetContents(CreateSimpleWebBundle(primary_url));
NavigateToBundleAndWaitForReady(wbn_url, primary_url);
NavigateToURLAndWaitForTitle(
embedded_test_server()->GetURL("/web_bundle/empty_page.html"),
"Empty Page");
SetContents("This is an invalid Web Bundle file.");
HistoryBackAndWaitUntilConsoleError(
"Failed to read metadata of Web Bundle file: Wrong magic bytes.");
}
#if defined(OS_ANDROID)
#define MAYBE_HistoryNavigationError_ExpectedUrlNotFound \
DISABLED_HistoryNavigationError_ExpectedUrlNotFound
#else
#define MAYBE_HistoryNavigationError_ExpectedUrlNotFound \
HistoryNavigationError_ExpectedUrlNotFound
#endif
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest,
MAYBE_HistoryNavigationError_ExpectedUrlNotFound) {
const std::string wbn_path = "/web_bundle/test.wbn";
const std::string primary_url_path = "/web_bundle/test.html";
const std::string alt_primary_url_path = "/web_bundle/alt.html";
RegisterRequestHandler(wbn_path);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL wbn_url = embedded_test_server()->GetURL(wbn_path);
const GURL primary_url = embedded_test_server()->GetURL(primary_url_path);
AddHeaders("Cache-Control:no-store\n");
SetContents(CreateSimpleWebBundle(primary_url));
NavigateToBundleAndWaitForReady(wbn_url, primary_url);
NavigateToURLAndWaitForTitle(
embedded_test_server()->GetURL("/web_bundle/empty_page.html"),
"Empty Page");
SetContents(CreateSimpleWebBundle(
embedded_test_server()->GetURL(alt_primary_url_path)));
HistoryBackAndWaitUntilConsoleError(
"The expected URL resource is not found in the web bundle.");
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, Iframe) {
const std::string wbn_path = "/test.wbn";
RegisterRequestHandler(wbn_path);
net::EmbeddedTestServer third_party_server;
GURL primary_url_origin;
GURL third_party_origin;
std::string web_bundle_content;
SetUpSubPageTest(embedded_test_server(), &third_party_server,
&primary_url_origin, &third_party_origin,
&web_bundle_content);
SetContents(web_bundle_content);
NavigateToBundleAndWaitForReady(primary_url_origin.Resolve(wbn_path),
primary_url_origin.Resolve("/top"));
RunSubPageTest(shell()->web_contents(), primary_url_origin,
third_party_origin, &AddIframeAndWaitForMessage,
false /* support_third_party_wbn_page */);
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, WindowOpen) {
const std::string wbn_path = "/test.wbn";
RegisterRequestHandler(wbn_path);
net::EmbeddedTestServer third_party_server;
GURL primary_url_origin;
GURL third_party_origin;
std::string web_bundle_content;
SetUpSubPageTest(embedded_test_server(), &third_party_server,
&primary_url_origin, &third_party_origin,
&web_bundle_content);
SetContents(web_bundle_content);
NavigateToBundleAndWaitForReady(primary_url_origin.Resolve(wbn_path),
primary_url_origin.Resolve("/top"));
RunSubPageTest(shell()->web_contents(), primary_url_origin,
third_party_origin, &WindowOpenAndWaitForMessage,
false /* support_third_party_wbn_page */);
}
IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, OutScopeSubPage) {
const std::string wbn_path = "/in_scope/test.wbn";
const std::string primary_url_path = "/in_scope/test.html";
RegisterRequestHandler(wbn_path);
RegisterRequestHandlerForSubPageTest(embedded_test_server(), "");
ASSERT_TRUE(embedded_test_server()->Start());
const GURL origin = embedded_test_server()->GetURL("/");
web_package::test::WebBundleBuilder builder(
origin.Resolve(primary_url_path).spec(), "");
AddHtmlFile(&builder, origin, primary_url_path, R"(
<script>
window.addEventListener('message',
event => domAutomationController.send(event.data),
false);
document.title = 'Ready';
</script>
)");
AddHtmlFile(&builder, origin, "/in_scope/subpage",
CreateSubPageHtml("in-scope-wbn-page"));
AddScriptFile(&builder, origin, "/in_scope/script",
CreateScriptForSubPageTest("in-scope-wbn-script"));
AddHtmlFile(&builder, origin, "/out_scope/subpage",
CreateSubPageHtml("out-scope-wbn-page"));
AddScriptFile(&builder, origin, "/out_scope/script",
CreateScriptForSubPageTest("out-scope-wbn-script"));
std::vector<uint8_t> bundle = builder.CreateBundle();
SetContents(std::string(bundle.begin(), bundle.end()));
NavigateToBundleAndWaitForReady(origin.Resolve(wbn_path),
origin.Resolve(primary_url_path));
const auto funcs = {&AddIframeAndWaitForMessage,
&WindowOpenAndWaitForMessage};
for (const auto func : funcs) {
EXPECT_EQ(
"in-scope-wbn-page in-scope-wbn-script",
(*func)(
shell()->web_contents(),
origin.Resolve("/in_scope/subpage").Resolve("#/in_scope/script")));
EXPECT_EQ(
"in-scope-wbn-page server-script",
(*func)(
shell()->web_contents(),
origin.Resolve("/in_scope/subpage").Resolve("#/out_scope/script")));
EXPECT_EQ(
"server-page server-script",
(*func)(
shell()->web_contents(),
origin.Resolve("/out_scope/subpage").Resolve("#/in_scope/script")));
EXPECT_EQ(
"server-page server-script",
(*func)(shell()->web_contents(), origin.Resolve("/out_scope/subpage")
.Resolve("#/out_scope/script")));
}
}
} // namespace content