mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
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>
This commit is contained in:
parent
cd9e831e91
commit
d470f219b1
24 changed files with 581 additions and 26 deletions
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4267,6 +4270,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")
|
||||
|
|
|
@ -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"),
|
||||
|
|
97
components/script/dom/clipboardevent.rs
Normal file
97
components/script/dom/clipboardevent.rs
Normal file
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
15
components/script/dom/webidls/ClipboardEvent.webidl
Normal file
15
components/script/dom/webidls/ClipboardEvent.webidl
Normal file
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -909,6 +909,9 @@ where
|
|||
EmbedderEvent::Vsync => {
|
||||
self.compositor.on_vsync();
|
||||
},
|
||||
EmbedderEvent::ClipboardAction(clipboard_event) => {
|
||||
self.send_to_constellation(ConstellationMsg::Clipboard(clipboard_event));
|
||||
},
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -644,7 +644,8 @@ impl ServoGlue {
|
|||
EmbedderMsg::ReportProfile(..) |
|
||||
EmbedderMsg::EventDelivered(..) |
|
||||
EmbedderMsg::PlayGamepadHapticEffect(..) |
|
||||
EmbedderMsg::StopGamepadHapticEffect(..) => {},
|
||||
EmbedderMsg::StopGamepadHapticEffect(..) |
|
||||
EmbedderMsg::ClearClipboardContents => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
2
tests/wpt/mozilla/meta/MANIFEST.json
vendored
2
tests/wpt/mozilla/meta/MANIFEST.json
vendored
|
@ -13499,7 +13499,7 @@
|
|||
]
|
||||
],
|
||||
"interfaces.https.html": [
|
||||
"75b7d9bc3e68a1147cf2d86b2a7af2b14f45597a",
|
||||
"71d2291cb162143e4abf298f0a23e6a06fe5c1bc",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
|
|
|
@ -38,6 +38,7 @@ test_interfaces([
|
|||
"ChannelMergerNode",
|
||||
"ChannelSplitterNode",
|
||||
"CharacterData",
|
||||
"ClipboardEvent",
|
||||
"CloseEvent",
|
||||
"ConstantSourceNode",
|
||||
"CryptoKey",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue