diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-12 14:27:29 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:35:20 +0000 |
commit | c30a6232df03e1efbd9f3b226777b07e087a1122 (patch) | |
tree | e992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/content/browser/web_package | |
parent | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff) | |
download | qtwebengine-chromium-85-based.tar.gz |
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/content/browser/web_package')
10 files changed, 1130 insertions, 201 deletions
diff --git a/chromium/content/browser/web_package/prefetched_signed_exchange_cache.cc b/chromium/content/browser/web_package/prefetched_signed_exchange_cache.cc index 0d3ae7b6004..82aeed5bf3f 100644 --- a/chromium/content/browser/web_package/prefetched_signed_exchange_cache.cc +++ b/chromium/content/browser/web_package/prefetched_signed_exchange_cache.cc @@ -8,7 +8,6 @@ #include "base/feature_list.h" #include "base/metrics/histogram_macros.h" #include "base/strings/stringprintf.h" -#include "base/task/post_task.h" #include "components/link_header_util/link_header_util.h" #include "content/browser/loader/cross_origin_read_blocking_checker.h" #include "content/browser/loader/navigation_loader_interceptor.h" @@ -277,8 +276,8 @@ class InnerResponseURLLoader : public network::mojom::URLLoader { return; } - base::PostTask( - FROM_HERE, {BrowserThread::IO}, + GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce( &InnerResponseURLLoader::CreateMojoBlobReader, weak_factory_.GetWeakPtr(), std::move(pipe_producer_handle), @@ -315,8 +314,8 @@ class InnerResponseURLLoader : public network::mojom::URLLoader { static void BlobReaderCompleteOnIO( base::WeakPtr<InnerResponseURLLoader> loader, net::Error result) { - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&InnerResponseURLLoader::BlobReaderComplete, + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&InnerResponseURLLoader::BlobReaderComplete, std::move(loader), result)); } diff --git a/chromium/content/browser/web_package/prefetched_signed_exchange_cache_adapter.cc b/chromium/content/browser/web_package/prefetched_signed_exchange_cache_adapter.cc index 4dfc440e9d2..beb8f05dedc 100644 --- a/chromium/content/browser/web_package/prefetched_signed_exchange_cache_adapter.cc +++ b/chromium/content/browser/web_package/prefetched_signed_exchange_cache_adapter.cc @@ -4,9 +4,9 @@ #include "content/browser/web_package/prefetched_signed_exchange_cache_adapter.h" -#include "base/task/post_task.h" #include "content/browser/loader/prefetch_url_loader.h" #include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" #include "mojo/public/cpp/bindings/associated_remote.h" #include "storage/browser/blob/blob_builder_from_stream.h" #include "storage/browser/blob/blob_data_handle.h" @@ -21,8 +21,8 @@ void AbortAndDeleteBlobBuilder( return; } - base::PostTask(FROM_HERE, {BrowserThread::IO}, - base::BindOnce(&storage::BlobBuilderFromStream::Abort, + GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&storage::BlobBuilderFromStream::Abort, std::move(blob_builder))); } @@ -81,8 +81,8 @@ void PrefetchedSignedExchangeCacheAdapter::OnStartLoadingResponseBody( length_hint = cached_exchange_->inner_response()->content_length; } blob_is_streaming_ = true; - base::PostTaskAndReplyWithResult( - FROM_HERE, {BrowserThread::IO}, + GetIOThreadTaskRunner({})->PostTaskAndReplyWithResult( + FROM_HERE, base::BindOnce( &PrefetchedSignedExchangeCacheAdapter::CreateBlobBuilderFromStream, weak_factory_.GetWeakPtr(), std::move(body), length_hint, @@ -103,8 +103,8 @@ void PrefetchedSignedExchangeCacheAdapter::StreamingBlobDone( storage::BlobBuilderFromStream* builder, std::unique_ptr<storage::BlobDataHandle> result) { blob_is_streaming_ = false; - base::DeleteSoon(FROM_HERE, {BrowserThread::IO}, - std::move(blob_builder_from_stream_)); + GetIOThreadTaskRunner({})->DeleteSoon(FROM_HERE, + std::move(blob_builder_from_stream_)); cached_exchange_->SetBlobDataHandle(std::move(result)); MaybeCallOnSignedExchangeStored(); } @@ -177,8 +177,8 @@ void PrefetchedSignedExchangeCacheAdapter::StreamingBlobDoneOnIO( storage::BlobBuilderFromStream* builder, std::unique_ptr<storage::BlobDataHandle> result) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&PrefetchedSignedExchangeCacheAdapter::StreamingBlobDone, adapter, builder, std::move(result))); } diff --git a/chromium/content/browser/web_package/signed_exchange_certificate_chain.cc b/chromium/content/browser/web_package/signed_exchange_certificate_chain.cc index d9151fdff48..ceb3dbffb01 100644 --- a/chromium/content/browser/web_package/signed_exchange_certificate_chain.cc +++ b/chromium/content/browser/web_package/signed_exchange_certificate_chain.cc @@ -213,9 +213,8 @@ SignedExchangeCertificateChain::IgnoreErrorsSPKIList::IgnoreErrorsSPKIList( void SignedExchangeCertificateChain::IgnoreErrorsSPKIList::Parse( const std::string& spki_list) { - hash_set_ = - network::IgnoreErrorsCertVerifier::MakeWhitelist(base::SplitString( - spki_list, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)); + hash_set_ = network::CreateSPKIHashSet(base::SplitString( + spki_list, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)); } SignedExchangeCertificateChain::IgnoreErrorsSPKIList::~IgnoreErrorsSPKIList() = diff --git a/chromium/content/browser/web_package/signed_exchange_certificate_chain.h b/chromium/content/browser/web_package/signed_exchange_certificate_chain.h index 25e76b901f8..03e3159ea2e 100644 --- a/chromium/content/browser/web_package/signed_exchange_certificate_chain.h +++ b/chromium/content/browser/web_package/signed_exchange_certificate_chain.h @@ -8,12 +8,13 @@ #include <memory> #include "base/containers/span.h" +#include "base/gtest_prod_util.h" #include "base/memory/scoped_refptr.h" #include "base/optional.h" #include "base/strings/string_piece_forward.h" #include "content/browser/web_package/signed_exchange_consts.h" #include "content/common/content_export.h" -#include "services/network/ignore_errors_cert_verifier.h" +#include "services/network/public/cpp/spki_hash_set.h" namespace base { class CommandLine; @@ -60,7 +61,7 @@ class CONTENT_EXPORT SignedExchangeCertificateChain { bool ShouldIgnoreErrorsInternal( scoped_refptr<net::X509Certificate> certificate); - network::IgnoreErrorsCertVerifier::SPKIHashSet hash_set_; + network::SPKIHashSet hash_set_; DISALLOW_COPY_AND_ASSIGN(IgnoreErrorsSPKIList); }; diff --git a/chromium/content/browser/web_package/signed_exchange_request_handler_browsertest.cc b/chromium/content/browser/web_package/signed_exchange_request_handler_browsertest.cc index 455222105c5..f3c50a9027f 100644 --- a/chromium/content/browser/web_package/signed_exchange_request_handler_browsertest.cc +++ b/chromium/content/browser/web_package/signed_exchange_request_handler_browsertest.cc @@ -1025,7 +1025,7 @@ IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest, EXPECT_TRUE(ExecuteScriptAndExtractBool(shell()->web_contents(), register_sw_script, &result)); // serviceWorker.register() fails because the document URL of - // ServiceWorkerProviderHost is empty. + // ServiceWorkerHost is empty. EXPECT_FALSE(result); } diff --git a/chromium/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc b/chromium/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc index 892a8733a4b..5e0de4b2789 100644 --- a/chromium/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc +++ b/chromium/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc @@ -254,8 +254,8 @@ class SignedExchangePrefetchBrowserTest scoped_refptr<ChromeBlobStorageContext> blob_context = ChromeBlobStorageContext::GetFor( shell()->web_contents()->GetBrowserContext()); - base::PostTask( - FROM_HERE, {BrowserThread::IO}, + GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&SignedExchangePrefetchBrowserTest::SetBlobLimitsOnIO, blob_context)); } diff --git a/chromium/content/browser/web_package/web_bundle_blob_data_source.cc b/chromium/content/browser/web_package/web_bundle_blob_data_source.cc index ee575969d8e..05e98bdb729 100644 --- a/chromium/content/browser/web_package/web_bundle_blob_data_source.cc +++ b/chromium/content/browser/web_package/web_bundle_blob_data_source.cc @@ -9,7 +9,6 @@ #include "base/memory/ref_counted.h" #include "base/numerics/checked_math.h" #include "base/numerics/safe_conversions.h" -#include "base/task/post_task.h" #include "base/task/task_traits.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -110,8 +109,8 @@ WebBundleBlobDataSource::WebBundleBlobDataSource( network::mojom::URLLoaderClientEndpointsPtr endpoints, BrowserContext::BlobContextGetter blob_context_getter) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - base::PostTask( - FROM_HERE, {BrowserThread::IO}, + GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&WebBundleBlobDataSource::CreateCoreOnIO, weak_factory_.GetWeakPtr(), length_hint, std::move(outer_response_body), std::move(endpoints), @@ -121,7 +120,7 @@ WebBundleBlobDataSource::WebBundleBlobDataSource( WebBundleBlobDataSource::~WebBundleBlobDataSource() { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (core_) - base::DeleteSoon(FROM_HERE, {BrowserThread::IO}, std::move(core_)); + GetIOThreadTaskRunner({})->DeleteSoon(FROM_HERE, std::move(core_)); auto tasks = std::move(pending_get_core_tasks_); for (auto& task : tasks) { @@ -143,8 +142,8 @@ void WebBundleBlobDataSource::AddReceiverImpl( pending_receiver) { if (!core_) return; - base::PostTask(FROM_HERE, {BrowserThread::IO}, - base::BindOnce(&BlobDataSourceCore::AddReceiver, weak_core_, + GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&BlobDataSourceCore::AddReceiver, weak_core_, std::move(pending_receiver))); } @@ -160,8 +159,8 @@ void WebBundleBlobDataSource::CreateCoreOnIO( length_hint, std::move(endpoints), std::move(blob_context_getter)); core->Start(std::move(outer_response_body)); auto weak_core = core->GetWeakPtr(); - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&WebBundleBlobDataSource::SetCoreOnUI, std::move(weak_ptr), std::move(weak_core), std::move(core))); } @@ -175,7 +174,7 @@ void WebBundleBlobDataSource::SetCoreOnUI( if (!weak_ptr) { // This happens when the WebBundleBlobDataSource was deleted before // SetCoreOnUI() is called. - base::DeleteSoon(FROM_HERE, {BrowserThread::IO}, std::move(core)); + GetIOThreadTaskRunner({})->DeleteSoon(FROM_HERE, std::move(core)); return; } weak_ptr->SetCoreOnUIImpl(std::move(weak_core), std::move(core)); @@ -228,12 +227,12 @@ void WebBundleBlobDataSource::ReadToDataPipeImpl( CompletionCallback wrapped_callback = base::BindOnce( [](CompletionCallback callback, net::Error net_error) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(std::move(callback), net_error)); + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), net_error)); }, std::move(callback)); - base::PostTask(FROM_HERE, {BrowserThread::IO}, - base::BindOnce(&BlobDataSourceCore::ReadToDataPipe, weak_core_, + GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&BlobDataSourceCore::ReadToDataPipe, weak_core_, offset, length, std::move(producer_handle), std::move(wrapped_callback))); } diff --git a/chromium/content/browser/web_package/web_bundle_browsertest.cc b/chromium/content/browser/web_package/web_bundle_browsertest.cc index ce0f8b43f4b..e6417c50b94 100644 --- a/chromium/content/browser/web_package/web_bundle_browsertest.cc +++ b/chromium/content/browser/web_package/web_bundle_browsertest.cc @@ -16,11 +16,15 @@ #include "base/test/scoped_feature_list.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" +#include "content/browser/frame_host/frame_tree_node.h" +#include "content/browser/frame_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" @@ -28,6 +32,7 @@ #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" @@ -47,10 +52,6 @@ namespace { constexpr char kInvalidFileUrl[] = "file:///tmp/test%2F/a.wbn"; constexpr char kTestPageUrl[] = "https://test.example.org/"; -constexpr char kTestPage1Url[] = "https://test.example.org/page1.html"; -constexpr char kTestPage2Url[] = "https://test.example.org/page2.html"; -constexpr char kTestPageForHashUrl[] = - "https://test.example.org/hash.html#hello"; constexpr char kDefaultHeaders[] = "HTTP/1.1 200 OK\n" @@ -96,6 +97,24 @@ void CopyFileAndGetContentUri(const base::FilePath& file, } #endif // OS_ANDROID +std::string ExecuteAndGetString(const ToRenderFrameHost& adapter, + const std::string& script) { + std::string result; + EXPECT_TRUE(content::ExecuteScriptAndExtractString( + adapter, "domAutomationController.send(" + script + ")", &result)); + return result; +} + +void NavigateAndWaitForTitle(content::WebContents* web_contents, + const GURL& test_data_url, + const GURL& expected_commit_url, + base::StringPiece ascii_title) { + base::string16 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) { @@ -321,19 +340,10 @@ class WebBundleBrowserTestBase : public ContentBrowserTest { browser_client_.SetAcceptLangs(langs); } - void NavigateAndWaitForTitle(const GURL& test_data_url, - const GURL& expected_commit_url, - base::StringPiece ascii_title) { - base::string16 expected_title = base::ASCIIToUTF16(ascii_title); - TitleWatcher title_watcher(shell()->web_contents(), expected_title); - EXPECT_TRUE(NavigateToURL(shell()->web_contents(), test_data_url, - expected_commit_url)); - EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); - } - void NavigateToBundleAndWaitForReady(const GURL& test_data_url, const GURL& expected_commit_url) { - NavigateAndWaitForTitle(test_data_url, expected_commit_url, "Ready"); + NavigateAndWaitForTitle(shell()->web_contents(), test_data_url, + expected_commit_url, "Ready"); } void RunTestScript(const std::string& script) { @@ -388,16 +398,31 @@ class FinishNavigationObserver : public WebContentsObserver { : 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(); - std::move(done_closure_).Run(); + --navigations_remaining_; + + if (navigations_remaining_ == 0) + std::move(done_closure_).Run(); + } + + void set_navigations_remaining(int navigations_remaining) { + navigations_remaining_ = navigations_remaining; } const base::Optional<net::Error>& error_code() const { return error_code_; } + const std::vector<NavigationType>& navigation_types() const { + return navigation_types_; + } private: base::OnceClosure done_closure_; base::Optional<net::Error> error_code_; + int navigations_remaining_ = 1; + std::vector<NavigationType> navigation_types_; + DISALLOW_COPY_AND_ASSIGN(FinishNavigationObserver); }; @@ -427,6 +452,13 @@ std::string ExpectNavigationFailureAndReturnConsoleMessage( 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) { data_decoder::test::WebBundleBuilder builder(primary_url.spec(), ""); builder.AddExchange(primary_url.spec(), @@ -668,6 +700,863 @@ void RunSubPageTest(const ToRenderFrameHost& adapter, .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( + data_decoder::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 result; + 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); + })() + )"; + EXPECT_TRUE(content::ExecuteScriptAndExtractString(adapter, script, &result)); + return result; +} + +// 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); + data_decoder::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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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_PAGE}, + 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); + data_decoder::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_PAGE, 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_PAGE}, + 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_PAGE, 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_PAGE, 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_PAGE, 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 is_same_process = (iframe_node->parent()->GetProcess() == + iframe_node->current_frame_host()->GetProcess()); + + 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(is_same_process + ? "/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_PAGE, 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_PAGE}, + 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_PAGE, 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 { @@ -762,12 +1651,19 @@ class WebBundleTrustableFileBrowserTest const GURL& test_data_url() const { return test_data_url_; } const GURL& empty_page_url() const { return empty_page_url_; } - std::string ExecuteAndGetString(const std::string& script) { - std::string result; - EXPECT_TRUE(content::ExecuteScriptAndExtractString( - shell()->web_contents(), "domAutomationController.send(" + script + ")", - &result)); - return result; + 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: @@ -818,70 +1714,57 @@ IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, RangeRequest) { RunTestScript("test-range-request.js"); } -IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, Navigations) { - WriteCommonWebBundleFile(); - NavigateToBundleAndWaitForReady(test_data_url(), GURL(kTestPageUrl)); - // Move to page 1. - NavigateToURLAndWaitForTitle(GURL(kTestPage1Url), "Page 1"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - GURL(kTestPage1Url)); - // Move to page 2. - NavigateToURLAndWaitForTitle(GURL(kTestPage2Url), "Page 2"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - GURL(kTestPage2Url)); - // Back to page 1. - ExecuteScriptAndWaitForTitle("history.back();", "Page 1"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - GURL(kTestPage1Url)); - - // Back to the initial page. - ExecuteScriptAndWaitForTitle("history.back();", "Ready"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), GURL(kTestPageUrl)); +IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, BasicNavigation) { + RunSharedNavigationTest(&SetUpBasicNavigationTest, &RunBasicNavigationTest); +} - // Move to page 1. - ExecuteScriptAndWaitForTitle("history.forward();", "Page 1"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - GURL(kTestPage1Url)); +IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, + BrowserInitiatedOutOfBundleNavigation) { + RunSharedNavigationTest(&SetUpBrowserInitiatedOutOfBundleNavigationTest, + &RunBrowserInitiatedOutOfBundleNavigationTest); +} - // Reload. - ExecuteScriptAndWaitForTitle("document.title = 'reset';", "reset"); - ExecuteScriptAndWaitForTitle("location.reload();", "Page 1"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - GURL(kTestPage1Url)); +IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, + RendererInitiatedOutOfBundleNavigation) { + RunSharedNavigationTest(&SetUpRendererInitiatedOutOfBundleNavigationTest, + &RunRendererInitiatedOutOfBundleNavigationTest); +} - // Move to page 2. - ExecuteScriptAndWaitForTitle("history.forward();", "Page 2"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - GURL(kTestPage2Url)); +IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, + SameDocumentNavigation) { + RunSharedNavigationTest(&SetUpSameDocumentNavigationTest, + &RunSameDocumentNavigationTest); +} - // Move out of the Web Bundle. - NavigateAndWaitForTitle(empty_page_url(), empty_page_url(), "Empty Page"); +IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, IframeNavigation) { + RunSharedNavigationTest(&SetUpIframeNavigationTest, &RunIframeNavigationTest); +} - // Back to the page 2. - ExecuteScriptAndWaitForTitle("history.back();", "Page 2"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - GURL(kTestPage2Url)); +IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, + IframeOutOfBundleNavigation) { + RunSharedNavigationTest(&SetUpIframeNavigationTest, + &RunIframeOutOfBundleNavigationTest); } -IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, NavigationWithHash) { - WriteCommonWebBundleFile(); - NavigateToBundleAndWaitForReady(test_data_url(), GURL(kTestPageUrl)); - NavigateToURLAndWaitForTitle(GURL(kTestPageForHashUrl), "#hello"); +IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, + IframeParentInitiatedOutOfBundleNavigation) { + RunSharedNavigationTest(&SetUpIframeNavigationTest, + &RunIframeParentInitiatedOutOfBundleNavigationTest); +} - EXPECT_EQ(ExecuteAndGetString("window.location.href"), - "https://test.example.org/hash.html#hello"); - EXPECT_EQ(ExecuteAndGetString("document.location.href"), - "https://test.example.org/hash.html#hello"); - EXPECT_EQ(ExecuteAndGetString("document.URL"), - "https://test.example.org/hash.html#hello"); +IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, + IframeSameDocumentNavigation) { + RunSharedNavigationTest(&SetUpIframeNavigationTest, + &RunIframeSameDocumentNavigationTest); } IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, BaseURI) { WriteCommonWebBundleFile(); NavigateToBundleAndWaitForReady(test_data_url(), GURL(kTestPageUrl)); - EXPECT_EQ(ExecuteAndGetString("(new Request('./foo/bar')).url"), + EXPECT_EQ(ExecuteAndGetString(shell()->web_contents(), + "(new Request('./foo/bar')).url"), "https://test.example.org/foo/bar"); - EXPECT_EQ(ExecuteAndGetString(R"( + EXPECT_EQ(ExecuteAndGetString(shell()->web_contents(), R"( (() => { const base_element = document.createElement('base'); base_element.href = 'https://example.org/piyo/'; @@ -890,7 +1773,8 @@ IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest, BaseURI) { })() )"), "https://example.org/piyo/"); - EXPECT_EQ(ExecuteAndGetString("(new Request('./foo/bar')).url"), + EXPECT_EQ(ExecuteAndGetString(shell()->web_contents(), + "(new Request('./foo/bar')).url"), "https://example.org/piyo/foo/bar"); } @@ -988,6 +1872,26 @@ class WebBundleFileBrowserTest 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_; @@ -995,83 +1899,44 @@ class WebBundleFileBrowserTest }; IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, BasicNavigation) { - const GURL test_data_url = - GetTestUrlForFile(GetTestDataPath("web_bundle_browsertest.wbn")); - NavigateToBundleAndWaitForReady( - test_data_url, web_bundle_utils::GetSynthesizedUrlForWebBundle( - test_data_url, GURL(kTestPageUrl))); + RunSharedNavigationTest(&SetUpBasicNavigationTest, &RunBasicNavigationTest); } -IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, Navigations) { - const GURL test_data_url = - GetTestUrlForFile(GetTestDataPath("web_bundle_browsertest.wbn")); - NavigateToBundleAndWaitForReady( - test_data_url, web_bundle_utils::GetSynthesizedUrlForWebBundle( - test_data_url, GURL(kTestPageUrl))); - // Move to page 1. - NavigateToURLAndWaitForTitle(GURL(kTestPage1Url), "Page 1"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - web_bundle_utils::GetSynthesizedUrlForWebBundle( - test_data_url, GURL(kTestPage1Url))); - // Move to page 2. - NavigateToURLAndWaitForTitle(GURL(kTestPage2Url), "Page 2"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - web_bundle_utils::GetSynthesizedUrlForWebBundle( - test_data_url, GURL(kTestPage2Url))); - - // Back to page 1. - ExecuteScriptAndWaitForTitle("history.back();", "Page 1"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - web_bundle_utils::GetSynthesizedUrlForWebBundle( - test_data_url, GURL(kTestPage1Url))); - // Back to the initial page. - ExecuteScriptAndWaitForTitle("history.back();", "Ready"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - web_bundle_utils::GetSynthesizedUrlForWebBundle( - test_data_url, GURL(kTestPageUrl))); - - // Move to page 1. - ExecuteScriptAndWaitForTitle("history.forward();", "Page 1"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - web_bundle_utils::GetSynthesizedUrlForWebBundle( - test_data_url, GURL(kTestPage1Url))); +IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, + BrowserInitiatedOutOfBundleNavigation) { + RunSharedNavigationTest(&SetUpBrowserInitiatedOutOfBundleNavigationTest, + &RunBrowserInitiatedOutOfBundleNavigationTest); +} - // Reload. - ExecuteScriptAndWaitForTitle("document.title = 'reset';", "reset"); - ExecuteScriptAndWaitForTitle("location.reload();", "Page 1"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - web_bundle_utils::GetSynthesizedUrlForWebBundle( - test_data_url, GURL(kTestPage1Url))); +IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, + RendererInitiatedOutOfBundleNavigation) { + RunSharedNavigationTest(&SetUpRendererInitiatedOutOfBundleNavigationTest, + &RunRendererInitiatedOutOfBundleNavigationTest); +} - // Move to page 2. - ExecuteScriptAndWaitForTitle("history.forward();", "Page 2"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - web_bundle_utils::GetSynthesizedUrlForWebBundle( - test_data_url, GURL(kTestPage2Url))); +IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, SameDocumentNavigation) { + RunSharedNavigationTest(&SetUpSameDocumentNavigationTest, + &RunSameDocumentNavigationTest); +} - const GURL empty_page_url = - GetTestUrlForFile(GetTestDataPath("empty_page.html")); +IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, IframeNavigation) { + RunSharedNavigationTest(&SetUpIframeNavigationTest, &RunIframeNavigationTest); +} - // Move out of the Web Bundle. - NavigateAndWaitForTitle(empty_page_url, empty_page_url, "Empty Page"); +IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, IframeOutOfBundleNavigation) { + RunSharedNavigationTest(&SetUpIframeNavigationTest, + &RunIframeOutOfBundleNavigationTest); +} - // Back to the page 2 in the Web Bundle. - ExecuteScriptAndWaitForTitle("history.back();", "Page 2"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - web_bundle_utils::GetSynthesizedUrlForWebBundle( - test_data_url, GURL(kTestPage2Url))); +IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, + IframeParentInitiatedOutOfBundleNavigation) { + RunSharedNavigationTest(&SetUpIframeNavigationTest, + &RunIframeParentInitiatedOutOfBundleNavigationTest); } -IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, NavigationWithHash) { - const GURL test_data_url = - GetTestUrlForFile(GetTestDataPath("web_bundle_browsertest.wbn")); - NavigateToBundleAndWaitForReady( - test_data_url, web_bundle_utils::GetSynthesizedUrlForWebBundle( - test_data_url, GURL(kTestPageUrl))); - NavigateToURLAndWaitForTitle(GURL(kTestPageForHashUrl), "#hello"); - EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), - web_bundle_utils::GetSynthesizedUrlForWebBundle( - test_data_url, GURL(kTestPageForHashUrl))); +IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, IframeSameDocumentNavigation) { + RunSharedNavigationTest(&SetUpIframeNavigationTest, + &RunIframeSameDocumentNavigationTest); } IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, InvalidWebBundleFile) { @@ -1152,13 +2017,15 @@ IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, NoLocalFileScheme) { } 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(test_data_url, + NavigateAndWaitForTitle(shell()->web_contents(), test_data_url, web_bundle_utils::GetSynthesizedUrlForWebBundle( test_data_url, GURL(kTestPageUrl)), kTestPageUrl); @@ -1214,12 +2081,12 @@ IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, Variants) { SetAcceptLangs("ja,en"); const GURL test_data_url = GetTestUrlForFile(GetTestDataPath("variants_test.wbn")); - NavigateAndWaitForTitle(test_data_url, + NavigateAndWaitForTitle(shell()->web_contents(), test_data_url, web_bundle_utils::GetSynthesizedUrlForWebBundle( test_data_url, GURL(kTestPageUrl)), "lang=ja"); SetAcceptLangs("en,ja"); - NavigateAndWaitForTitle(test_data_url, + NavigateAndWaitForTitle(shell()->web_contents(), test_data_url, web_bundle_utils::GetSynthesizedUrlForWebBundle( test_data_url, GURL(kTestPageUrl)), "lang=en"); @@ -1421,6 +2288,24 @@ class WebBundleNetworkBrowserTest : public WebBundleBrowserTestBase { 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; @@ -1669,7 +2554,50 @@ IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, PathMismatch) { primary_url.spec().c_str(), wbn_url.spec().c_str())); } -IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, Navigations) { +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); +} + +IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, IframeNavigation) { + RunSharedNavigationTest(&SetUpIframeNavigationTest, &RunIframeNavigationTest); +} + +IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, + IframeOutOfBundleNavigation) { + RunSharedNavigationTest(&SetUpIframeNavigationTest, + &RunIframeOutOfBundleNavigationTest); +} + +IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, + IframeParentInitiatedOutOfBundleNavigation) { + RunSharedNavigationTest(&SetUpIframeNavigationTest, + &RunIframeParentInitiatedOutOfBundleNavigationTest); +} + +IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, + 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()); @@ -1694,7 +2622,8 @@ IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, Navigations) { "In scope page from server / in scope script from server"); } -IN_PROC_BROWSER_TEST_F(WebBundleNetworkBrowserTest, HistoryNavigations) { +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()); diff --git a/chromium/content/browser/web_package/web_bundle_interceptor_for_history_navigation_with_existing_reader.cc b/chromium/content/browser/web_package/web_bundle_interceptor_for_history_navigation_with_existing_reader.cc index cb9d37dd19f..03a8934792e 100644 --- a/chromium/content/browser/web_package/web_bundle_interceptor_for_history_navigation_with_existing_reader.cc +++ b/chromium/content/browser/web_package/web_bundle_interceptor_for_history_navigation_with_existing_reader.cc @@ -8,6 +8,7 @@ #include "content/browser/web_package/web_bundle_reader.h" #include "content/browser/web_package/web_bundle_source.h" #include "content/browser/web_package/web_bundle_utils.h" +#include "net/base/url_util.h" #include "services/network/public/cpp/resource_request.h" #include "url/gurl.h" @@ -35,12 +36,13 @@ void WebBundleInterceptorForHistoryNavigationWithExistingReader:: LoaderCallback callback, FallbackCallback fallback_callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK_EQ(resource_request.url, + DCHECK_EQ(net::SimplifyUrlForRequest(resource_request.url), url_loader_factory_->reader()->source().is_file() - ? web_bundle_utils::GetSynthesizedUrlForWebBundle( - url_loader_factory_->reader()->source().url(), - target_inner_url_) - : target_inner_url_); + ? net::SimplifyUrlForRequest( + web_bundle_utils::GetSynthesizedUrlForWebBundle( + url_loader_factory_->reader()->source().url(), + target_inner_url_)) + : net::SimplifyUrlForRequest(target_inner_url_)); std::move(callback).Run( base::MakeRefCounted<SingleRequestURLLoaderFactory>(base::BindOnce( &WebBundleInterceptorForHistoryNavigationWithExistingReader:: @@ -54,12 +56,13 @@ void WebBundleInterceptorForHistoryNavigationWithExistingReader:: mojo::PendingReceiver<network::mojom::URLLoader> receiver, mojo::PendingRemote<network::mojom::URLLoaderClient> client) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK_EQ(resource_request.url, + DCHECK_EQ(net::SimplifyUrlForRequest(resource_request.url), url_loader_factory_->reader()->source().is_file() - ? web_bundle_utils::GetSynthesizedUrlForWebBundle( - url_loader_factory_->reader()->source().url(), - target_inner_url_) - : target_inner_url_); + ? net::SimplifyUrlForRequest( + web_bundle_utils::GetSynthesizedUrlForWebBundle( + url_loader_factory_->reader()->source().url(), + target_inner_url_)) + : net::SimplifyUrlForRequest(target_inner_url_)); CreateLoaderAndStartAndDone(resource_request, std::move(receiver), std::move(client)); } diff --git a/chromium/content/browser/web_package/web_bundle_reader.cc b/chromium/content/browser/web_package/web_bundle_reader.cc index 869195a08d2..75051285636 100644 --- a/chromium/content/browser/web_package/web_bundle_reader.cc +++ b/chromium/content/browser/web_package/web_bundle_reader.cc @@ -8,7 +8,6 @@ #include "base/check_op.h" #include "base/numerics/safe_math.h" -#include "base/task/post_task.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "content/browser/web_package/web_bundle_blob_data_source.h" @@ -188,7 +187,7 @@ void WebBundleReader::ReadResponse( auto it = entries_.find(net::SimplifyUrlForRequest(resource_request.url)); if (it == entries_.end() || it->second->response_locations.empty()) { - PostTask( + base::ThreadPool::PostTask( FROM_HERE, base::BindOnce( std::move(callback), nullptr, @@ -206,7 +205,7 @@ void WebBundleReader::ReadResponse( accept_langs); auto found = matcher.FindBestMatchingIndex(entry->variants_value); if (!found || *found >= entry->response_locations.size()) { - PostTask( + base::ThreadPool::PostTask( FROM_HERE, base::BindOnce( std::move(callback), nullptr, @@ -257,8 +256,8 @@ void WebBundleReader::Reconnect() { pending_remote.InitWithNewPipeAndPassReceiver()); parser_->OpenDataSource(std::move(pending_remote)); - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&WebBundleReader::DidReconnect, this, + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&WebBundleReader::DidReconnect, this, base::nullopt /* error */)); } @@ -267,8 +266,8 @@ void WebBundleReader::ReconnectForFile(base::File file) { base::Optional<std::string> error; if (file_error != base::File::FILE_OK) error = base::File::ErrorToString(file_error); - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&WebBundleReader::DidReconnect, this, std::move(error))); } @@ -279,7 +278,7 @@ void WebBundleReader::DidReconnect(base::Optional<std::string> error) { if (error) { for (auto& pair : read_tasks) { - PostTask( + base::ThreadPool::PostTask( FROM_HERE, base::BindOnce(std::move(pair.second), nullptr, data_decoder::mojom::BundleResponseParseError::New( @@ -352,8 +351,8 @@ void WebBundleReader::ReadMetadataInternal(MetadataCallback callback, DCHECK(source_->is_trusted_file() || source_->is_file()); base::File::Error error = parser_->OpenFile(std::move(file)); if (base::File::FILE_OK != error) { - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce( std::move(callback), data_decoder::mojom::BundleMetadataParseError::New( |