mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
async clipboard: implement readText
(#36689)
part of #36084 Testing: Only idl harness tests will pass for now --------- Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com>
This commit is contained in:
parent
348eede37b
commit
5b913441d4
5 changed files with 151 additions and 13 deletions
|
@ -3,24 +3,75 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use constellation_traits::BlobImpl;
|
use constellation_traits::BlobImpl;
|
||||||
|
use data_url::mime::Mime;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use embedder_traits::EmbedderMsg;
|
use embedder_traits::EmbedderMsg;
|
||||||
|
use js::rust::HandleValue as SafeHandleValue;
|
||||||
|
|
||||||
use crate::dom::bindings::codegen::Bindings::ClipboardBinding::{
|
use crate::dom::bindings::codegen::Bindings::ClipboardBinding::{
|
||||||
ClipboardMethods, PresentationStyle,
|
ClipboardMethods, PresentationStyle,
|
||||||
};
|
};
|
||||||
|
use crate::dom::bindings::error::Error;
|
||||||
use crate::dom::bindings::refcounted::TrustedPromise;
|
use crate::dom::bindings::refcounted::TrustedPromise;
|
||||||
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
|
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
|
||||||
use crate::dom::bindings::root::DomRoot;
|
use crate::dom::bindings::root::DomRoot;
|
||||||
use crate::dom::bindings::str::DOMString;
|
use crate::dom::bindings::str::DOMString;
|
||||||
use crate::dom::blob::Blob;
|
use crate::dom::blob::Blob;
|
||||||
|
use crate::dom::clipboarditem::Representation;
|
||||||
use crate::dom::eventtarget::EventTarget;
|
use crate::dom::eventtarget::EventTarget;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::promise::Promise;
|
use crate::dom::promise::Promise;
|
||||||
|
use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
|
||||||
use crate::dom::window::Window;
|
use crate::dom::window::Window;
|
||||||
use crate::script_runtime::CanGc;
|
use crate::realms::{InRealm, enter_realm};
|
||||||
|
use crate::routed_promise::{RoutedPromiseListener, route_promise};
|
||||||
|
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
||||||
|
|
||||||
|
/// The fulfillment handler for the reacting to representationDataPromise part of
|
||||||
|
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>.
|
||||||
|
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||||||
|
struct RepresentationDataPromiseFulfillmentHandler {
|
||||||
|
#[ignore_malloc_size_of = "Rc are hard"]
|
||||||
|
promise: Rc<Promise>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Callback for RepresentationDataPromiseFulfillmentHandler {
|
||||||
|
/// The fulfillment case of Step 3.4.1.1.4.3 of
|
||||||
|
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>.
|
||||||
|
fn callback(&self, cx: SafeJSContext, v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
|
||||||
|
// If v is a DOMString, then follow the below steps:
|
||||||
|
// Resolve p with v.
|
||||||
|
// Return p.
|
||||||
|
self.promise.resolve(cx, v, can_gc);
|
||||||
|
|
||||||
|
// NOTE: Since we ask text from arboard, v can't be a Blob
|
||||||
|
// If v is a Blob, then follow the below steps:
|
||||||
|
// Let string be the result of UTF-8 decoding v’s underlying byte sequence.
|
||||||
|
// Resolve p with string.
|
||||||
|
// Return p.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The rejection handler for the reacting to representationDataPromise part of
|
||||||
|
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>.
|
||||||
|
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||||||
|
struct RepresentationDataPromiseRejectionHandler {
|
||||||
|
#[ignore_malloc_size_of = "Rc are hard"]
|
||||||
|
promise: Rc<Promise>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Callback for RepresentationDataPromiseRejectionHandler {
|
||||||
|
/// The rejection case of Step 3.4.1.1.4.3 of
|
||||||
|
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>.
|
||||||
|
fn callback(&self, _cx: SafeJSContext, _v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
|
||||||
|
// Reject p with "NotFoundError" DOMException in realm.
|
||||||
|
// Return p.
|
||||||
|
self.promise.reject_error(Error::NotFound, can_gc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub(crate) struct Clipboard {
|
pub(crate) struct Clipboard {
|
||||||
|
@ -40,6 +91,34 @@ impl Clipboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClipboardMethods<crate::DomTypeHolder> for Clipboard {
|
impl ClipboardMethods<crate::DomTypeHolder> for Clipboard {
|
||||||
|
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>
|
||||||
|
fn ReadText(&self, can_gc: CanGc) -> Rc<Promise> {
|
||||||
|
// Step 1 Let realm be this's relevant realm.
|
||||||
|
let global = self.global();
|
||||||
|
|
||||||
|
// Step 2 Let p be a new promise in realm.
|
||||||
|
let p = Promise::new(&global, can_gc);
|
||||||
|
|
||||||
|
// Step 3 Run the following steps in parallel:
|
||||||
|
|
||||||
|
// TODO Step 3.1 Let r be the result of running check clipboard read permission.
|
||||||
|
// Step 3.2 If r is false, then:
|
||||||
|
// Step 3.2.1 Queue a global task on the permission task source, given realm’s global object,
|
||||||
|
// to reject p with "NotAllowedError" DOMException in realm.
|
||||||
|
// Step 3.2.2 Abort these steps.
|
||||||
|
|
||||||
|
// Step 3.3 Let data be a copy of the system clipboard data.
|
||||||
|
let window = global.as_window();
|
||||||
|
let sender = route_promise(&p, self, global.task_manager().clipboard_task_source());
|
||||||
|
window.send_to_embedder(EmbedderMsg::GetClipboardText(window.webview_id(), sender));
|
||||||
|
|
||||||
|
// Step 3.4 Queue a global task on the clipboard task source,
|
||||||
|
// given realm’s global object, to perform the below steps:
|
||||||
|
// NOTE: We queue the task inside route_promise and perform the steps inside handle_response
|
||||||
|
|
||||||
|
p
|
||||||
|
}
|
||||||
|
|
||||||
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-writetext>
|
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-writetext>
|
||||||
fn WriteText(&self, data: DOMString, can_gc: CanGc) -> Rc<Promise> {
|
fn WriteText(&self, data: DOMString, can_gc: CanGc) -> Rc<Promise> {
|
||||||
// Step 1 Let realm be this's relevant realm.
|
// Step 1 Let realm be this's relevant realm.
|
||||||
|
@ -95,6 +174,71 @@ impl ClipboardMethods<crate::DomTypeHolder> for Clipboard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RoutedPromiseListener<Result<String, String>> for Clipboard {
|
||||||
|
fn handle_response(
|
||||||
|
&self,
|
||||||
|
response: Result<String, String>,
|
||||||
|
promise: &Rc<Promise>,
|
||||||
|
can_gc: CanGc,
|
||||||
|
) {
|
||||||
|
let global = self.global();
|
||||||
|
let text = response.unwrap_or_default();
|
||||||
|
|
||||||
|
// Step 3.4.1 For each systemClipboardItem in data:
|
||||||
|
// Step 3.4.1.1 For each systemClipboardRepresentation in systemClipboardItem:
|
||||||
|
// TODO: Arboard provide the first item that has a String representation
|
||||||
|
|
||||||
|
// Step 3.4.1.1.1 Let mimeType be the result of running the
|
||||||
|
// well-known mime type from os specific format algorithm given systemClipboardRepresentation’s name.
|
||||||
|
// Note: This is done by arboard, so we just convert the format to a MIME
|
||||||
|
let mime_type = Mime::from_str("text/plain").unwrap();
|
||||||
|
|
||||||
|
// Step 3.4.1.1.2 If mimeType is null, continue this loop.
|
||||||
|
// Note: Since the previous step is infallible, we don't need to handle this case
|
||||||
|
|
||||||
|
// Step 3.4.1.1.3 Let representation be a new representation.
|
||||||
|
let representation = Representation {
|
||||||
|
mime_type,
|
||||||
|
is_custom: false,
|
||||||
|
data: Promise::new_resolved(
|
||||||
|
&global,
|
||||||
|
GlobalScope::get_cx(),
|
||||||
|
DOMString::from(text),
|
||||||
|
can_gc,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 3.4.1.1.4 If representation’s MIME type essence is "text/plain", then:
|
||||||
|
|
||||||
|
// Step 3.4.1.1.4.1 Set representation’s MIME type to mimeType.
|
||||||
|
// Note: Done when creating a new representation
|
||||||
|
|
||||||
|
// Step 3.4.1.1.4.2 Let representationDataPromise be the representation’s data.
|
||||||
|
// Step 3.4.1.1.4.3 React to representationDataPromise:
|
||||||
|
let fulfillment_handler = Box::new(RepresentationDataPromiseFulfillmentHandler {
|
||||||
|
promise: promise.clone(),
|
||||||
|
});
|
||||||
|
let rejection_handler = Box::new(RepresentationDataPromiseRejectionHandler {
|
||||||
|
promise: promise.clone(),
|
||||||
|
});
|
||||||
|
let handler = PromiseNativeHandler::new(
|
||||||
|
&global,
|
||||||
|
Some(fulfillment_handler),
|
||||||
|
Some(rejection_handler),
|
||||||
|
can_gc,
|
||||||
|
);
|
||||||
|
let realm = enter_realm(&*global);
|
||||||
|
let comp = InRealm::Entered(&realm);
|
||||||
|
representation
|
||||||
|
.data
|
||||||
|
.append_native_handler(&handler, comp, can_gc);
|
||||||
|
|
||||||
|
// Step 3.4.2 Reject p with "NotFoundError" DOMException in realm.
|
||||||
|
// Step 3.4.3 Return p.
|
||||||
|
// NOTE: We follow the same behaviour of Gecko by doing nothing if no text is available instead of rejecting p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <https://w3c.github.io/clipboard-apis/#write-blobs-and-option-to-the-clipboard>
|
/// <https://w3c.github.io/clipboard-apis/#write-blobs-and-option-to-the-clipboard>
|
||||||
fn write_blobs_and_option_to_the_clipboard(
|
fn write_blobs_and_option_to_the_clipboard(
|
||||||
window: &Window,
|
window: &Window,
|
||||||
|
|
|
@ -29,13 +29,13 @@ const CUSTOM_FORMAT_PREFIX: &str = "web ";
|
||||||
|
|
||||||
/// <https://w3c.github.io/clipboard-apis/#representation>
|
/// <https://w3c.github.io/clipboard-apis/#representation>
|
||||||
#[derive(JSTraceable, MallocSizeOf)]
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
struct Representation {
|
pub(super) struct Representation {
|
||||||
#[no_trace]
|
#[no_trace]
|
||||||
#[ignore_malloc_size_of = "Extern type"]
|
#[ignore_malloc_size_of = "Extern type"]
|
||||||
mime_type: Mime,
|
pub mime_type: Mime,
|
||||||
is_custom: bool,
|
pub is_custom: bool,
|
||||||
#[ignore_malloc_size_of = "Rc is hard"]
|
#[ignore_malloc_size_of = "Rc is hard"]
|
||||||
data: Rc<Promise>,
|
pub data: Rc<Promise>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
|
|
|
@ -88,7 +88,7 @@ DOMInterfaces = {
|
||||||
},
|
},
|
||||||
|
|
||||||
'Clipboard': {
|
'Clipboard': {
|
||||||
'canGc': ['WriteText']
|
'canGc': ['ReadText', 'WriteText']
|
||||||
},
|
},
|
||||||
|
|
||||||
'ClipboardItem': {
|
'ClipboardItem': {
|
||||||
|
|
|
@ -9,7 +9,7 @@ typedef sequence<ClipboardItem> ClipboardItems;
|
||||||
[SecureContext, Exposed=Window, Pref="dom_async_clipboard_enabled"]
|
[SecureContext, Exposed=Window, Pref="dom_async_clipboard_enabled"]
|
||||||
interface Clipboard : EventTarget {
|
interface Clipboard : EventTarget {
|
||||||
// Promise<ClipboardItems> read();
|
// Promise<ClipboardItems> read();
|
||||||
// Promise<DOMString> readText();
|
Promise<DOMString> readText();
|
||||||
// Promise<undefined> write(ClipboardItems data);
|
// Promise<undefined> write(ClipboardItems data);
|
||||||
Promise<undefined> writeText(DOMString data);
|
Promise<undefined> writeText(DOMString data);
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,9 +8,6 @@
|
||||||
[Clipboard interface: operation read(optional ClipboardUnsanitizedFormats)]
|
[Clipboard interface: operation read(optional ClipboardUnsanitizedFormats)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Clipboard interface: operation readText()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Clipboard interface: operation write(ClipboardItems)]
|
[Clipboard interface: operation write(ClipboardItems)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -20,9 +17,6 @@
|
||||||
[Clipboard interface: calling read(optional ClipboardUnsanitizedFormats) on navigator.clipboard with too few arguments must throw TypeError]
|
[Clipboard interface: calling read(optional ClipboardUnsanitizedFormats) on navigator.clipboard with too few arguments must throw TypeError]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Clipboard interface: navigator.clipboard must inherit property "readText()" with the proper type]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Clipboard interface: navigator.clipboard must inherit property "write(ClipboardItems)" with the proper type]
|
[Clipboard interface: navigator.clipboard must inherit property "write(ClipboardItems)" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue