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:
Gae24 2025-01-15 20:45:29 +01:00 committed by GitHub
parent cd9e831e91
commit d470f219b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 581 additions and 26 deletions

View 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()
}
}

View file

@ -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>

View file

@ -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-datas files to match clipboard-event-datas items
// Step 7.1.4 Update clipboard-event-datas types to match clipboard-event-datas 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 events 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,

View file

@ -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()

View file

@ -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()

View file

@ -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;

View 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;
};

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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
}