diff --git a/BUILD.gn b/BUILD.gn index 1ab182b5bb6a..552c362feadc 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -427,6 +427,7 @@ source_set("electron_lib") { "chromium_src:chrome_spellchecker", "shell/common:mojo", "shell/common:plugin", + "shell/common:web_contents_utility", "shell/services/node/public/mojom", "//base:base_static", "//base/allocator:buildflags", diff --git a/docs/api/session.md b/docs/api/session.md index 41126bb92521..7b4b1adc3caa 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -933,6 +933,7 @@ session.fromPartition('some-partition').setPermissionRequestHandler((webContents * `storage-access` - Allows content loaded in a third-party context to request access to third-party cookies using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API). * `top-level-storage-access` - Allow top-level sites to request third-party cookie access on behalf of embedded content originating from another site in the same related website set using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API). * `usb` - Expose non-standard Universal Serial Bus (USB) compatible devices services to the web with the [WebUSB API](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API). + * `deprecated-sync-clipboard-read` _Deprecated_ - Request access to run `document.execCommand("paste")` * `requestingOrigin` string - The origin URL of the permission check * `details` Object - Some properties are only available on certain permission types. * `embeddingOrigin` string (optional) - The origin of the frame embedding the frame that made the permission check. Only set for cross-origin sub frames making permission checks. diff --git a/docs/api/structures/web-preferences.md b/docs/api/structures/web-preferences.md index 414d9c6c3b02..26ead57c2329 100644 --- a/docs/api/structures/web-preferences.md +++ b/docs/api/structures/web-preferences.md @@ -148,6 +148,7 @@ this will cause the `preferred-size-changed` event to be emitted on the `WebContents` when the preferred size changes. Default is `false`. * `transparent` boolean (optional) - Whether to enable background transparency for the guest page. Default is `true`. **Note:** The guest page's text and background colors are derived from the [color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme) of its root element. When transparency is enabled, the text color will still change accordingly but the background will remain transparent. +* `enableDeprecatedPaste` boolean (optional) _Deprecated_ - Whether to enable the `paste` [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand). Default is `false`. [chrome-content-scripts]: https://developer.chrome.com/extensions/content_scripts#execution-environment [runtime-enabled-features]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/runtime_enabled_features.json5 diff --git a/docs/breaking-changes.md b/docs/breaking-changes.md index 32a22c9ffab9..9f481479e4ff 100644 --- a/docs/breaking-changes.md +++ b/docs/breaking-changes.md @@ -39,6 +39,15 @@ This brings the behavior to parity with Linux. Prior behavior: Menu bar is still ## Planned Breaking API Changes (33.0) +### Deprecated: `document.execCommand("paste")` + +The synchronous clipboard read API [document.execCommand("paste")](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard) has been +deprecated in favor of [async clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API). This is to align with the browser defaults. + +The `enableDeprecatedPaste` option on `WebPreferences` that triggers the permission +checks for this API and the associated permission type `deprecated-sync-clipboard-read` +are also deprecated. + ### Behavior Changed: frame properties may retrieve detached WebFrameMain instances or none at all APIs which provide access to a `WebFrameMain` instance may return an instance diff --git a/patches/chromium/.patches b/patches/chromium/.patches index fd4c1b71dd1f..fe0d2a059067 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -128,7 +128,7 @@ fix_font_face_resolution_when_renderer_is_blocked.patch feat_enable_passing_exit_code_on_service_process_crash.patch chore_remove_reference_to_chrome_browser_themes.patch feat_enable_customizing_symbol_color_in_framecaptionbutton.patch -build_expose_webplugininfo_interface_to_electron.patch +build_allow_electron_mojom_interfaces_to_depend_on_blink.patch osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch chore_partial_revert_of.patch @@ -139,3 +139,4 @@ cherry-pick-1282289030ab.patch cherry-pick-3dc17c461b12.patch ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch feat_add_signals_when_embedder_cleanup_callbacks_run_for.patch +feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch diff --git a/patches/chromium/build_expose_webplugininfo_interface_to_electron.patch b/patches/chromium/build_allow_electron_mojom_interfaces_to_depend_on_blink.patch similarity index 55% rename from patches/chromium/build_expose_webplugininfo_interface_to_electron.patch rename to patches/chromium/build_allow_electron_mojom_interfaces_to_depend_on_blink.patch index 57d4fd242113..27b0621db7da 100644 --- a/patches/chromium/build_expose_webplugininfo_interface_to_electron.patch +++ b/patches/chromium/build_allow_electron_mojom_interfaces_to_depend_on_blink.patch @@ -1,20 +1,24 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: deepak1556 Date: Fri, 9 Aug 2024 22:39:47 +0900 -Subject: build: expose webplugininfo interface to electron +Subject: build: allow electron mojom interfaces to depend on blink + mojom_platform -Allows implementing electron::mojom::ElectronPluginInfoHost interface -which provides plugin details between browser<->renderer. +Needed for: + +1) //electron/shell/common:plugin +2) //electron/shell/common:web_contents_utility diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn -index b84e3a73036b0bc2b782d35c04359ba401521917..2d8b206a118592f15a93c2a7bcb2c37c80571cd8 100644 +index b84e3a73036b0bc2b782d35c04359ba401521917..0099cf78779215bcc85ce3fb57cb34827337111e 100644 --- a/content/public/common/BUILD.gn +++ b/content/public/common/BUILD.gn -@@ -377,6 +377,7 @@ mojom("interfaces") { +@@ -377,6 +377,8 @@ mojom("interfaces") { "//content/common/*", "//extensions/common:mojom", "//extensions/common:mojom_blink", + "//electron/shell/common:plugin", ++ "//electron/shell/common:web_contents_utility", ] sources = [ diff --git a/patches/chromium/feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch b/patches/chromium/feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch new file mode 100644 index 000000000000..11a449783988 --- /dev/null +++ b/patches/chromium/feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch @@ -0,0 +1,114 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: deepak1556 +Date: Thu, 30 Jan 2025 20:28:38 +0900 +Subject: feat: separate content settings callback for sync and async clipboard + +`AllowReadFromClipboard` is called from both the types without a way to differentiate. + +[sync path] - third_party/blink/renderer/core/editing/commands/clipboard_commands.cc +[async path] - third_party/blink/renderer/modules/clipboard/clipboard_promise.cc + +This patch adds a new callback to separate these two paths so that we +can have sync permission checks for the sync path. + +Additionally, `blink::PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ` +has been added to support type conversion in permission policy checks. We have extended +`blink::PermissionType` in `electron::WebContentsPermissionHelper::PermissionType` +but it is hard to import the latter into the content permission layer checks. + +This patch will be removed when the deprecated sync api support is +removed. + +diff --git a/components/permissions/permission_util.cc b/components/permissions/permission_util.cc +index d21e492cac6b6750dfab320e35c94841df54e2e4..3c05ae9bf223e9b3b6ca6d4c05be31c517e2a996 100644 +--- a/components/permissions/permission_util.cc ++++ b/components/permissions/permission_util.cc +@@ -364,6 +364,7 @@ ContentSettingsType PermissionUtil::PermissionTypeToContentSettingsTypeSafe( + return ContentSettingsType::AUTOMATIC_FULLSCREEN; + case PermissionType::WEB_APP_INSTALLATION: + return ContentSettingsType::WEB_APP_INSTALLATION; ++ case PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: + case PermissionType::NUM: + break; + } +diff --git a/content/browser/permissions/permission_controller_impl.cc b/content/browser/permissions/permission_controller_impl.cc +index 4e6b9b4073892051a72e40808365fe59c1e77903..e4403c1e95510b7846cc57f3c5a95a41480149f5 100644 +--- a/content/browser/permissions/permission_controller_impl.cc ++++ b/content/browser/permissions/permission_controller_impl.cc +@@ -94,6 +94,7 @@ PermissionToSchedulingFeature(PermissionType permission_name) { + case PermissionType::POINTER_LOCK: + case PermissionType::AUTOMATIC_FULLSCREEN: + case PermissionType::WEB_APP_INSTALLATION: ++ case PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: + return std::nullopt; + } + } +diff --git a/third_party/blink/common/permissions/permission_utils.cc b/third_party/blink/common/permissions/permission_utils.cc +index baa74bd6c7bf6350f4ef06f00d94d1d517b43943..2d4f846f08383e22e42c55f783eb1041de4cd258 100644 +--- a/third_party/blink/common/permissions/permission_utils.cc ++++ b/third_party/blink/common/permissions/permission_utils.cc +@@ -99,6 +99,8 @@ std::string GetPermissionString(PermissionType permission) { + return "AutomaticFullscreen"; + case PermissionType::WEB_APP_INSTALLATION: + return "WebAppInstallation"; ++ case PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: ++ return "DeprecatedSyncClipboardRead"; + case PermissionType::NUM: + NOTREACHED(); + } +@@ -171,6 +173,7 @@ PermissionTypeToPermissionsPolicyFeature(PermissionType permission) { + case PermissionType::NOTIFICATIONS: + case PermissionType::KEYBOARD_LOCK: + case PermissionType::POINTER_LOCK: ++ case PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: + return std::nullopt; + + case PermissionType::NUM: +diff --git a/third_party/blink/public/common/permissions/permission_utils.h b/third_party/blink/public/common/permissions/permission_utils.h +index ae03b7f099d30c157cfda7d1beb7c535d3615471..ca287e7a5271ee83c393de6c1fe347973f4292ba 100644 +--- a/third_party/blink/public/common/permissions/permission_utils.h ++++ b/third_party/blink/public/common/permissions/permission_utils.h +@@ -64,6 +64,7 @@ enum class PermissionType { + AUTOMATIC_FULLSCREEN = 40, + HAND_TRACKING = 41, + WEB_APP_INSTALLATION = 42, ++ DEPRECATED_SYNC_CLIPBOARD_READ = 43, + + // Always keep this at the end. + NUM, +diff --git a/third_party/blink/public/platform/web_content_settings_client.h b/third_party/blink/public/platform/web_content_settings_client.h +index 28f616f21f998c7cd1c794e58efaccf9e6c11e6e..c64896642209124e500db2ed6fe2357e426cd10b 100644 +--- a/third_party/blink/public/platform/web_content_settings_client.h ++++ b/third_party/blink/public/platform/web_content_settings_client.h +@@ -55,6 +55,9 @@ class WebContentSettingsClient { + // Controls whether access to write the clipboard is allowed for this frame. + virtual bool AllowWriteToClipboard() { return false; } + ++ // Controls whether synchronous access to read the clipboard is allowed for this frame. ++ virtual bool AllowReadFromClipboardSync() { return false; } ++ + // Controls whether to enable MutationEvents for this frame. + // The common use case of this method is actually to selectively disable + // MutationEvents, but it's been named for consistency with the rest of the +diff --git a/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc b/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc +index 20ebd3f2f5fa7b16ad1b2081ca41b007bc78a354..b248e3135182d36a6524c2e626157a0e4c759d14 100644 +--- a/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc ++++ b/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc +@@ -121,7 +121,7 @@ bool ClipboardCommands::CanReadClipboard(LocalFrame& frame, + return true; + } + return frame.GetContentSettingsClient() && +- frame.GetContentSettingsClient()->AllowReadFromClipboard(); ++ frame.GetContentSettingsClient()->AllowReadFromClipboardSync(); + } + + bool ClipboardCommands::CanWriteClipboard(LocalFrame& frame, +@@ -300,7 +300,7 @@ bool ClipboardCommands::PasteSupported(LocalFrame* frame) { + return true; + } + return frame->GetContentSettingsClient() && +- frame->GetContentSettingsClient()->AllowReadFromClipboard(); ++ frame->GetContentSettingsClient()->AllowReadFromClipboardSync(); + } + + bool ClipboardCommands::ExecuteCopy(LocalFrame& frame, diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 6c28fee6f80e..13a584a827f1 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -70,7 +70,6 @@ #include "content/public/common/webplugininfo.h" #include "electron/buildflags/buildflags.h" #include "electron/mas.h" -#include "electron/shell/common/api/api.mojom.h" #include "gin/arguments.h" #include "gin/data_object_builder.h" #include "gin/handle.h" @@ -110,6 +109,7 @@ #include "shell/browser/web_contents_zoom_controller.h" #include "shell/browser/web_view_guest_delegate.h" #include "shell/browser/web_view_manager.h" +#include "shell/common/api/api.mojom.h" #include "shell/common/api/electron_api_native_image.h" #include "shell/common/api/electron_bindings.h" #include "shell/common/color_util.h" diff --git a/shell/browser/api/electron_api_web_contents.h b/shell/browser/api/electron_api_web_contents.h index 2c75de6c140c..941002f3aa37 100644 --- a/shell/browser/api/electron_api_web_contents.h +++ b/shell/browser/api/electron_api_web_contents.h @@ -31,7 +31,6 @@ #include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_observer.h" #include "electron/buildflags/buildflags.h" -#include "electron/shell/common/api/api.mojom.h" #include "gin/handle.h" #include "gin/wrappable.h" #include "printing/buildflags/buildflags.h" @@ -42,9 +41,11 @@ #include "shell/browser/osr/osr_paint_event.h" #include "shell/browser/ui/inspectable_web_contents_delegate.h" #include "shell/browser/ui/inspectable_web_contents_view_delegate.h" +#include "shell/common/api/api.mojom.h" #include "shell/common/gin_helper/cleaned_up_at_exit.h" #include "shell/common/gin_helper/constructible.h" #include "shell/common/gin_helper/pinnable.h" +#include "shell/common/web_contents_utility.mojom.h" #include "ui/base/models/image_model.h" #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) diff --git a/shell/browser/api/electron_api_web_frame_main.cc b/shell/browser/api/electron_api_web_frame_main.cc index 7ced5e5763cd..f52be1145c67 100644 --- a/shell/browser/api/electron_api_web_frame_main.cc +++ b/shell/browser/api/electron_api_web_frame_main.cc @@ -17,13 +17,13 @@ #include "content/public/browser/frame_tree_node_id.h" #include "content/public/browser/render_frame_host.h" #include "content/public/common/isolated_world_ids.h" -#include "electron/shell/common/api/api.mojom.h" #include "gin/handle.h" #include "gin/object_template_builder.h" #include "services/service_manager/public/cpp/interface_provider.h" #include "shell/browser/api/message_port.h" #include "shell/browser/browser.h" #include "shell/browser/javascript_environment.h" +#include "shell/common/api/api.mojom.h" #include "shell/common/gin_converters/blink_converter.h" #include "shell/common/gin_converters/frame_converter.h" #include "shell/common/gin_converters/gurl_converter.h" diff --git a/shell/browser/electron_api_ipc_handler_impl.h b/shell/browser/electron_api_ipc_handler_impl.h index 96e9a620ebd5..819407a0de7b 100644 --- a/shell/browser/electron_api_ipc_handler_impl.h +++ b/shell/browser/electron_api_ipc_handler_impl.h @@ -10,9 +10,9 @@ #include "base/memory/weak_ptr.h" #include "content/public/browser/global_routing_id.h" #include "content/public/browser/web_contents_observer.h" -#include "electron/shell/common/api/api.mojom.h" #include "mojo/public/cpp/bindings/associated_receiver.h" #include "shell/browser/api/electron_api_web_contents.h" +#include "shell/common/api/api.mojom.h" namespace content { class RenderFrameHost; diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index b473468b289f..c1af79c5e95f 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -52,7 +52,6 @@ #include "crypto/crypto_buildflags.h" #include "electron/buildflags/buildflags.h" #include "electron/fuses.h" -#include "electron/shell/common/api/api.mojom.h" #include "extensions/browser/extension_navigation_ui_data.h" #include "extensions/common/extension_id.h" #include "mojo/public/cpp/bindings/binder_map.h" @@ -116,6 +115,7 @@ #include "shell/common/platform_util.h" #include "shell/common/plugin.mojom.h" #include "shell/common/thread_restrictions.h" +#include "shell/common/web_contents_utility.mojom.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" #include "third_party/blink/public/common/loader/url_loader_throttle.h" #include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h" diff --git a/shell/browser/electron_permission_manager.cc b/shell/browser/electron_permission_manager.cc index af5b852853cf..ea65ac18da64 100644 --- a/shell/browser/electron_permission_manager.cc +++ b/shell/browser/electron_permission_manager.cc @@ -298,8 +298,13 @@ bool ElectronPermissionManager::CheckPermissionWithDetails( content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, base::Value::Dict details) const { - if (check_handler_.is_null()) - return true; + if (check_handler_.is_null()) { + if (permission == blink::PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ) { + return false; + } else { + return true; + } + } auto* web_contents = render_frame_host diff --git a/shell/browser/electron_web_contents_utility_handler_impl.cc b/shell/browser/electron_web_contents_utility_handler_impl.cc index 33b5f017906a..e1baff6b4eae 100644 --- a/shell/browser/electron_web_contents_utility_handler_impl.cc +++ b/shell/browser/electron_web_contents_utility_handler_impl.cc @@ -6,15 +6,19 @@ #include +#include "content/public/browser/browser_context.h" +#include "content/public/browser/permission_controller.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" +#include "shell/browser/web_contents_permission_helper.h" +#include "third_party/blink/public/mojom/permissions/permission_status.mojom.h" namespace electron { ElectronWebContentsUtilityHandlerImpl::ElectronWebContentsUtilityHandlerImpl( content::RenderFrameHost* frame_host, mojo::PendingAssociatedReceiver receiver) - : render_frame_host_id_(frame_host->GetGlobalId()) { + : render_frame_host_token_(frame_host->GetGlobalFrameToken()) { content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(frame_host); DCHECK(web_contents); @@ -28,8 +32,11 @@ ElectronWebContentsUtilityHandlerImpl::ElectronWebContentsUtilityHandlerImpl( ElectronWebContentsUtilityHandlerImpl:: ~ElectronWebContentsUtilityHandlerImpl() = default; -void ElectronWebContentsUtilityHandlerImpl::WebContentsDestroyed() { - delete this; +void ElectronWebContentsUtilityHandlerImpl::RenderFrameDeleted( + content::RenderFrameHost* render_frame_host) { + if (render_frame_host->GetGlobalFrameToken() == render_frame_host_token_) { + delete this; + } } void ElectronWebContentsUtilityHandlerImpl::OnConnectionError() { @@ -59,9 +66,42 @@ void ElectronWebContentsUtilityHandlerImpl::DoGetZoomLevel( } } +void ElectronWebContentsUtilityHandlerImpl::CanAccessClipboardDeprecated( + mojom::PermissionName name, + const blink::LocalFrameToken& frame_token, + CanAccessClipboardDeprecatedCallback callback) { + if (render_frame_host_token_.frame_token == frame_token) { + // Paste requires either (1) user activation, ... + if (web_contents()->HasRecentInteraction()) { + std::move(callback).Run(blink::mojom::PermissionStatus::GRANTED); + return; + } + + // (2) granted permission, ... + content::RenderFrameHost* render_frame_host = GetRenderFrameHost(); + content::BrowserContext* browser_context = + render_frame_host->GetBrowserContext(); + content::PermissionController* permission_controller = + browser_context->GetPermissionController(); + blink::PermissionType permission; + if (name == mojom::PermissionName::DEPRECATED_SYNC_CLIPBOARD_READ) { + permission = blink::PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ; + } else { + std::move(callback).Run(blink::mojom::PermissionStatus::DENIED); + return; + } + blink::mojom::PermissionStatus status = + permission_controller->GetPermissionStatusForCurrentDocument( + permission, render_frame_host); + std::move(callback).Run(status); + } else { + std::move(callback).Run(blink::mojom::PermissionStatus::DENIED); + } +} + content::RenderFrameHost* ElectronWebContentsUtilityHandlerImpl::GetRenderFrameHost() { - return content::RenderFrameHost::FromID(render_frame_host_id_); + return content::RenderFrameHost::FromFrameToken(render_frame_host_token_); } // static diff --git a/shell/browser/electron_web_contents_utility_handler_impl.h b/shell/browser/electron_web_contents_utility_handler_impl.h index 2f0f07d007ed..c4b53c4a5193 100644 --- a/shell/browser/electron_web_contents_utility_handler_impl.h +++ b/shell/browser/electron_web_contents_utility_handler_impl.h @@ -10,7 +10,7 @@ #include "base/memory/weak_ptr.h" #include "content/public/browser/global_routing_id.h" #include "content/public/browser/web_contents_observer.h" -#include "electron/shell/common/api/api.mojom.h" +#include "electron/shell/common/web_contents_utility.mojom.h" #include "mojo/public/cpp/bindings/associated_receiver.h" #include "shell/browser/api/electron_api_web_contents.h" @@ -43,6 +43,10 @@ class ElectronWebContentsUtilityHandlerImpl void OnFirstNonEmptyLayout() override; void SetTemporaryZoomLevel(double level) override; void DoGetZoomLevel(DoGetZoomLevelCallback callback) override; + void CanAccessClipboardDeprecated( + mojom::PermissionName name, + const blink::LocalFrameToken& frame_token, + CanAccessClipboardDeprecatedCallback callback) override; base::WeakPtr GetWeakPtr() { return weak_factory_.GetWeakPtr(); @@ -52,13 +56,13 @@ class ElectronWebContentsUtilityHandlerImpl ~ElectronWebContentsUtilityHandlerImpl() override; // content::WebContentsObserver: - void WebContentsDestroyed() override; + void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; void OnConnectionError(); content::RenderFrameHost* GetRenderFrameHost(); - content::GlobalRenderFrameHostId render_frame_host_id_; + content::GlobalRenderFrameHostToken render_frame_host_token_; mojo::AssociatedReceiver receiver_{this}; diff --git a/shell/browser/web_contents_permission_helper.h b/shell/browser/web_contents_permission_helper.h index 4dfd6a53f7cc..0bf7ea496521 100644 --- a/shell/browser/web_contents_permission_helper.h +++ b/shell/browser/web_contents_permission_helper.h @@ -32,7 +32,7 @@ class WebContentsPermissionHelper HID, USB, KEYBOARD_LOCK, - FILE_SYSTEM + FILE_SYSTEM, }; // Asynchronous Requests diff --git a/shell/browser/web_contents_preferences.cc b/shell/browser/web_contents_preferences.cc index 19c81d14374f..0d97bf0b6198 100644 --- a/shell/browser/web_contents_preferences.cc +++ b/shell/browser/web_contents_preferences.cc @@ -148,6 +148,7 @@ void WebContentsPreferences::Clear() { blink::mojom::ImageAnimationPolicy::kImageAnimationPolicyAllowed; preload_path_ = std::nullopt; v8_cache_options_ = blink::mojom::V8CacheOptions::kDefault; + deprecated_paste_enabled_ = false; #if BUILDFLAG(IS_MAC) scroll_bounce_ = false; @@ -245,6 +246,9 @@ void WebContentsPreferences::SetFromDictionary( web_preferences.Get("v8CacheOptions", &v8_cache_options_); + web_preferences.Get(options::kEnableDeprecatedPaste, + &deprecated_paste_enabled_); + #if BUILDFLAG(IS_MAC) web_preferences.Get(options::kScrollBounce, &scroll_bounce_); #endif @@ -472,6 +476,8 @@ void WebContentsPreferences::OverrideWebkitPrefs( prefs->webview_tag = webview_tag_; prefs->v8_cache_options = v8_cache_options_; + + prefs->dom_paste_enabled = deprecated_paste_enabled_; } WEB_CONTENTS_USER_DATA_KEY_IMPL(WebContentsPreferences); diff --git a/shell/browser/web_contents_preferences.h b/shell/browser/web_contents_preferences.h index b134f1aa76b5..737957fb2377 100644 --- a/shell/browser/web_contents_preferences.h +++ b/shell/browser/web_contents_preferences.h @@ -132,6 +132,7 @@ class WebContentsPreferences blink::mojom::ImageAnimationPolicy image_animation_policy_; std::optional preload_path_; blink::mojom::V8CacheOptions v8_cache_options_; + bool deprecated_paste_enabled_ = false; #if BUILDFLAG(IS_MAC) bool scroll_bounce_; diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index e9b365fddb2d..7b25bdecc714 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -26,3 +26,16 @@ mojom("plugin") { "//mojo/public/mojom/base", ] } + +mojom("web_contents_utility") { + # We don't want Blink variants of these bindings to be generated. + disable_variants = true + + sources = [ "web_contents_utility.mojom" ] + + public_deps = [ + "//content/public/common:interfaces", + "//third_party/blink/public/mojom/tokens", + "//url/mojom:url_mojom_origin", + ] +} diff --git a/shell/common/api/api.mojom b/shell/common/api/api.mojom index 64c60f25cdfb..b9859ba7f272 100644 --- a/shell/common/api/api.mojom +++ b/shell/common/api/api.mojom @@ -25,17 +25,6 @@ interface ElectronAutofillDriver { HideAutofillPopup(); }; -interface ElectronWebContentsUtility { - // Informs underlying WebContents that first non-empty layout was performed - // by compositor. - OnFirstNonEmptyLayout(); - - SetTemporaryZoomLevel(double zoom_level); - - [Sync] - DoGetZoomLevel() => (double result); -}; - interface ElectronApiIPC { // Emits an event on |channel| from the ipcMain JavaScript object in the main // process. diff --git a/shell/common/gin_converters/content_converter.cc b/shell/common/gin_converters/content_converter.cc index 888e848576f8..d5a257b8f24a 100644 --- a/shell/common/gin_converters/content_converter.cc +++ b/shell/common/gin_converters/content_converter.cc @@ -223,6 +223,8 @@ v8::Local Converter::ToV8( return StringToV8(isolate, "speaker-selection"); case blink::PermissionType::WEB_APP_INSTALLATION: return StringToV8(isolate, "web-app-installation"); + case blink::PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: + return StringToV8(isolate, "deprecated-sync-clipboard-read"); case blink::PermissionType::NUM: break; } diff --git a/shell/common/options_switches.cc b/shell/common/options_switches.cc index 918d5faf369c..0cbc3ef0f1fa 100644 --- a/shell/common/options_switches.cc +++ b/shell/common/options_switches.cc @@ -187,6 +187,10 @@ const char kEnablePreferredSizeMode[] = "enablePreferredSizeMode"; const char ktitleBarOverlay[] = "titleBarOverlay"; +// Enables the permission managed support for +// document.execCommand("paste"). +const char kEnableDeprecatedPaste[] = "enableDeprecatedPaste"; + } // namespace options namespace switches { diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index 6b99e9e79dc3..0599886bcbf0 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -95,6 +95,8 @@ extern const char kHiddenPage[]; extern const char kSpellcheck[]; #endif +extern const char kEnableDeprecatedPaste[]; + } // namespace options // Following are actually command line switches, should be moved to other files. diff --git a/shell/common/web_contents_utility.mojom b/shell/common/web_contents_utility.mojom new file mode 100644 index 000000000000..a941ee42a71d --- /dev/null +++ b/shell/common/web_contents_utility.mojom @@ -0,0 +1,25 @@ +module electron.mojom; + +import "third_party/blink/public/mojom/permissions/permission_status.mojom"; +import "third_party/blink/public/mojom/tokens/tokens.mojom"; +import "url/mojom/origin.mojom"; + +enum PermissionName { + DEPRECATED_SYNC_CLIPBOARD_READ, +}; + +interface ElectronWebContentsUtility { + // Informs underlying WebContents that first non-empty layout was performed + // by compositor. + OnFirstNonEmptyLayout(); + + SetTemporaryZoomLevel(double zoom_level); + + [Sync] + DoGetZoomLevel() => (double result); + + [Sync] + CanAccessClipboardDeprecated( + PermissionName name, + blink.mojom.LocalFrameToken frame_token) => (blink.mojom.PermissionStatus status); +}; diff --git a/shell/renderer/api/electron_api_web_frame.cc b/shell/renderer/api/electron_api_web_frame.cc index 86679ea2922c..59a42c0b33d2 100644 --- a/shell/renderer/api/electron_api_web_frame.cc +++ b/shell/renderer/api/electron_api_web_frame.cc @@ -31,6 +31,7 @@ #include "shell/common/gin_helper/promise.h" #include "shell/common/node_includes.h" #include "shell/common/options_switches.h" +#include "shell/common/web_contents_utility.mojom.h" #include "shell/renderer/api/context_bridge/object_cache.h" #include "shell/renderer/api/electron_api_context_bridge.h" #include "shell/renderer/api/electron_api_spell_check_client.h" diff --git a/shell/renderer/content_settings_observer.cc b/shell/renderer/content_settings_observer.cc index ce66e8d00785..e0d2cdae3d2c 100644 --- a/shell/renderer/content_settings_observer.cc +++ b/shell/renderer/content_settings_observer.cc @@ -6,10 +6,12 @@ #include "content/public/renderer/render_frame.h" #include "shell/common/options_switches.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" #include "third_party/blink/public/common/web_preferences/web_preferences.h" #include "third_party/blink/public/platform/url_conversion.h" #include "third_party/blink/public/platform/web_security_origin.h" #include "third_party/blink/public/web/web_local_frame.h" +#include "third_party/blink/public/web/web_view.h" namespace electron { @@ -21,6 +23,15 @@ ContentSettingsObserver::ContentSettingsObserver( ContentSettingsObserver::~ContentSettingsObserver() = default; +mojom::ElectronWebContentsUtility& +ContentSettingsObserver::GetWebContentsUtility() { + if (!web_contents_utility_) { + render_frame()->GetRemoteAssociatedInterfaces()->GetInterface( + &web_contents_utility_); + } + return *web_contents_utility_; +} + bool ContentSettingsObserver::AllowStorageAccessSync(StorageType storage_type) { blink::WebFrame* frame = render_frame()->GetWebFrame(); if (frame->GetSecurityOrigin().IsOpaque() || @@ -32,6 +43,20 @@ bool ContentSettingsObserver::AllowStorageAccessSync(StorageType storage_type) { return true; } +bool ContentSettingsObserver::AllowReadFromClipboardSync() { + blink::WebLocalFrame* frame = render_frame()->GetWebFrame(); + if (frame->View()->GetWebPreferences().dom_paste_enabled) { + blink::mojom::PermissionStatus status{ + blink::mojom::PermissionStatus::DENIED}; + GetWebContentsUtility().CanAccessClipboardDeprecated( + mojom::PermissionName::DEPRECATED_SYNC_CLIPBOARD_READ, + frame->GetLocalFrameToken(), &status); + return status == blink::mojom::PermissionStatus::GRANTED; + } else { + return false; + } +} + void ContentSettingsObserver::OnDestruct() { delete this; } diff --git a/shell/renderer/content_settings_observer.h b/shell/renderer/content_settings_observer.h index 77c04b973a5f..c58dcf627166 100644 --- a/shell/renderer/content_settings_observer.h +++ b/shell/renderer/content_settings_observer.h @@ -6,7 +6,10 @@ #define ELECTRON_SHELL_RENDERER_CONTENT_SETTINGS_OBSERVER_H_ #include "content/public/renderer/render_frame_observer.h" +#include "mojo/public/cpp/bindings/associated_remote.h" +#include "shell/common/web_contents_utility.mojom.h" #include "third_party/blink/public/platform/web_content_settings_client.h" +#include "url/origin.h" namespace electron { @@ -22,10 +25,17 @@ class ContentSettingsObserver : public content::RenderFrameObserver, // blink::WebContentSettingsClient implementation. bool AllowStorageAccessSync(StorageType storage_type) override; + bool AllowReadFromClipboardSync() override; private: // content::RenderFrameObserver implementation. void OnDestruct() override; + + // A getter for `content_settings_manager_` that ensures it is bound. + mojom::ElectronWebContentsUtility& GetWebContentsUtility(); + + mojo::AssociatedRemote + web_contents_utility_; }; } // namespace electron diff --git a/shell/renderer/electron_api_service_impl.h b/shell/renderer/electron_api_service_impl.h index 1a50a18b0143..8c6820f020f1 100644 --- a/shell/renderer/electron_api_service_impl.h +++ b/shell/renderer/electron_api_service_impl.h @@ -10,9 +10,9 @@ #include "base/memory/weak_ptr.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame_observer.h" -#include "electron/shell/common/api/api.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver.h" +#include "shell/common/api/api.mojom.h" namespace electron { diff --git a/shell/renderer/electron_render_frame_observer.cc b/shell/renderer/electron_render_frame_observer.cc index b8c7a464b655..23b2b6698f19 100644 --- a/shell/renderer/electron_render_frame_observer.cc +++ b/shell/renderer/electron_render_frame_observer.cc @@ -9,13 +9,14 @@ #include "base/memory/ref_counted_memory.h" #include "base/trace_event/trace_event.h" #include "content/public/renderer/render_frame.h" -#include "electron/shell/common/api/api.mojom.h" #include "ipc/ipc_message_macros.h" #include "net/base/net_module.h" #include "net/grit/net_resources.h" #include "services/service_manager/public/cpp/interface_provider.h" +#include "shell/common/api/api.mojom.h" #include "shell/common/gin_helper/microtasks_scope.h" #include "shell/common/options_switches.h" +#include "shell/common/web_contents_utility.mojom.h" #include "shell/common/world_ids.h" #include "shell/renderer/renderer_client_base.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index bc36b1937b59..fa64d54cc39b 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -3324,6 +3324,153 @@ describe('navigator.clipboard.write', () => { }); }); +describe('paste execCommand', () => { + const readClipboard: any = (w: BrowserWindow) => { + return w.webContents.executeJavaScript(` + new Promise((resolve) => { + const timeout = setTimeout(() => { + resolve(''); + }, 2000); + document.addEventListener('paste', (event) => { + clearTimeout(timeout); + event.preventDefault(); + let paste = event.clipboardData.getData("text"); + resolve(paste); + }); + document.execCommand('paste'); + }); + `, true); + }; + + let ses: Electron.Session; + beforeEach(() => { + ses = session.fromPartition(`paste-execCommand-${Math.random()}`); + }); + + afterEach(() => { + ses.setPermissionCheckHandler(null); + closeAllWindows(); + }); + + it('is disabled by default', async () => { + const w: BrowserWindow = new BrowserWindow({}); + await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); + const text = 'Sync Clipboard Disabled by default'; + clipboard.write({ + text + }); + const paste = await readClipboard(w); + expect(paste).to.be.empty(); + expect(clipboard.readText()).to.equal(text); + }); + + it('does not execute with default permissions', async () => { + const w: BrowserWindow = new BrowserWindow({ + webPreferences: { + enableDeprecatedPaste: true, + session: ses + } + }); + await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); + const text = 'Sync Clipboard Disabled by default permissions'; + clipboard.write({ + text + }); + const paste = await readClipboard(w); + expect(paste).to.be.empty(); + expect(clipboard.readText()).to.equal(text); + }); + + it('does not execute with permission denied', async () => { + const w: BrowserWindow = new BrowserWindow({ + webPreferences: { + enableDeprecatedPaste: true, + session: ses + } + }); + await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); + ses.setPermissionCheckHandler((webContents, permission) => { + if (permission === 'deprecated-sync-clipboard-read') { + return false; + } + return true; + }); + const text = 'Sync Clipboard Disabled by permission denied'; + clipboard.write({ + text + }); + const paste = await readClipboard(w); + expect(paste).to.be.empty(); + expect(clipboard.readText()).to.equal(text); + }); + + it('can trigger paste event when permission is granted', async () => { + const w: BrowserWindow = new BrowserWindow({ + webPreferences: { + enableDeprecatedPaste: true, + session: ses + } + }); + await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); + ses.setPermissionCheckHandler((webContents, permission) => { + if (permission === 'deprecated-sync-clipboard-read') { + return true; + } + return false; + }); + const text = 'Sync Clipboard Test'; + clipboard.write({ + text + }); + const paste = await readClipboard(w); + expect(paste).to.equal(text); + }); + + it('can trigger paste event when permission is granted for child windows', async () => { + const w: BrowserWindow = new BrowserWindow({ + webPreferences: { + session: ses + } + }); + await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); + w.webContents.setWindowOpenHandler(details => { + if (details.url === 'about:blank') { + return { + action: 'allow', + overrideBrowserWindowOptions: { + webPreferences: { + enableDeprecatedPaste: true, + session: ses + } + } + }; + } else { + return { + action: 'deny' + }; + } + }); + ses.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) => { + if (requestingOrigin === `${webContents?.opener?.origin}/` && + details.requestingUrl === 'about:blank' && + permission === 'deprecated-sync-clipboard-read') { + return true; + } + return false; + }); + const childPromise = once(w.webContents, 'did-create-window') as Promise<[BrowserWindow, Electron.DidCreateWindowDetails]>; + w.webContents.executeJavaScript('window.open("about:blank")', true); + const [childWindow] = await childPromise; + expect(childWindow.webContents.opener).to.equal(w.webContents.mainFrame); + const text = 'Sync Clipboard Test for Child Window'; + clipboard.write({ + text + }); + const paste = await readClipboard(childWindow); + expect(paste).to.equal(text); + }); +}); + ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.setAppBadge/clearAppBadge', () => { let w: BrowserWindow;