CVE-2026-78897
Overview
Background
- `DisableScreenshots` policy (`prefs::kDisableScreenshots`)
- an enterprise policy, stored as a profile pref, that should block programmatic screenshots, including extension capture APIs.
- `<webview>` / `<controlledframe>`
- embedder-only guest-view elements (for Chrome Apps and Isolated Web Apps) whose
captureVisibleRegion()method runsWebViewInternalCaptureVisibleRegionFunctionin the browser process. - `WebContentsCaptureClient`
- a shared base for capture functions; it asks the subclass’s
GetScreenshotAccess()whether capture is allowed and maps a denial toFAILURE_REASON_SCREEN_SHOTS_DISABLEDorFAILURE_REASON_SCREEN_SHOTS_DISABLED_BY_DLP. - `ExtensionsBrowserClient::IsScreenshotRestricted`
- an embedder hook from the
extensions/layer tochrome/. It used to report only ChromeOS Data Leak Prevention (DLP) restrictions.
Root Cause Analysis
Each WebContentsCaptureClient subclass did its own screenshot authorization in GetScreenshotAccess(). TabsCaptureVisibleTabFunction (in chrome/) checked both prefs::kDisableScreenshots and IsScreenshotRestricted(). WebViewInternalCaptureVisibleRegionFunction lives in extensions/ and cannot read Chrome prefs directly, so it called only ExtensionsBrowserClient::Get()->IsScreenshotRestricted(); that method returned false on non-ChromeOS builds and checked only DLP on ChromeOS.
As a result, <webview>/<controlledframe> captureVisibleRegion() succeeded even when an administrator had set DisableScreenshots, a missing authorization check on a privileged capture path.
The fix moves the policy check into ChromeExtensionsBrowserClient::IsScreenshotRestricted(), which now returns base::expected<void, ScreenshotAccessError>, returns kDisabledByPreferences when kDisableScreenshots is set, and then checks DLP (ChromeOS only). Both GetScreenshotAccess() implementations now just forward to this one method, so every capture client gets the same checks.
tabs.captureVisibleTab) instead of living in the shared hook, so the extensions/-layer webview capture path skipped it. The fix centralises the kDisableScreenshots and DLP checks in ChromeExtensionsBrowserClient::IsScreenshotRestricted() and has all capture clients forward to it.Attack Path
- Managed profile with screenshots disabled
An administrator sets
DisableScreenshots, soprefs::kDisableScreenshotsistruefor the profile. - Privileged embedder hosts a guest
A Chrome App with
<webview>, or an Isolated Web App with thecontrolled-framepermissions-policy feature (as inCaptureVisibleRegionDisabledByPolicy), embeds web content in a guest. - Call `captureVisibleRegion()`
The embedder calls
captureVisibleRegion(), which reachesWebViewInternalCaptureVisibleRegionFunction::GetScreenshotAccess()in the browser process. - Policy bypassed
Before the fix the method returned
kEnabledunless DLP applied, so the capture succeeded and the embedder got an image of the guest’s rendered content despite the policy.
Impact Assessment
<webview> or <controlledframe> capability could capture screenshots of guest content on a device where the administrator has disabled screenshots, defeating an enterprise data-protection control. It needs an installed Chrome App or Isolated Web App with these embedder-only APIs (ordinary web pages cannot reach them) and a managed profile with DisableScreenshots set. The vendor advisory rates this low severity and does not report in-the-wild exploitation; NVD scores it CVSS 6.5. Using this to exfiltrate sensitive content shown in the guest is plausible but not shown by the patch (inferred).Changed Functions
| Function | Change | Notes |
|---|---|---|
ifextensions/browser/api/web_contents_capture_client.cc |
modified | |
switchextensions/browser/api/web_contents_capture_client.cc |
modified | |
SkBitmapextensions/browser/api/web_contents_capture_client.h |
modified | |
ifchrome/browser/controlled_frame/controlled_frame_apitest.cc |
modified | |
IN_PROC_BROWSER_TEST_Fchrome/browser/controlled_frame/controlled_frame_apitest.cc |
modified |
Files Changed
extensions/browser/api/guest_view/web_view/web_view_internal_api.ccchrome/browser/extensions/chrome_extensions_browser_client.hextensions/browser/extensions_browser_client.ccextensions/browser/screenshot_access.hchrome/browser/extensions/chrome_extensions_browser_client.ccextensions/browser/extensions_browser_client.hchrome/browser/extensions/api/tabs/tabs_api.ccextensions/browser/api/web_contents_capture_client.hextensions/browser/api/web_contents_capture_client.ccextensions/browser/BUILD.gnchrome/browser/extensions/api/tabs/tabs_api.hextensions/browser/api/guest_view/web_view/web_view_internal_api.hchrome/browser/controlled_frame/controlled_frame_apitest.cc
Audit Directions
- Policy checks done per caller instead of in the shared hookLook for other enterprise prefs (for example printing, downloads, clipboard or developer tools) that are enforced in
chrome/API functions but have parallelextensions/or guest-view implementations that skip them. - Other capture surfacesCheck that
DisableScreenshots/DLP also apply totabCapture,desktopCapture,pageCapture, DevToolsPage.captureScreenshot,<controlledframe>-specific APIs and Isolated Web App capture paths. - Layering gaps between `extensions/` and `chrome/`Review other
ExtensionsBrowserClienthooks whose default or Chrome implementation returns a permissive value on some platforms (#if !BUILDFLAG(...)returningfalse), which can quietly disable a check.
Patch
From b4f89038dec62d6ed9f362391a048aa126c419f8 Mon Sep 17 00:00:00 2001
From: Giovanni Pezzino <giovax@google.com>
Date: Tue, 07 Jul 2026 01:20:54 -0700
Subject: [PATCH] Enforce DisableScreenshots enterprise policy in webview and controlledframe
The captureVisibleRegion API used by <webview> and <controlledframe>
components failed to check the DisableScreenshots enterprise policy
preference. Because WebViewInternalCaptureVisibleRegionFunction resides
in the extensions/ layer, it only checked IsScreenshotRestricted (DLP
restrictions) and omitted global preference checks.
This CL unifies screenshot restriction checks by defining
ScreenshotAccessError in extensions/browser/screenshot_access.h and
updating ExtensionsBrowserClient::IsScreenshotRestricted() and
WebContentsCaptureClient::GetScreenshotAccess() to return
base::expected<void, ScreenshotAccessError>. In
ChromeExtensionsBrowserClient, this method checks both
prefs::kDisableScreenshots and DLP restrictions. Both
WebViewInternalCaptureVisibleRegionFunction::GetScreenshotAccess() and
TabsCaptureVisibleTabFunction::GetScreenshotAccess() now directly
forward to this unified method.
BUG=b:513486883
TAG=agy
TEST=ControlledFrameApiTest.CaptureVisibleRegionDisabledByPolicy
Change-Id: I9ef600a16bf56b78980cb54b3c246eb96a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8036583
Reviewed-by: Andrew Rayskiy <greengrape@google.com>
Reviewed-by: Devlin Cronin <rdevlin.cronin@chromium.org>
Commit-Queue: Giovanni Pezzino <giovax@google.com>
Cr-Commit-Position: refs/heads/main@{#1657764}
---
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index d09c83330..a8808f5 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -3593,20 +3593,10 @@
TabsCaptureVisibleTabFunction::TabsCaptureVisibleTabFunction()
: chrome_details_(this) {}
-WebContentsCaptureClient::ScreenshotAccess
+base::expected<void, extensions::ScreenshotAccessError>
TabsCaptureVisibleTabFunction::GetScreenshotAccess(
content::WebContents* web_contents) const {
- PrefService* service =
- Profile::FromBrowserContext(browser_context())->GetPrefs();
- if (service->GetBoolean(prefs::kDisableScreenshots)) {
- return ScreenshotAccess::kDisabledByPreferences;
- }
-
- if (ExtensionsBrowserClient::Get()->IsScreenshotRestricted(web_contents)) {
- return ScreenshotAccess::kDisabledByDlp;
- }
-
- return ScreenshotAccess::kEnabled;
+ return ExtensionsBrowserClient::Get()->IsScreenshotRestricted(web_contents);
}
bool TabsCaptureVisibleTabFunction::ClientAllowsTransparency() {
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.h b/chrome/browser/extensions/api/tabs/tabs_api.h
index 065f696..a5c36f80 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.h
+++ b/chrome/browser/extensions/api/tabs/tabs_api.h
@@ -489,7 +489,7 @@
content::WebContents* GetWebContentsForID(int window_id, std::string* error);
// extensions::WebContentsCaptureClient:
- ScreenshotAccess GetScreenshotAccess(
+ base::expected<void, extensions::ScreenshotAccessError> GetScreenshotAccess(
content::WebContents* web_contents) const override;
bool ClientAllowsTransparency() override;
void OnCaptureSuccess(const SkBitmap& bitmap) override;
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.cc b/chrome/browser/extensions/chrome_extensions_browser_client.cc
index 29c7642..459bf07 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.cc
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.cc
@@ -93,6 +93,7 @@
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/embedder_support/user_agent_utils.h"
+#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/privacy_sandbox_prefs.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "components/search_engines/template_url_service.h"
@@ -122,6 +123,7 @@
#include "extensions/browser/process_manager_delegate.h"
#include "extensions/browser/safe_browsing_delegate.h"
#include "extensions/browser/scoped_extension_keep_alive.h"
+#include "extensions/browser/screenshot_access.h"
#include "extensions/browser/unpacked_installer.h"
#include "extensions/browser/url_request_util.h"
#include "extensions/common/extension_id.h"
@@ -814,14 +816,22 @@
return util::HasIsolatedStorage(extension_id, context);
}
-bool ChromeExtensionsBrowserClient::IsScreenshotRestricted(
+base::expected<void, extensions::ScreenshotAccessError>
+ChromeExtensionsBrowserClient::IsScreenshotRestricted(
content::WebContents* web_contents) const {
-#if !BUILDFLAG(IS_CHROMEOS)
- return false;
-#else
- return policy::DlpContentManager::Get()->IsScreenshotApiRestricted(
- web_contents);
+ Profile* profile =
+ Profile::FromBrowserContext(web_contents->GetBrowserContext());
+ if (profile->GetPrefs()->GetBoolean(prefs::kDisableScreenshots)) {
+ return base::unexpected(
+ extensions::ScreenshotAccessError::kDisabledByPreferences);
+ }
+#if BUILDFLAG(IS_CHROMEOS)
+ if (policy::DlpContentManager::Get()->IsScreenshotApiRestricted(
+ web_contents)) {
+ return base::unexpected(extensions::ScreenshotAccessError::kDisabledByDlp);
+ }
#endif
+ return base::ok();
}
bool ChromeExtensionsBrowserClient::IsValidTabId(
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.h b/chrome/browser/extensions/chrome_extensions_browser_client.h
index 8e3cbeb..5fa368c 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.h
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.h
@@ -12,6 +12,7 @@
#include "base/functional/callback_forward.h"
#include "base/lazy_instance.h"
#include "base/memory/scoped_refptr.h"
+#include "base/types/expected.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
@@ -222,8 +223,8 @@
const base::FilePath& path) override;
bool HasIsolatedStorage(const ExtensionId& extension_id,
content::BrowserContext* context) override;
- bool IsScreenshotRestricted(
- content::WebContents* web_contents) const override;
+ base::expected<void, extensions::ScreenshotAccessError>
+ IsScreenshotRestricted(content::WebContents* web_contents) const override;
bool IsValidTabId(content::BrowserContext* context,
int tab_id,
bool include_incognito,
diff --git a/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc b/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
index 8b04468..14ff5b3 100644
--- a/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
+++ b/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
@@ -376,13 +376,10 @@
return user_gesture();
}
-WebContentsCaptureClient::ScreenshotAccess
+base::expected<void, ScreenshotAccessError>
WebViewInternalCaptureVisibleRegionFunction::GetScreenshotAccess(
content::WebContents* web_contents) const {
- if (ExtensionsBrowserClient::Get()->IsScreenshotRestricted(web_contents))
- return ScreenshotAccess::kDisabledByDlp;
-
- return ScreenshotAccess::kEnabled;
+ return ExtensionsBrowserClient::Get()->IsScreenshotRestricted(web_contents);
}
bool WebViewInternalCaptureVisibleRegionFunction::ClientAllowsTransparency() {
diff --git a/extensions/browser/api/guest_view/web_view/web_view_internal_api.h b/extensions/browser/api/guest_view/web_view/web_view_internal_api.h
index a41a5e1..03c01ae0 100644
--- a/extensions/browser/api/guest_view/web_view/web_view_internal_api.h
+++ b/extensions/browser/api/guest_view/web_view/web_view_internal_api.h
@@ -69,7 +69,7 @@
private:
// extensions::WebContentsCaptureClient:
- ScreenshotAccess GetScreenshotAccess(
+ base::expected<void, ScreenshotAccessError> GetScreenshotAccess(
content::WebContents* web_contents) const override;
bool ClientAllowsTransparency() override;
void OnCaptureSuccess(const SkBitmap& bitmap) override;
diff --git a/extensions/browser/api/web_contents_capture_client.cc b/extensions/browser/api/web_contents_capture_client.cc
index 5f2dc0d..09847442 100644
--- a/extensions/browser/api/web_contents_capture_client.cc
+++ b/extensions/browser/api/web_contents_capture_client.cc
@@ -9,6 +9,7 @@
#include "base/base64.h"
#include "base/strings/strcat.h"
#include "base/syslog_logging.h"
+#include "base/types/expected_macros.h"
#include "build/chromeos_buildflags.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "content/public/browser/render_widget_host.h"
@@ -39,13 +40,15 @@
}
// Check for screenshot capture restrictions.
- ScreenshotAccess screenshot_access = GetScreenshotAccess(web_contents);
- if (screenshot_access == ScreenshotAccess::kDisabledByPreferences) {
- return FAILURE_REASON_SCREEN_SHOTS_DISABLED;
- }
- if (screenshot_access == ScreenshotAccess::kDisabledByDlp) {
- return FAILURE_REASON_SCREEN_SHOTS_DISABLED_BY_DLP;
- }
+ RETURN_IF_ERROR(GetScreenshotAccess(web_contents),
+ [](ScreenshotAccessError error) {
+ switch (error) {
+ case ScreenshotAccessError::kDisabledByPreferences:
+ return FAILURE_REASON_SCREEN_SHOTS_DISABLED;
+ case ScreenshotAccessError::kDisabledByDlp:
+ return FAILURE_REASON_SCREEN_SHOTS_DISABLED_BY_DLP;
+ }
+ });
// The default format and quality setting used when encoding jpegs.
const api::extension_types::ImageFormat kDefaultFormat =
diff --git a/extensions/browser/api/web_contents_capture_client.h b/extensions/browser/api/web_contents_capture_client.h
index a051442f..f69e133 100644
--- a/extensions/browser/api/web_contents_capture_client.h
+++ b/extensions/browser/api/web_contents_capture_client.h
@@ -8,7 +8,9 @@
#include <optional>
#include <string>
+#include "base/types/expected.h"
#include "extensions/browser/extension_function.h"
+#include "extensions/browser/screenshot_access.h"
#include "extensions/common/api/extension_types.h"
class SkBitmap;
@@ -31,12 +33,7 @@
protected:
virtual ~WebContentsCaptureClient() {}
- enum class ScreenshotAccess {
- kEnabled,
- kDisabledByPreferences,
- kDisabledByDlp,
- };
- virtual ScreenshotAccess GetScreenshotAccess(
+ virtual base::expected<void, ScreenshotAccessError> GetScreenshotAccess(
content::WebContents* web_contents) const = 0;
virtual bool ClientAllowsTransparency() = 0;
diff --git a/extensions/browser/extensions_browser_client.cc b/extensions/browser/extensions_browser_client.cc
index 9077a1e7..0ff0338 100644
--- a/extensions/browser/extensions_browser_client.cc
+++ b/extensions/browser/extensions_browser_client.cc
@@ -161,9 +161,10 @@
return false;
}
-bool ExtensionsBrowserClient::IsScreenshotRestricted(
+base::expected<void, ScreenshotAccessError>
+ExtensionsBrowserClient::IsScreenshotRestricted(
content::WebContents* web_contents) const {
- return false;
+ return base::ok();
}
bool ExtensionsBrowserClient::IsValidTabId(
diff --git a/extensions/browser/extensions_browser_client.h b/extensions/browser/extensions_browser_client.h
index 082e1410..4f3c988e 100644
--- a/extensions/browser/extensions_browser_client.h
+++ b/extensions/browser/extensions_browser_client.h
@@ -13,6 +13,7 @@
#include "base/functional/callback_forward.h"
#include "base/memory/ref_counted.h"
#include "base/memory/ref_counted_memory.h"
+#include "base/types/expected.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
@@ -23,6 +24,7 @@
#include "extensions/browser/extension_event_histogram_value.h"
#include "extensions/browser/extension_prefs_observer.h"
#include "extensions/browser/extensions_browser_api_provider.h"
+#include "extensions/browser/screenshot_access.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/mojom/view_type.mojom.h"
#include "extensions/common/url_pattern_set.h"
@@ -511,9 +513,10 @@
virtual bool HasIsolatedStorage(const ExtensionId& extension_id,
content::BrowserContext* context);
- // Returns whether screenshot of `web_contents` is restricted due to Data Leak
- // Protection policy.
- virtual bool IsScreenshotRestricted(content::WebContents* web_contents) const;
+ // Returns whether screenshot of `web_contents` is restricted due to
+ // preference or Data Leak Protection policy.
+ virtual base::expected<void, ScreenshotAccessError> IsScreenshotRestricted(
+ content::WebContents* web_contents) const;
// Returns true if `tab_id` exists on `browser_context`.
virtual bool IsValidTabId(content::BrowserContext* browser_context,
diff --git a/extensions/browser/screenshot_access.h b/extensions/browser/screenshot_access.h
new file mode 100644
index 0000000..dabf42e
--- /dev/null
+++ b/extensions/browser/screenshot_access.h
@@ -0,0 +1,17 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_SCREENSHOT_ACCESS_H_
+#define EXTENSIONS_BROWSER_SCREENSHOT_ACCESS_H_
+
+namespace extensions {
+
+enum class ScreenshotAccessError {
+ kDisabledByPreferences,
+ kDisabledByDlp,
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_SCREENSHOT_ACCESS_H_
diff --git a/chrome/browser/controlled_frame/controlled_frame_apitest.cc b/chrome/browser/controlled_frame/controlled_frame_apitest.cc
index 357088d7..3f70244 100644
--- a/chrome/browser/controlled_frame/controlled_frame_apitest.cc
+++ b/chrome/browser/controlled_frame/controlled_frame_apitest.cc
@@ -25,9 +25,11 @@
#include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
+#include "chrome/common/pref_names.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/embedder_support/user_agent_utils.h"
+#include "components/prefs/pref_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/browser/service_worker_running_info.h"
@@ -479,6 +481,44 @@
EXPECT_EQ(kEvalSuccessStr, VerifyBackgroundColorIsRed(web_view_guest));
}
+IN_PROC_BROWSER_TEST_F(ControlledFrameApiTest,
+ CaptureVisibleRegionDisabledByPolicy) {
+ std::unique_ptr<web_app::ScopedBundledIsolatedWebApp> app =
+ web_app::IsolatedWebAppBuilder(
+ web_app::ManifestBuilder().AddPermissionsPolicy(
+ network::mojom::PermissionsPolicyFeature::kControlledFrame,
+ /*self=*/true,
+ /*origins=*/{}))
+ .BuildBundle();
+ app->TrustSigningKey();
+ ASSERT_OK_AND_ASSIGN(web_app::IsolatedWebAppUrlInfo url_info,
+ app->Install(profile()));
+ content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
+
+ ASSERT_TRUE(CreateControlledFrame(
+ app_frame, embedded_https_test_server().GetURL("/index.html")));
+
+ profile()->GetPrefs()->SetBoolean(prefs::kDisableScreenshots, true);
+
+ const std::string script = R"(
+ (async function() {
+ const frame = document.getElementsByTagName('controlledframe')[0];
+ if (!frame || !frame.request) {
+ return 'NO_FRAME';
+ }
+ try {
+ await frame.captureVisibleRegion();
+ return 'UNEXPECTED_SUCCESS';
+ } catch (err) {
+ return err.message ? err.message : String(err);
+ }
+ })();
+ )";
+
+ EXPECT_EQ("Failed to capture webview: screenshot has been disabled",
+ content::EvalJs(app_frame, script));
+}
+
IN_PROC_BROWSER_TEST_F(ControlledFrameApiTest, EnterFullScreenControlledFrame) {
const GURL embed_url = embedded_https_test_server().GetURL("/index.html");
const url::Origin embed_origin = url::Origin::Create(embed_url);
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 4c28ce46..40ecdf6 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -529,6 +529,7 @@
"sandboxed_unpacker.h",
"scoped_active_install.cc",
"scoped_active_install.h",
+ "screenshot_access.h",
"script_executor.cc",
"script_executor.h",
"script_injection_tracker.cc",
Regression Test / PoC
diff --git a/chrome/browser/controlled_frame/controlled_frame_apitest.cc b/chrome/browser/controlled_frame/controlled_frame_apitest.cc
index 357088d7..3f70244 100644
--- a/chrome/browser/controlled_frame/controlled_frame_apitest.cc
+++ b/chrome/browser/controlled_frame/controlled_frame_apitest.cc
@@ -25,9 +25,11 @@
#include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
+#include "chrome/common/pref_names.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/embedder_support/user_agent_utils.h"
+#include "components/prefs/pref_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/browser/service_worker_running_info.h"
@@ -479,6 +481,44 @@
EXPECT_EQ(kEvalSuccessStr, VerifyBackgroundColorIsRed(web_view_guest));
}
+IN_PROC_BROWSER_TEST_F(ControlledFrameApiTest,
+ CaptureVisibleRegionDisabledByPolicy) {
+ std::unique_ptr<web_app::ScopedBundledIsolatedWebApp> app =
+ web_app::IsolatedWebAppBuilder(
+ web_app::ManifestBuilder().AddPermissionsPolicy(
+ network::mojom::PermissionsPolicyFeature::kControlledFrame,
+ /*self=*/true,
+ /*origins=*/{}))
+ .BuildBundle();
+ app->TrustSigningKey();
+ ASSERT_OK_AND_ASSIGN(web_app::IsolatedWebAppUrlInfo url_info,
+ app->Install(profile()));
+ content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
+
+ ASSERT_TRUE(CreateControlledFrame(
+ app_frame, embedded_https_test_server().GetURL("/index.html")));
+
+ profile()->GetPrefs()->SetBoolean(prefs::kDisableScreenshots, true);
+
+ const std::string script = R"(
+ (async function() {
+ const frame = document.getElementsByTagName('controlledframe')[0];
+ if (!frame || !frame.request) {
+ return 'NO_FRAME';
+ }
+ try {
+ await frame.captureVisibleRegion();
+ return 'UNEXPECTED_SUCCESS';
+ } catch (err) {
+ return err.message ? err.message : String(err);
+ }
+ })();
+ )";
+
+ EXPECT_EQ("Failed to capture webview: screenshot has been disabled",
+ content::EvalJs(app_frame, script));
+}
+
IN_PROC_BROWSER_TEST_F(ControlledFrameApiTest, EnterFullScreenControlledFrame) {
const GURL embed_url = embedded_https_test_server().GetURL("/index.html");
const url::Origin embed_origin = url::Origin::Create(embed_url);