aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGae24 <96017547+Gae24@users.noreply.github.com>2025-01-15 20:45:29 +0100
committerGitHub <noreply@github.com>2025-01-15 19:45:29 +0000
commitd470f219b1cb80f50bb9050a7649627397f7a5d9 (patch)
treeab726a47db048f1a14edab25d686f2c2b10e52d5
parentcd9e831e91e7587a71c5fa401b3721b5cfad8f43 (diff)
downloadservo-d470f219b1cb80f50bb9050a7649627397f7a5d9.tar.gz
servo-d470f219b1cb80f50bb9050a7649627397f7a5d9.zip
Implement Clipboard Event Api (#33576)
* implement ClipboardEvent interface Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * draft implementation of clipboard events Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * handle received clipboard events inside html elemtents Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * use rustdoc style Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * fix compilation errors due to rebase Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * update arboard crate Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * improve paste events Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * code cleanup revert arboard crate's update, handle text only Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * restrict visibility of some methods to script crate Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * propagate CanGc argument Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * simplify handle_clipboard_msg Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * remove code duplication Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * fix potential borrow hazard Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * add clipboard_event pref, restore unit test code Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * retrict visibility of some document's methods Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * check if clipboardevent is trusted Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * enable clipboardevent Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * fix compilation for egl ports Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> --------- Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com>
-rw-r--r--components/compositing/windowing.rs7
-rw-r--r--components/config/prefs.rs2
-rw-r--r--components/constellation/constellation.rs51
-rw-r--r--components/constellation/tracing.rs3
-rw-r--r--components/script/dom/clipboardevent.rs97
-rw-r--r--components/script/dom/datatransfer.rs26
-rw-r--r--components/script/dom/document.rs200
-rw-r--r--components/script/dom/htmlinputelement.rs10
-rw-r--r--components/script/dom/htmltextareaelement.rs8
-rw-r--r--components/script/dom/mod.rs1
-rw-r--r--components/script/dom/webidls/ClipboardEvent.webidl15
-rw-r--r--components/script/drag_data_store.rs12
-rw-r--r--components/script/script_thread.rs4
-rw-r--r--components/script/textinput.rs93
-rw-r--r--components/servo/lib.rs3
-rw-r--r--components/shared/compositing/constellation_msg.rs8
-rw-r--r--components/shared/embedder/lib.rs4
-rw-r--r--components/shared/script/lib.rs28
-rw-r--r--ports/servoshell/desktop/tracing.rs2
-rw-r--r--ports/servoshell/desktop/webview.rs26
-rw-r--r--ports/servoshell/egl/servo_glue.rs3
-rw-r--r--resources/prefs.json1
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json2
-rw-r--r--tests/wpt/mozilla/tests/mozilla/interfaces.https.html1
24 files changed, 581 insertions, 26 deletions
diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs
index dce66be05ca..f2648246c2b 100644
--- a/components/compositing/windowing.rs
+++ b/components/compositing/windowing.rs
@@ -14,8 +14,8 @@ use keyboard_types::{CompositionEvent, KeyboardEvent};
use libc::c_void;
use net::protocols::ProtocolRegistry;
use script_traits::{
- GamepadEvent, MediaSessionActionType, MouseButton, Theme, TouchEventType, TouchId,
- TraversalDirection, WheelDelta,
+ ClipboardEventType, GamepadEvent, MediaSessionActionType, MouseButton, Theme, TouchEventType,
+ TouchId, TraversalDirection, WheelDelta,
};
use servo_geometry::{DeviceIndependentIntRect, DeviceIndependentIntSize, DeviceIndependentPixel};
use servo_url::ServoUrl;
@@ -134,6 +134,8 @@ pub enum EmbedderEvent {
Gamepad(GamepadEvent),
/// Vertical Synchronization tick
Vsync,
+ /// Sent when access to clipboard is required
+ ClipboardAction(ClipboardEventType),
}
impl Debug for EmbedderEvent {
@@ -196,6 +198,7 @@ impl Debug for EmbedderEvent {
EmbedderEvent::ReplaceNativeSurface(..) => write!(f, "ReplaceNativeSurface"),
EmbedderEvent::Gamepad(..) => write!(f, "Gamepad"),
EmbedderEvent::Vsync => write!(f, "Vsync"),
+ EmbedderEvent::ClipboardAction(_) => write!(f, "ClipboardAction"),
}
}
}
diff --git a/components/config/prefs.rs b/components/config/prefs.rs
index 9c4edf30e3a..1561198f281 100644
--- a/components/config/prefs.rs
+++ b/components/config/prefs.rs
@@ -75,6 +75,7 @@ pub struct Preferences {
pub dom_allow_scripts_to_close_windows: bool,
pub dom_canvas_capture_enabled: bool,
pub dom_canvas_text_enabled: bool,
+ pub dom_clipboardevent_enabled: bool,
pub dom_composition_event_enabled: bool,
pub dom_crypto_subtle_enabled: bool,
pub dom_customelements_enabled: bool,
@@ -236,6 +237,7 @@ impl Preferences {
dom_bluetooth_testing_enabled: false,
dom_canvas_capture_enabled: false,
dom_canvas_text_enabled: true,
+ dom_clipboardevent_enabled: true,
dom_composition_event_enabled: false,
dom_crypto_subtle_enabled: true,
dom_customelements_enabled: true,
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index 0558c234f51..50932dbad9d 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -138,13 +138,13 @@ use script_layout_interface::{LayoutFactory, ScriptThreadFactory};
use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent};
use script_traits::{
webdriver_msg, AnimationState, AnimationTickType, AuxiliaryBrowsingContextLoadInfo,
- BroadcastMsg, CompositorEvent, ConstellationControlMsg, DiscardBrowsingContext,
- DocumentActivity, DocumentState, GamepadEvent, IFrameLoadInfo, IFrameLoadInfoWithData,
- IFrameSandboxState, IFrameSizeMsg, Job, LayoutMsg as FromLayoutMsg, LoadData, LoadOrigin,
- LogEntry, MediaSessionActionType, MessagePortMsg, MouseEventType, NavigationHistoryBehavior,
- PortMessageTask, SWManagerMsg, SWManagerSenders, ScriptMsg as FromScriptMsg,
- ScriptToConstellationChan, ServiceWorkerManagerFactory, ServiceWorkerMsg,
- StructuredSerializedData, Theme, TraversalDirection, UpdatePipelineIdReason,
+ BroadcastMsg, ClipboardEventType, CompositorEvent, ConstellationControlMsg,
+ DiscardBrowsingContext, DocumentActivity, DocumentState, GamepadEvent, IFrameLoadInfo,
+ IFrameLoadInfoWithData, IFrameSandboxState, IFrameSizeMsg, Job, LayoutMsg as FromLayoutMsg,
+ LoadData, LoadOrigin, LogEntry, MediaSessionActionType, MessagePortMsg, MouseEventType,
+ NavigationHistoryBehavior, PortMessageTask, SWManagerMsg, SWManagerSenders,
+ ScriptMsg as FromScriptMsg, ScriptToConstellationChan, ServiceWorkerManagerFactory,
+ ServiceWorkerMsg, StructuredSerializedData, Theme, TraversalDirection, UpdatePipelineIdReason,
WebDriverCommandMsg, WindowSizeData, WindowSizeType,
};
use serde::{Deserialize, Serialize};
@@ -1491,6 +1491,9 @@ where
FromCompositorMsg::Gamepad(gamepad_event) => {
self.handle_gamepad_msg(gamepad_event);
},
+ FromCompositorMsg::Clipboard(clipboard_event) => {
+ self.handle_clipboard_msg(clipboard_event);
+ },
}
}
@@ -4269,6 +4272,40 @@ where
#[cfg_attr(
feature = "tracing",
+ tracing::instrument(skip_all, fields(servo_profiling = true))
+ )]
+ fn handle_clipboard_msg(&mut self, event: ClipboardEventType) {
+ let focused_browsing_context_id = self
+ .webviews
+ .focused_webview()
+ .map(|(_, webview)| webview.focused_browsing_context_id);
+
+ if let Some(browsing_context_id) = focused_browsing_context_id {
+ let event = CompositorEvent::ClipboardEvent(event);
+ let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
+ Some(ctx) => ctx.pipeline_id,
+ None => {
+ return warn!(
+ "{}: Got clipboard event for nonexistent browsing context",
+ browsing_context_id,
+ );
+ },
+ };
+ let msg = ConstellationControlMsg::SendEvent(pipeline_id, event);
+ let result = match self.pipelines.get(&pipeline_id) {
+ Some(pipeline) => pipeline.event_loop.send(msg),
+ None => {
+ return debug!("{}: Got clipboard event after closure", pipeline_id);
+ },
+ };
+ if let Err(e) = result {
+ self.handle_send_error(pipeline_id, e);
+ }
+ }
+ }
+
+ #[cfg_attr(
+ feature = "tracing",
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
)]
fn handle_reload_msg(&mut self, top_level_browsing_context_id: TopLevelBrowsingContextId) {
diff --git a/components/constellation/tracing.rs b/components/constellation/tracing.rs
index 88df4b49d6b..6c9924341da 100644
--- a/components/constellation/tracing.rs
+++ b/components/constellation/tracing.rs
@@ -93,6 +93,7 @@ mod from_compositor {
Self::IMEDismissed => target!("IMEDismissed"),
Self::ReadyToPresent(..) => target!("ReadyToPresent"),
Self::Gamepad(..) => target!("Gamepad"),
+ Self::Clipboard(..) => target!("Clipboard"),
}
}
}
@@ -114,6 +115,7 @@ mod from_compositor {
Self::CompositionEvent(..) => target_variant!("CompositionEvent"),
Self::IMEDismissedEvent => target_variant!("IMEDismissedEvent"),
Self::GamepadEvent(..) => target_variant!("GamepadEvent"),
+ Self::ClipboardEvent(..) => target_variant!("ClipboardEvent"),
}
}
}
@@ -215,6 +217,7 @@ mod from_script {
Self::WebViewBlurred => target_variant!("WebViewBlurred"),
Self::AllowUnload(..) => target_variant!("AllowUnload"),
Self::Keyboard(..) => target_variant!("Keyboard"),
+ Self::ClearClipboardContents => target_variant!("ClearClipboardContents"),
Self::GetClipboardContents(..) => target_variant!("GetClipboardContents"),
Self::SetClipboardContents(..) => target_variant!("SetClipboardContents"),
Self::SetCursor(..) => target_variant!("SetCursor"),
diff --git a/components/script/dom/clipboardevent.rs b/components/script/dom/clipboardevent.rs
new file mode 100644
index 00000000000..4791b73e7a9
--- /dev/null
+++ b/components/script/dom/clipboardevent.rs
@@ -0,0 +1,97 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use dom_struct::dom_struct;
+use js::rust::HandleObject;
+
+use crate::dom::bindings::codegen::Bindings::ClipboardEventBinding::{
+ ClipboardEventInit, ClipboardEventMethods,
+};
+use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
+use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
+use crate::dom::bindings::root::{DomRoot, MutNullableDom};
+use crate::dom::bindings::str::DOMString;
+use crate::dom::datatransfer::DataTransfer;
+use crate::dom::event::{Event, EventBubbles, EventCancelable};
+use crate::dom::window::Window;
+use crate::script_runtime::CanGc;
+
+#[dom_struct]
+pub struct ClipboardEvent {
+ event: Event,
+ clipboard_data: MutNullableDom<DataTransfer>,
+}
+
+impl ClipboardEvent {
+ fn new_inherited() -> ClipboardEvent {
+ ClipboardEvent {
+ event: Event::new_inherited(),
+ clipboard_data: MutNullableDom::new(None),
+ }
+ }
+
+ pub(crate) fn new(
+ window: &Window,
+ proto: Option<HandleObject>,
+ type_: DOMString,
+ can_bubble: EventBubbles,
+ cancelable: EventCancelable,
+ clipboard_data: Option<&DataTransfer>,
+ can_gc: CanGc,
+ ) -> DomRoot<ClipboardEvent> {
+ let ev = reflect_dom_object_with_proto(
+ Box::new(ClipboardEvent::new_inherited()),
+ window,
+ proto,
+ can_gc,
+ );
+ ev.upcast::<Event>()
+ .InitEvent(type_, bool::from(can_bubble), bool::from(cancelable));
+ ev.clipboard_data.set(clipboard_data);
+ ev
+ }
+
+ pub(crate) fn set_clipboard_data(&self, clipboard_data: Option<&DataTransfer>) {
+ self.clipboard_data.set(clipboard_data);
+ }
+
+ pub(crate) fn get_clipboard_data(&self) -> Option<DomRoot<DataTransfer>> {
+ self.clipboard_data.get()
+ }
+}
+
+impl ClipboardEventMethods<crate::DomTypeHolder> for ClipboardEvent {
+ /// <https://www.w3.org/TR/clipboard-apis/#dom-clipboardevent-clipboardevent>
+ fn Constructor(
+ window: &Window,
+ proto: Option<HandleObject>,
+ can_gc: CanGc,
+ type_: DOMString,
+ init: &ClipboardEventInit,
+ ) -> DomRoot<ClipboardEvent> {
+ // Missing composed field
+ let bubbles = EventBubbles::from(init.parent.bubbles);
+ let cancelable = EventCancelable::from(init.parent.cancelable);
+ ClipboardEvent::new(
+ window,
+ proto,
+ type_,
+ bubbles,
+ cancelable,
+ init.clipboardData.as_deref(),
+ can_gc,
+ )
+ }
+
+ /// <https://www.w3.org/TR/clipboard-apis/#dom-clipboardevent-clipboarddata>
+ fn GetClipboardData(&self) -> Option<DomRoot<DataTransfer>> {
+ self.clipboard_data.get()
+ }
+
+ /// <https://dom.spec.whatwg.org/#dom-event-istrusted>
+ fn IsTrusted(&self) -> bool {
+ self.event.IsTrusted()
+ }
+}
diff --git a/components/script/dom/datatransfer.rs b/components/script/dom/datatransfer.rs
index 001d036919b..6f2e857c214 100644
--- a/components/script/dom/datatransfer.rs
+++ b/components/script/dom/datatransfer.rs
@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use std::cell::RefCell;
+use std::cell::{Ref, RefCell};
use std::rc::Rc;
use dom_struct::dom_struct;
@@ -64,11 +64,8 @@ impl DataTransfer {
window: &Window,
proto: Option<HandleObject>,
can_gc: CanGc,
+ data_store: Rc<RefCell<Option<DragDataStore>>>,
) -> DomRoot<DataTransfer> {
- let mut drag_data_store = DragDataStore::new();
- drag_data_store.set_mode(Mode::ReadWrite);
-
- let data_store = Rc::new(RefCell::new(Some(drag_data_store)));
let item_list = DataTransferItemList::new(window, Rc::clone(&data_store));
reflect_dom_object_with_proto(
@@ -78,6 +75,18 @@ impl DataTransfer {
can_gc,
)
}
+
+ pub(crate) fn new(
+ window: &Window,
+ data_store: Rc<RefCell<Option<DragDataStore>>>,
+ can_gc: CanGc,
+ ) -> DomRoot<DataTransfer> {
+ Self::new_with_proto(window, None, can_gc, data_store)
+ }
+
+ pub(crate) fn data_store(&self) -> Option<Ref<DragDataStore>> {
+ Ref::filter_map(self.data_store.borrow(), |data_store| data_store.as_ref()).ok()
+ }
}
impl DataTransferMethods<crate::DomTypeHolder> for DataTransfer {
@@ -87,7 +96,12 @@ impl DataTransferMethods<crate::DomTypeHolder> for DataTransfer {
proto: Option<HandleObject>,
can_gc: CanGc,
) -> DomRoot<DataTransfer> {
- DataTransfer::new_with_proto(window, proto, can_gc)
+ let mut drag_data_store = DragDataStore::new();
+ drag_data_store.set_mode(Mode::ReadWrite);
+
+ let data_store = Rc::new(RefCell::new(Some(drag_data_store)));
+
+ DataTransfer::new_with_proto(window, proto, can_gc, data_store)
}
/// <https://html.spec.whatwg.org/multipage/#dom-datatransfer-dropeffect>
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 11a0c0935cd..b73bec01967 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -48,8 +48,9 @@ use profile_traits::ipc as profile_ipc;
use profile_traits::time::{TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType};
use script_layout_interface::{PendingRestyle, TrustedNodeAddress};
use script_traits::{
- AnimationState, AnimationTickType, CompositorEvent, DocumentActivity, MouseButton,
- MouseEventType, ScriptMsg, TouchEventType, TouchId, UntrustedNodeAddress, WheelDelta,
+ AnimationState, AnimationTickType, ClipboardEventType, CompositorEvent, DocumentActivity,
+ MouseButton, MouseEventType, ScriptMsg, TouchEventType, TouchId, UntrustedNodeAddress,
+ WheelDelta,
};
use servo_arc::Arc;
use servo_atoms::Atom;
@@ -111,11 +112,13 @@ use crate::dom::bindings::xmlname::{
namespace_from_domstring, validate_and_extract, xml_name_type,
};
use crate::dom::cdatasection::CDATASection;
+use crate::dom::clipboardevent::ClipboardEvent;
use crate::dom::comment::Comment;
use crate::dom::compositionevent::CompositionEvent;
use crate::dom::cssstylesheet::CSSStyleSheet;
use crate::dom::customelementregistry::CustomElementDefinition;
use crate::dom::customevent::CustomEvent;
+use crate::dom::datatransfer::DataTransfer;
use crate::dom::documentfragment::DocumentFragment;
use crate::dom::documentorshadowroot::{DocumentOrShadowRoot, StyleSheetInDocument};
use crate::dom::documenttype::DocumentType;
@@ -182,6 +185,7 @@ use crate::dom::wheelevent::WheelEvent;
use crate::dom::window::Window;
use crate::dom::windowproxy::WindowProxy;
use crate::dom::xpathevaluator::XPathEvaluator;
+use crate::drag_data_store::{DragDataStore, Kind, Mode, PlainString};
use crate::fetch::FetchCanceller;
use crate::iframe_collection::IFrameCollection;
use crate::messaging::{CommonScriptMsg, MainThreadScriptMsg};
@@ -1443,6 +1447,198 @@ impl Document {
event.fire(target, can_gc);
}
+ /// <https://www.w3.org/TR/clipboard-apis/#clipboard-actions>
+ pub(crate) fn handle_clipboard_action(
+ &self,
+ action: ClipboardEventType,
+ can_gc: CanGc,
+ ) -> bool {
+ // The script_triggered flag is set if the action runs because of a script, e.g. document.execCommand()
+ let script_triggered = false;
+
+ // The script_may_access_clipboard flag is set
+ // if action is paste and the script thread is allowed to read from clipboard or
+ // if action is copy or cut and the script thread is allowed to modify the clipboard
+ let script_may_access_clipboard = false;
+
+ // Step 1 If the script-triggered flag is set and the script-may-access-clipboard flag is unset
+ if script_triggered && !script_may_access_clipboard {
+ return false;
+ }
+
+ // Step 2 Fire a clipboard event
+ let event = ClipboardEvent::new(
+ &self.window,
+ None,
+ DOMString::from(action.as_str()),
+ EventBubbles::Bubbles,
+ EventCancelable::Cancelable,
+ None,
+ can_gc,
+ );
+ self.fire_clipboard_event(&event, action, can_gc);
+
+ // Step 3 If a script doesn't call preventDefault()
+ // the event will be handled inside target's VirtualMethods::handle_event
+
+ let e = event.upcast::<Event>();
+
+ if !e.IsTrusted() {
+ return false;
+ }
+
+ // Step 4 If the event was canceled, then
+ if e.DefaultPrevented() {
+ match e.Type().str() {
+ "copy" => {
+ // Step 4.1 Call the write content to the clipboard algorithm,
+ // passing on the DataTransferItemList items, a clear-was-called flag and a types-to-clear list.
+ if let Some(clipboard_data) = event.get_clipboard_data() {
+ let drag_data_store =
+ clipboard_data.data_store().expect("This shouldn't fail");
+ self.write_content_to_the_clipboard(&drag_data_store);
+ }
+ },
+ "cut" => {
+ // Step 4.1 Call the write content to the clipboard algorithm,
+ // passing on the DataTransferItemList items, a clear-was-called flag and a types-to-clear list.
+ if let Some(clipboard_data) = event.get_clipboard_data() {
+ let drag_data_store =
+ clipboard_data.data_store().expect("This shouldn't fail");
+ self.write_content_to_the_clipboard(&drag_data_store);
+ }
+
+ // Step 4.2 Fire a clipboard event named clipboardchange
+ self.fire_clipboardchange_event(can_gc);
+ },
+ "paste" => return false,
+ _ => (),
+ }
+ }
+ //Step 5
+ true
+ }
+
+ /// <https://www.w3.org/TR/clipboard-apis/#fire-a-clipboard-event>
+ fn fire_clipboard_event(
+ &self,
+ event: &ClipboardEvent,
+ action: ClipboardEventType,
+ can_gc: CanGc,
+ ) {
+ // Step 1 Let clear_was_called be false
+ // Step 2 Let types_to_clear an empty list
+ let mut drag_data_store = DragDataStore::new();
+
+ // Step 4 let clipboard-entry be the sequence number of clipboard content, null if the OS doesn't support it.
+
+ // Step 5 let trusted be true if the event is generated by the user agent, false otherwise
+ let trusted = true;
+
+ // Step 6 if the context is editable:
+ let focused = self.get_focused_element();
+ let body = self.GetBody();
+
+ let target = match (&focused, &body) {
+ (Some(focused), _) => focused.upcast(),
+ (&None, Some(body)) => body.upcast(),
+ (&None, &None) => self.window.upcast(),
+ };
+ // Step 6.2 else TODO require Selection see https://github.com/w3c/clipboard-apis/issues/70
+
+ // Step 7
+ match action {
+ ClipboardEventType::Copy | ClipboardEventType::Cut => {
+ // Step 7.2.1
+ drag_data_store.set_mode(Mode::ReadWrite);
+ },
+ ClipboardEventType::Paste(ref contents) => {
+ // Step 7.1.1
+ drag_data_store.set_mode(Mode::ReadOnly);
+ // Step 7.1.2 If trusted or the implementation gives script-generated events access to the clipboard
+ if trusted {
+ // Step 7.1.2.1 For each clipboard-part on the OS clipboard:
+
+ // Step 7.1.2.1.1 If clipboard-part contains plain text, then
+ let plain_string = PlainString::new(
+ DOMString::from_string(contents.to_string()),
+ DOMString::from("text/plain"),
+ );
+ let _ = drag_data_store.add(Kind::Text(plain_string));
+
+ // Step 7.1.2.1.2 TODO If clipboard-part represents file references, then for each file reference
+ // Step 7.1.2.1.3 TODO If clipboard-part contains HTML- or XHTML-formatted text then
+
+ // Step 7.1.3 Update clipboard-event-data’s files to match clipboard-event-data’s items
+ // Step 7.1.4 Update clipboard-event-data’s types to match clipboard-event-data’s items
+ }
+ },
+ ClipboardEventType::Change => (),
+ }
+
+ // Step 3
+ let clipboard_event_data = DataTransfer::new(
+ &self.window,
+ Rc::new(RefCell::new(Some(drag_data_store))),
+ can_gc,
+ );
+
+ // Step 8
+ event.set_clipboard_data(Some(&clipboard_event_data));
+ let event = event.upcast::<Event>();
+ // Step 9
+ event.set_trusted(trusted);
+ // Step 10 Set event’s composed to true.
+ // Step 11
+ event.dispatch(target, false, can_gc);
+ }
+
+ pub(crate) fn fire_clipboardchange_event(&self, can_gc: CanGc) {
+ let clipboardchange_event = ClipboardEvent::new(
+ &self.window,
+ None,
+ DOMString::from("clipboardchange"),
+ EventBubbles::Bubbles,
+ EventCancelable::Cancelable,
+ None,
+ can_gc,
+ );
+ self.fire_clipboard_event(&clipboardchange_event, ClipboardEventType::Change, can_gc);
+ }
+
+ /// <https://www.w3.org/TR/clipboard-apis/#write-content-to-the-clipboard>
+ fn write_content_to_the_clipboard(&self, drag_data_store: &DragDataStore) {
+ // Step 1
+ if drag_data_store.list_len() > 0 {
+ // Step 1.1 Clear the clipboard.
+ self.send_to_embedder(EmbedderMsg::ClearClipboardContents);
+ // Step 1.2
+ for item in drag_data_store.iter_item_list() {
+ match item {
+ Kind::Text(string) => {
+ // Step 1.2.1.1 Ensure encoding is correct per OS and locale conventions
+ // Step 1.2.1.2 Normalize line endings according to platform conventions
+ // Step 1.2.1.3
+ self.send_to_embedder(EmbedderMsg::SetClipboardContents(string.data()));
+ },
+ Kind::File(_) => {
+ // Step 1.2.2 If data is of a type listed in the mandatory data types list, then
+ // Step 1.2.2.1 Place part on clipboard with the appropriate OS clipboard format description
+ // Step 1.2.3 Else this is left to the implementation
+ },
+ }
+ }
+ } else {
+ // Step 2.1
+ if drag_data_store.clear_was_called {
+ // Step 2.1.1 If types-to-clear list is empty, clear the clipboard
+ self.send_to_embedder(EmbedderMsg::ClearClipboardContents);
+ // Step 2.1.2 Else remove the types in the list from the clipboard
+ // As of now this can't be done with Arboard, and it's possible that will be removed from the spec
+ }
+ }
+ }
+
#[allow(unsafe_code)]
pub(crate) unsafe fn handle_mouse_move_event(
&self,
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index 7c4a1a626cd..9de33dcaa72 100644
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -48,6 +48,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
use crate::dom::bindings::str::{DOMString, USVString};
+use crate::dom::clipboardevent::ClipboardEvent;
use crate::dom::compositionevent::CompositionEvent;
use crate::dom::document::Document;
use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers};
@@ -79,7 +80,10 @@ use crate::textinput::KeyReaction::{
DispatchInput, Nothing, RedrawSelection, TriggerDefaultAction,
};
use crate::textinput::Lines::Single;
-use crate::textinput::{Direction, SelectionDirection, TextInput, UTF16CodeUnits, UTF8Bytes};
+use crate::textinput::{
+ handle_text_clipboard_action, Direction, SelectionDirection, TextInput, UTF16CodeUnits,
+ UTF8Bytes,
+};
const DEFAULT_SUBMIT_VALUE: &str = "Submit";
const DEFAULT_RESET_VALUE: &str = "Reset";
@@ -2648,6 +2652,10 @@ impl VirtualMethods for HTMLInputElement {
}
event.mark_as_handled();
}
+ } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
+ if !event.DefaultPrevented() {
+ handle_text_clipboard_action(self, &self.textinput, clipboard_event, CanGc::note());
+ }
}
self.validity_state()
diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs
index 9ea70e03bd6..fe0c822aa48 100644
--- a/components/script/dom/htmltextareaelement.rs
+++ b/components/script/dom/htmltextareaelement.rs
@@ -23,6 +23,7 @@ use crate::dom::bindings::error::ErrorResult;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
use crate::dom::bindings::str::DOMString;
+use crate::dom::clipboardevent::ClipboardEvent;
use crate::dom::compositionevent::CompositionEvent;
use crate::dom::document::Document;
use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers};
@@ -42,7 +43,8 @@ use crate::dom::validitystate::{ValidationFlags, ValidityState};
use crate::dom::virtualmethods::VirtualMethods;
use crate::script_runtime::CanGc;
use crate::textinput::{
- Direction, KeyReaction, Lines, SelectionDirection, TextInput, UTF16CodeUnits, UTF8Bytes,
+ handle_text_clipboard_action, Direction, KeyReaction, Lines, SelectionDirection, TextInput,
+ UTF16CodeUnits, UTF8Bytes,
};
#[dom_struct]
@@ -675,6 +677,10 @@ impl VirtualMethods for HTMLTextAreaElement {
}
event.mark_as_handled();
}
+ } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
+ if !event.DefaultPrevented() {
+ handle_text_clipboard_action(self, &self.textinput, clipboard_event, CanGc::note());
+ }
}
self.validity_state()
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index 8f51e796320..cfada01bf18 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -254,6 +254,7 @@ pub(crate) mod channelmergernode;
pub(crate) mod channelsplitternode;
pub(crate) mod characterdata;
pub(crate) mod client;
+pub(crate) mod clipboardevent;
pub(crate) mod closeevent;
pub(crate) mod comment;
pub(crate) mod compositionevent;
diff --git a/components/script/dom/webidls/ClipboardEvent.webidl b/components/script/dom/webidls/ClipboardEvent.webidl
new file mode 100644
index 00000000000..d23700d8415
--- /dev/null
+++ b/components/script/dom/webidls/ClipboardEvent.webidl
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://w3c.github.io/clipboard-apis/
+
+[Exposed=Window, Pref="dom_clipboardevent_enabled"]
+interface ClipboardEvent : Event {
+ constructor (DOMString type, optional ClipboardEventInit eventInitDict = {});
+ readonly attribute DataTransfer? clipboardData;
+};
+
+dictionary ClipboardEventInit : EventInit {
+ DataTransfer? clipboardData = null;
+};
diff --git a/components/script/drag_data_store.rs b/components/script/drag_data_store.rs
index a4db339dff4..6073695de90 100644
--- a/components/script/drag_data_store.rs
+++ b/components/script/drag_data_store.rs
@@ -31,6 +31,10 @@ impl PlainString {
pub(crate) fn new(data: DOMString, type_: DOMString) -> Self {
Self { data, type_ }
}
+
+ pub fn data(&self) -> String {
+ self.data.to_string()
+ }
}
#[derive(Clone)]
@@ -99,7 +103,6 @@ pub(crate) enum Mode {
/// <https://html.spec.whatwg.org/multipage/#concept-dnd-rw>
ReadWrite,
/// <https://html.spec.whatwg.org/multipage/#concept-dnd-ro>
- #[allow(dead_code)] // TODO this used by ClipboardEvent.
ReadOnly,
/// <https://html.spec.whatwg.org/multipage/#concept-dnd-p>
Protected,
@@ -115,6 +118,7 @@ pub(crate) struct DragDataStore {
mode: Mode,
/// <https://html.spec.whatwg.org/multipage/#drag-data-store-allowed-effects-state>
allowed_effects_state: String,
+ pub clear_was_called: bool,
}
impl DragDataStore {
@@ -128,6 +132,7 @@ impl DragDataStore {
bitmap: None,
mode: Mode::Protected,
allowed_effects_state: String::from("uninitialized"),
+ clear_was_called: false,
}
}
@@ -255,6 +260,10 @@ impl DragDataStore {
self.item_list.len()
}
+ pub(crate) fn iter_item_list(&self) -> std::slice::Iter<'_, Kind> {
+ self.item_list.iter()
+ }
+
pub(crate) fn get_item(&self, index: usize) -> Option<Kind> {
self.item_list.get(index).cloned()
}
@@ -265,6 +274,7 @@ impl DragDataStore {
pub(crate) fn clear_list(&mut self) {
self.item_list.clear();
+ self.clear_was_called = true;
}
}
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 0cf14937c4c..08ce5fa7540 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -1141,6 +1141,10 @@ impl ScriptThread {
CompositorEvent::GamepadEvent(gamepad_event) => {
window.as_global_scope().handle_gamepad_event(gamepad_event);
},
+
+ CompositorEvent::ClipboardEvent(clipboard_action) => {
+ document.handle_clipboard_action(clipboard_action, can_gc);
+ },
}
}
ScriptThread::set_user_interacting(false);
diff --git a/components/script/textinput.rs b/components/script/textinput.rs
index 338303fc7b8..d59cd5b6a5d 100644
--- a/components/script/textinput.rs
+++ b/components/script/textinput.rs
@@ -10,12 +10,21 @@ use std::default::Default;
use std::ops::{Add, AddAssign, Range};
use keyboard_types::{Key, KeyState, Modifiers, ShortcutMatcher};
+use script_traits::ScriptToConstellationChan;
use unicode_segmentation::UnicodeSegmentation;
use crate::clipboard_provider::ClipboardProvider;
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
+use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::str::DOMString;
use crate::dom::compositionevent::CompositionEvent;
+use crate::dom::event::Event;
use crate::dom::keyboardevent::KeyboardEvent;
+use crate::dom::node::NodeTraits;
+use crate::dom::types::ClipboardEvent;
+use crate::drag_data_store::{DragDataStore, Kind};
+use crate::script_runtime::CanGc;
#[derive(Clone, Copy, PartialEq)]
pub enum Selection {
@@ -1128,4 +1137,88 @@ impl<T: ClipboardProvider> TextInput<T> {
.fold(UTF8Bytes::zero(), |acc, x| acc + x.len_utf8());
self.edit_point.index = byte_offset;
}
+
+ fn paste_contents(&mut self, drag_data_store: &DragDataStore) {
+ for item in drag_data_store.iter_item_list() {
+ if let Kind::Text(string) = item {
+ self.insert_string(string.data());
+ }
+ }
+ }
+}
+
+/// <https://www.w3.org/TR/clipboard-apis/#clipboard-actions> step 3
+pub(crate) fn handle_text_clipboard_action(
+ owning_node: &impl NodeTraits,
+ textinput: &DomRefCell<TextInput<ScriptToConstellationChan>>,
+ event: &ClipboardEvent,
+ can_gc: CanGc,
+) -> bool {
+ let e = event.upcast::<Event>();
+
+ if !e.IsTrusted() {
+ return false;
+ }
+
+ // Step 3
+ match e.Type().str() {
+ "copy" => {
+ let selection = textinput.borrow().get_selection_text();
+
+ // Step 3.1 Copy the selected contents, if any, to the clipboard
+ if let Some(text) = selection {
+ textinput
+ .borrow_mut()
+ .clipboard_provider
+ .set_clipboard_contents(text);
+ }
+
+ // Step 3.2 Fire a clipboard event named clipboardchange
+ owning_node
+ .owner_document()
+ .fire_clipboardchange_event(can_gc);
+ },
+ "cut" => {
+ let selection = textinput.borrow().get_selection_text();
+
+ // Step 3.1 If there is a selection in an editable context where cutting is enabled, then
+ if let Some(text) = selection {
+ // Step 3.1.1 Copy the selected contents, if any, to the clipboard
+ textinput
+ .borrow_mut()
+ .clipboard_provider
+ .set_clipboard_contents(text);
+
+ // Step 3.1.2 Remove the contents of the selection from the document and collapse the selection.
+ textinput.borrow_mut().delete_char(Direction::Backward);
+
+ // Step 3.1.3 Fire a clipboard event named clipboardchange
+ owning_node
+ .owner_document()
+ .fire_clipboardchange_event(can_gc);
+
+ // Step 3.1.4 Queue tasks to fire any events that should fire due to the modification.
+ } else {
+ // Step 3.2 Else, if there is no selection or the context is not editable, then
+ return false;
+ }
+ },
+ "paste" => {
+ // Step 3.1 If there is a selection or cursor in an editable context where pasting is enabled, then
+ if let Some(data) = event.get_clipboard_data() {
+ // Step 3.1.1 Insert the most suitable content found on the clipboard, if any, into the context.
+ let drag_data_store = data.data_store().expect("This shouldn't fail");
+ textinput.borrow_mut().paste_contents(&drag_data_store);
+
+ // Step 3.1.2 Queue tasks to fire any events that should fire due to the modification.
+ } else {
+ // Step 3.2 Else return false.
+ return false;
+ }
+ },
+ _ => (),
+ }
+
+ //Step 5
+ true
}
diff --git a/components/servo/lib.rs b/components/servo/lib.rs
index d9b2e560fba..a7215f372db 100644
--- a/components/servo/lib.rs
+++ b/components/servo/lib.rs
@@ -909,6 +909,9 @@ where
EmbedderEvent::Vsync => {
self.compositor.on_vsync();
},
+ EmbedderEvent::ClipboardAction(clipboard_event) => {
+ self.send_to_constellation(ConstellationMsg::Clipboard(clipboard_event));
+ },
}
false
}
diff --git a/components/shared/compositing/constellation_msg.rs b/components/shared/compositing/constellation_msg.rs
index 46d7cc5978f..8b641ecf46e 100644
--- a/components/shared/compositing/constellation_msg.rs
+++ b/components/shared/compositing/constellation_msg.rs
@@ -12,8 +12,9 @@ use embedder_traits::Cursor;
use ipc_channel::ipc::IpcSender;
use keyboard_types::{CompositionEvent, KeyboardEvent};
use script_traits::{
- AnimationTickType, CompositorEvent, GamepadEvent, LogEntry, MediaSessionActionType, Theme,
- TraversalDirection, WebDriverCommandMsg, WindowSizeData, WindowSizeType,
+ AnimationTickType, ClipboardEventType, CompositorEvent, GamepadEvent, LogEntry,
+ MediaSessionActionType, Theme, TraversalDirection, WebDriverCommandMsg, WindowSizeData,
+ WindowSizeType,
};
use servo_url::ServoUrl;
@@ -88,6 +89,8 @@ pub enum ConstellationMsg {
ReadyToPresent(Vec<WebViewId>),
/// Gamepad state has changed
Gamepad(GamepadEvent),
+ /// Inform the constellation of a clipboard event.
+ Clipboard(ClipboardEventType),
}
impl fmt::Debug for ConstellationMsg {
@@ -134,6 +137,7 @@ impl ConstellationMsg {
ClearCache => "ClearCache",
ReadyToPresent(..) => "ReadyToPresent",
Gamepad(..) => "Gamepad",
+ Clipboard(..) => "Clipboard",
}
}
}
diff --git a/components/shared/embedder/lib.rs b/components/shared/embedder/lib.rs
index 2548cad70f3..b03fa1035df 100644
--- a/components/shared/embedder/lib.rs
+++ b/components/shared/embedder/lib.rs
@@ -192,6 +192,8 @@ pub enum EmbedderMsg {
AllowUnload(IpcSender<bool>),
/// Sends an unconsumed key event back to the embedder.
Keyboard(KeyboardEvent),
+ /// Inform embedder to clear the clipboard
+ ClearClipboardContents,
/// Gets system clipboard contents
GetClipboardContents(IpcSender<String>),
/// Sets system clipboard contents
@@ -256,6 +258,7 @@ pub enum CompositorEventVariant {
CompositionEvent,
IMEDismissedEvent,
GamepadEvent,
+ ClipboardEvent,
}
impl Debug for EmbedderMsg {
@@ -269,6 +272,7 @@ impl Debug for EmbedderMsg {
EmbedderMsg::AllowUnload(..) => write!(f, "AllowUnload"),
EmbedderMsg::AllowNavigationRequest(..) => write!(f, "AllowNavigationRequest"),
EmbedderMsg::Keyboard(..) => write!(f, "Keyboard"),
+ EmbedderMsg::ClearClipboardContents => write!(f, "ClearClipboardContents"),
EmbedderMsg::GetClipboardContents(..) => write!(f, "GetClipboardContents"),
EmbedderMsg::SetClipboardContents(..) => write!(f, "SetClipboardContents"),
EmbedderMsg::SetCursor(..) => write!(f, "SetCursor"),
diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs
index e6901be54b6..60f7abba6d6 100644
--- a/components/shared/script/lib.rs
+++ b/components/shared/script/lib.rs
@@ -535,6 +535,31 @@ pub struct WheelDelta {
pub mode: WheelMode,
}
+/// The types of clipboard events
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub enum ClipboardEventType {
+ /// Contents of the system clipboard are changed
+ Change,
+ /// Copy
+ Copy,
+ /// Cut
+ Cut,
+ /// Paste
+ Paste(String),
+}
+
+impl ClipboardEventType {
+ /// Convert to event name
+ pub fn as_str(&self) -> &str {
+ match *self {
+ ClipboardEventType::Change => "clipboardchange",
+ ClipboardEventType::Copy => "copy",
+ ClipboardEventType::Cut => "cut",
+ ClipboardEventType::Paste(..) => "paste",
+ }
+ }
+}
+
/// Events from the compositor that the script thread needs to know about
#[derive(Debug, Deserialize, Serialize)]
pub enum CompositorEvent {
@@ -574,6 +599,8 @@ pub enum CompositorEvent {
IMEDismissedEvent,
/// Connected gamepad state updated
GamepadEvent(GamepadEvent),
+ /// A clipboard action was requested
+ ClipboardEvent(ClipboardEventType),
}
impl From<&CompositorEvent> for CompositorEventVariant {
@@ -588,6 +615,7 @@ impl From<&CompositorEvent> for CompositorEventVariant {
CompositorEvent::CompositionEvent(..) => CompositorEventVariant::CompositionEvent,
CompositorEvent::IMEDismissedEvent => CompositorEventVariant::IMEDismissedEvent,
CompositorEvent::GamepadEvent(..) => CompositorEventVariant::GamepadEvent,
+ CompositorEvent::ClipboardEvent(..) => CompositorEventVariant::ClipboardEvent,
}
}
}
diff --git a/ports/servoshell/desktop/tracing.rs b/ports/servoshell/desktop/tracing.rs
index 6e092318aa8..91538ae6772 100644
--- a/ports/servoshell/desktop/tracing.rs
+++ b/ports/servoshell/desktop/tracing.rs
@@ -156,6 +156,7 @@ mod from_servo {
Self::WebViewBlurred => target!("WebViewBlurred"),
Self::AllowUnload(..) => target!("AllowUnload"),
Self::Keyboard(..) => target!("Keyboard"),
+ Self::ClearClipboardContents => target!("ClearClipboardContents"),
Self::GetClipboardContents(..) => target!("GetClipboardContents"),
Self::SetClipboardContents(..) => target!("SetClipboardContents"),
Self::SetCursor(..) => target!("SetCursor"),
@@ -236,6 +237,7 @@ mod to_servo {
Self::ReplaceNativeSurface(..) => target!("ReplaceNativeSurface"),
Self::Gamepad(..) => target!("Gamepad"),
Self::Vsync => target!("Vsync"),
+ Self::ClipboardAction(..) => target!("ClipboardAction"),
}
}
}
diff --git a/ports/servoshell/desktop/webview.rs b/ports/servoshell/desktop/webview.rs
index 8a08ac2ec45..771e3f85e2a 100644
--- a/ports/servoshell/desktop/webview.rs
+++ b/ports/servoshell/desktop/webview.rs
@@ -27,8 +27,8 @@ use servo::embedder_traits::{
};
use servo::ipc_channel::ipc::IpcSender;
use servo::script_traits::{
- GamepadEvent, GamepadIndex, GamepadInputBounds, GamepadSupportedHapticEffects,
- GamepadUpdateType, TouchEventType, TraversalDirection,
+ ClipboardEventType, GamepadEvent, GamepadIndex, GamepadInputBounds,
+ GamepadSupportedHapticEffects, GamepadUpdateType, TouchEventType, TraversalDirection,
};
use servo::servo_url::ServoUrl;
use servo::webrender_api::units::DeviceRect;
@@ -483,6 +483,23 @@ where
Duration::from_secs(duration),
))
})
+ .shortcut(CMD_OR_CONTROL, 'X', || {
+ Some(EmbedderEvent::ClipboardAction(ClipboardEventType::Cut))
+ })
+ .shortcut(CMD_OR_CONTROL, 'C', || {
+ Some(EmbedderEvent::ClipboardAction(ClipboardEventType::Copy))
+ })
+ .shortcut(CMD_OR_CONTROL, 'V', || {
+ Some(EmbedderEvent::ClipboardAction(ClipboardEventType::Paste(
+ self.clipboard
+ .as_mut()
+ .and_then(|clipboard| clipboard.get_text().ok())
+ .unwrap_or_else(|| {
+ warn!("Error getting clipboard text. Returning empty string.");
+ String::new()
+ }),
+ )))
+ })
.shortcut(Modifiers::CONTROL, Key::F9, || {
Some(EmbedderEvent::CaptureWebRender)
})
@@ -850,6 +867,11 @@ where
EmbedderMsg::Keyboard(key_event) => {
self.handle_key_from_servo(webview_id, key_event);
},
+ EmbedderMsg::ClearClipboardContents => {
+ self.clipboard
+ .as_mut()
+ .and_then(|clipboard| clipboard.clear().ok());
+ },
EmbedderMsg::GetClipboardContents(sender) => {
let contents = self
.clipboard
diff --git a/ports/servoshell/egl/servo_glue.rs b/ports/servoshell/egl/servo_glue.rs
index ed3f4ae02a3..616a2b47321 100644
--- a/ports/servoshell/egl/servo_glue.rs
+++ b/ports/servoshell/egl/servo_glue.rs
@@ -644,7 +644,8 @@ impl ServoGlue {
EmbedderMsg::ReportProfile(..) |
EmbedderMsg::EventDelivered(..) |
EmbedderMsg::PlayGamepadHapticEffect(..) |
- EmbedderMsg::StopGamepadHapticEffect(..) => {},
+ EmbedderMsg::StopGamepadHapticEffect(..) |
+ EmbedderMsg::ClearClipboardContents => {},
}
}
diff --git a/resources/prefs.json b/resources/prefs.json
index 3ae80ab9da7..45d46759e57 100644
--- a/resources/prefs.json
+++ b/resources/prefs.json
@@ -7,6 +7,7 @@
"dom_bluetooth_testing_enabled": false,
"dom_canvas_capture_enabled": false,
"dom_canvas_text_enabled": true,
+ "dom_clipboardevent_enabled": true,
"dom_compositionevent_enabled": false,
"dom_crypto_subtle_enabled": true,
"dom_customelements_enabled": true,
diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json
index 2577bfabe3e..5a5063ea176 100644
--- a/tests/wpt/mozilla/meta/MANIFEST.json
+++ b/tests/wpt/mozilla/meta/MANIFEST.json
@@ -13499,7 +13499,7 @@
]
],
"interfaces.https.html": [
- "75b7d9bc3e68a1147cf2d86b2a7af2b14f45597a",
+ "71d2291cb162143e4abf298f0a23e6a06fe5c1bc",
[
null,
{}
diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html
index 75b7d9bc3e6..71d2291cb16 100644
--- a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html
+++ b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html
@@ -38,6 +38,7 @@ test_interfaces([
"ChannelMergerNode",
"ChannelSplitterNode",
"CharacterData",
+ "ClipboardEvent",
"CloseEvent",
"ConstantSourceNode",
"CryptoKey",