| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/controlled_frame/controlled_frame_test_base.h" |
| |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| |
| #include "base/strings/string_number_conversions.h" |
| #include "base/types/expected.h" |
| #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h" |
| #include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h" |
| #include "chrome/common/chrome_features.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "extensions/browser/guest_view/web_view/web_view_guest.h" |
| |
| namespace controlled_frame { |
| |
| ControlledFrameTestBase::ControlledFrameTestBase() |
| : channel_(version_info::Channel::DEFAULT), |
| feature_setting_(FeatureSetting::ENABLED), |
| flag_setting_(FlagSetting::CONTROLLED_FRAME) { |
| ConfigureEnvironment(); |
| } |
| |
| ControlledFrameTestBase::ControlledFrameTestBase( |
| const version_info::Channel& channel, |
| const FeatureSetting& feature_setting, |
| const FlagSetting& flag_setting) |
| : channel_(channel), |
| feature_setting_(feature_setting), |
| flag_setting_(flag_setting) { |
| ConfigureEnvironment(); |
| } |
| |
| ControlledFrameTestBase::~ControlledFrameTestBase() = default; |
| |
| void ControlledFrameTestBase::SetUpCommandLine( |
| base::CommandLine* command_line) { |
| if (flag_setting() == FlagSetting::EXPERIMENTAL) { |
| // Enable experimental web platform features as a proxy for enabling |
| // Controlled Frame. |
| command_line->AppendSwitch( |
| switches::kEnableExperimentalWebPlatformFeatures); |
| } else if (flag_setting() == FlagSetting::CONTROLLED_FRAME) { |
| // Enable just the Controlled Frame API. |
| command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures, |
| "ControlledFrame"); |
| } |
| } |
| |
| std::string ControlledFrameTestBase::ConfigToString() { |
| return "channel=" + base::NumberToString(int(channel())) + |
| "; feature=" + base::NumberToString(int(feature_setting())) + |
| "; flag=" + base::NumberToString(int(flag_setting())); |
| } |
| |
| void ControlledFrameTestBase::ConfigureEnvironment() { |
| // Initialize |scoped_feature_list_|. Start by initializing |feature_list|. |
| auto feature_list = std::make_unique<base::FeatureList>(); |
| // IsolatedWebAppBrowserTestHarness enables features::kIsolatedWebApps and |
| // features::kIsolatedWebAppDevMode. |
| std::vector<base::test::FeatureRef> enabled_features = { |
| blink::features::kIsolateSandboxedIframes}; |
| std::vector<base::test::FeatureRef> disabled_features = {}; |
| switch (feature_setting()) { |
| case FeatureSetting::UNINITIALIZED: |
| FAIL() << "FeatureSetting should be initialized."; |
| case FeatureSetting::NONE: |
| break; |
| case FeatureSetting::DISABLED: |
| disabled_features.push_back(blink::features::kControlledFrame); |
| break; |
| case FeatureSetting::ENABLED: |
| enabled_features.push_back(blink::features::kControlledFrame); |
| break; |
| } |
| scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features); |
| } |
| |
| void ControlledFrameTestBase::StartContentServer( |
| std::string_view chrome_test_data_relative_dir) { |
| embedded_https_test_server().ServeFilesFromSourceDirectory( |
| GetChromeTestDataDir().AppendASCII(chrome_test_data_relative_dir)); |
| ASSERT_TRUE(embedded_https_test_server().Start()); |
| } |
| |
| web_app::IsolatedWebAppUrlInfo |
| ControlledFrameTestBase::CreateAndInstallEmptyApp( |
| const web_app::ManifestBuilder& manifest_builder) { |
| auto updated_manifest_builder = manifest_builder; |
| updated_manifest_builder.AddPermissionsPolicy( |
| network::mojom::PermissionsPolicyFeature::kControlledFrame, /*self=*/true, |
| /*origins=*/{}); |
| app_ = web_app::IsolatedWebAppBuilder(updated_manifest_builder).BuildBundle(); |
| app_->TrustSigningKey(); |
| base::expected<web_app::IsolatedWebAppUrlInfo, std::string> url_info = |
| app_->Install(profile()); |
| CHECK(url_info.has_value()) << url_info.error(); |
| return url_info.value(); |
| } |
| |
| // Keep this in sync with web_kiosk_base_test.cc. |
| [[nodiscard]] bool ControlledFrameTestBase::CreateControlledFrame( |
| content::RenderFrameHost* frame, |
| const GURL& src) { |
| static std::string kCreateControlledFrame = R"( |
| new Promise((resolve, reject) => { |
| const controlledframe = document.createElement('controlledframe'); |
| if (!('src' in controlledframe)) { |
| // Tag is undefined or generates a malformed response. |
| reject('FAIL'); |
| return; |
| } |
| controlledframe.setAttribute('src', $1); |
| controlledframe.addEventListener('loadstop', resolve); |
| controlledframe.addEventListener('loadabort', reject); |
| document.body.appendChild(controlledframe); |
| }); |
| )"; |
| return ExecJs(frame, content::JsReplace(kCreateControlledFrame, src)); |
| } |
| |
| std::pair<content::RenderFrameHost*, content::RenderFrameHost*> |
| ControlledFrameTestBase::InstallAndOpenIwaThenCreateControlledFrame( |
| std::optional<std::string_view> controlled_frame_host_name, |
| std::string_view controlled_frame_src_relative_url, |
| web_app::ManifestBuilder manfest_builder) { |
| CHECK(embedded_https_test_server().Started()) |
| << "Controlled Frame content server has not been started."; |
| |
| web_app::IsolatedWebAppUrlInfo url_info = |
| CreateAndInstallEmptyApp(manfest_builder); |
| content::RenderFrameHost* app_frame = OpenApp(url_info.app_id()); |
| CHECK(app_frame); |
| |
| GURL controlled_frame_src = controlled_frame_host_name.has_value() |
| ? embedded_https_test_server().GetURL( |
| controlled_frame_host_name.value(), |
| controlled_frame_src_relative_url) |
| : embedded_https_test_server().GetURL( |
| controlled_frame_src_relative_url); |
| |
| CHECK(CreateControlledFrame(app_frame, controlled_frame_src)); |
| |
| extensions::WebViewGuest* web_view_guest = GetWebViewGuest(app_frame); |
| CHECK(web_view_guest); |
| |
| content::RenderFrameHost* controlled_frame = |
| web_view_guest->GetGuestMainFrame(); |
| CHECK(controlled_frame); |
| |
| return {app_frame, controlled_frame}; |
| } |
| |
| extensions::WebViewGuest* ControlledFrameTestBase::GetWebViewGuest( |
| content::RenderFrameHost* embedder_frame) { |
| extensions::WebViewGuest* web_view_guest = nullptr; |
| embedder_frame->ForEachRenderFrameHostWithAction( |
| [&web_view_guest](content::RenderFrameHost* rfh) { |
| if (auto* web_view = |
| extensions::WebViewGuest::FromRenderFrameHost(rfh)) { |
| web_view_guest = web_view; |
| return content::RenderFrameHost::FrameIterationAction::kStop; |
| } |
| return content::RenderFrameHost::FrameIterationAction::kContinue; |
| }); |
| return web_view_guest; |
| } |
| |
| } // namespace controlled_frame |