blob: cfdae5c36321e8eb8454b262fab74ae49c2bae13 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_WEB_PACKAGE_WEB_BUNDLE_BROWSERTEST_BASE_H_
#define CONTENT_BROWSER_WEB_PACKAGE_WEB_BUNDLE_BROWSERTEST_BASE_H_
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/string_piece.h"
#include "build/build_config.h"
#include "components/web_package/mojom/web_bundle_parser.mojom-forward.h"
#include "components/web_package/test_support/mock_web_bundle_parser_factory.h"
#include "components/web_package/web_bundle_builder.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/navigation_type.h"
#include "content/browser/web_contents/web_contents_impl.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/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 "net/base/filename_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace content {
namespace web_bundle_browsertest_utils {
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);
#if BUILDFLAG(IS_ANDROID)
void CopyFileAndGetContentUri(const base::FilePath& file,
GURL* content_uri,
base::FilePath* new_file_path);
#endif // BUILDFLAG(IS_ANDROID)
std::string ExecuteAndGetString(const ToRenderFrameHost& adapter,
const std::string& script);
void NavigateAndWaitForTitle(content::WebContents* web_contents,
const GURL& test_data_url,
const GURL& expected_commit_url,
base::StringPiece ascii_title);
class DownloadObserver : public DownloadManager::Observer {
public:
explicit DownloadObserver(DownloadManager* manager);
DownloadObserver(const DownloadObserver&) = delete;
DownloadObserver& operator=(const DownloadObserver&) = delete;
~DownloadObserver() override;
void WaitUntilDownloadCreated();
const GURL& observed_url() const;
void OnDownloadCreated(content::DownloadManager* manager,
download::DownloadItem* item) override;
private:
raw_ptr<DownloadManager> manager_;
base::RunLoop run_loop_;
GURL url_;
};
class MockParserFactory {
public:
MockParserFactory(std::vector<GURL> urls,
const base::FilePath& response_body_file);
explicit MockParserFactory(
const std::vector<std::pair<GURL, const std::string&>> items);
MockParserFactory(const MockParserFactory&) = delete;
MockParserFactory& operator=(const MockParserFactory&) = delete;
int GetParserCreationCount() const;
void SimulateParserDisconnect();
void SimulateParseMetadataCrash();
void SimulateParseResponseCrash();
private:
void FinishSetUp(web_package::mojom::BundleMetadataPtr metadata);
void BindWebBundleParserFactory(
mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>
receiver);
data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
web_package::MockWebBundleParserFactory wrapped_factory_;
};
class TestBrowserClient : public ContentBrowserClient {
public:
TestBrowserClient() = default;
TestBrowserClient(const TestBrowserClient&) = delete;
TestBrowserClient& operator=(const TestBrowserClient&) = delete;
~TestBrowserClient() override = default;
bool CanAcceptUntrustedExchangesIfNeeded() override;
};
class WebBundleBrowserTestBase : public ContentBrowserTest {
public:
WebBundleBrowserTestBase(const WebBundleBrowserTestBase&) = delete;
WebBundleBrowserTestBase& operator=(const WebBundleBrowserTestBase&) = delete;
protected:
WebBundleBrowserTestBase() = default;
~WebBundleBrowserTestBase() override = default;
void SetUpOnMainThread() override;
void TearDownOnMainThread() override;
void NavigateToBundleAndWaitForReady(const GURL& test_data_url,
const GURL& expected_commit_url);
void RunTestScript(const std::string& script);
void ExecuteScriptAndWaitForTitle(const std::string& script,
const std::string& title);
void NavigateToURLAndWaitForTitle(const GURL& url, const std::string& title);
void CreateTemporaryWebBundleFile(const std::string& content,
base::FilePath* file_path);
private:
raw_ptr<ContentBrowserClient> original_client_ = nullptr;
TestBrowserClient browser_client_;
base::ScopedTempDir temp_dir_;
};
class FinishNavigationObserver : public WebContentsObserver {
public:
explicit FinishNavigationObserver(WebContents* contents,
base::OnceClosure done_closure);
~FinishNavigationObserver() override;
FinishNavigationObserver(const FinishNavigationObserver&) = delete;
FinishNavigationObserver& operator=(const FinishNavigationObserver&) = delete;
void DidFinishNavigation(NavigationHandle* navigation_handle) override;
void set_navigations_remaining(int navigations_remaining);
const absl::optional<net::Error>& error_code() const;
const std::vector<NavigationType>& navigation_types() const;
private:
base::OnceClosure done_closure_;
absl::optional<net::Error> error_code_;
int navigations_remaining_ = 1;
std::vector<NavigationType> navigation_types_;
};
std::string ExpectNavigationFailureAndReturnConsoleMessage(
content::WebContents* web_contents,
const GURL& url);
FrameTreeNode* GetFirstChild(WebContents* web_contents);
std::string CreateSimpleWebBundle(const GURL& primary_url);
void AddHtmlFile(web_package::WebBundleBuilder* builder,
const GURL& base_url,
const std::string& path,
const std::string& content);
void AddScriptFile(web_package::WebBundleBuilder* builder,
const GURL& base_url,
const std::string& path,
const std::string& content);
std::string CreatePathTestWebBundle(const GURL& base_url);
std::string CreateSubPageHtml(const std::string& page_info);
std::string CreateScriptForSubPageTest(const std::string& script_info);
void RegisterRequestHandlerForSubPageTest(net::EmbeddedTestServer* server,
const std::string& 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);
std::string AddIframeAndWaitForMessage(const ToRenderFrameHost& adapter,
const GURL& url);
std::string WindowOpenAndWaitForMessage(const ToRenderFrameHost& adapter,
const GURL& url);
// 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);
std::string CreateHtmlForNavigationTest(const std::string& page_info,
const std::string& additional_html);
std::string CreateScriptForNavigationTest(const std::string& script_info);
void AddHtmlAndScriptForNavigationTest(web_package::WebBundleBuilder* builder,
const GURL& base_url,
const std::string& path,
const std::string& additional_html);
std::string GetLoadResultForNavigationTest(const ToRenderFrameHost& adapter);
// 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);
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);
void SetUpSharedNavigationsTest(net::EmbeddedTestServer* server,
const std::vector<std::string>& pathes,
GURL* url_origin,
std::string* web_bundle_content);
void SetUpBasicNavigationTest(net::EmbeddedTestServer* server,
GURL* url_origin,
std::string* 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);
void SetUpBrowserInitiatedOutOfBundleNavigationTest(
net::EmbeddedTestServer* server,
GURL* url_origin,
std::string* 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);
void SetUpRendererInitiatedOutOfBundleNavigationTest(
net::EmbeddedTestServer* server,
GURL* url_origin,
std::string* 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);
void SetUpSameDocumentNavigationTest(net::EmbeddedTestServer* server,
GURL* url_origin,
std::string* 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);
void SetUpIframeNavigationTest(net::EmbeddedTestServer* server,
GURL* url_origin,
std::string* web_bundle_content);
// 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);
// 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);
// 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);
// 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);
enum class TestFilePathMode {
kNormalFilePath,
#if BUILDFLAG(IS_ANDROID)
kContentURI,
#endif // BUILDFLAG(IS_ANDROID)
};
// Adding web_bundle_browsertest_utils:: extra to the prefix so the files using
// these directives outside of the namespace don't fail.
#if BUILDFLAG(IS_ANDROID)
#define TEST_FILE_PATH_MODE_PARAMS \
testing::Values( \
web_bundle_browsertest_utils::TestFilePathMode::kNormalFilePath, \
web_bundle_browsertest_utils::TestFilePathMode::kContentURI)
#else
#define TEST_FILE_PATH_MODE_PARAMS \
testing::Values( \
web_bundle_browsertest_utils::TestFilePathMode::kNormalFilePath)
#endif // BUILDFLAG(IS_ANDROID)
} // namespace web_bundle_browsertest_utils
} // namespace content
#endif // CONTENT_BROWSER_WEB_PACKAGE_WEB_BUNDLE_BROWSERTEST_BASE_H_