Skip to content

Commit d1243a1

Browse files
authored
dom: Implement ClipboardItem (servo#36336)
implement the `ClipboardItem` interface Testing: covered by existing wpt tests part of servo#36084 --------- Signed-off-by: Gae24 <[email protected]>
1 parent 33b00db commit d1243a1

File tree

12 files changed

+219
-38
lines changed

12 files changed

+219
-38
lines changed

components/config/prefs.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ pub struct Preferences {
6969
/// List of comma-separated backends to be used by wgpu.
7070
pub dom_webgpu_wgpu_backend: String,
7171
pub dom_abort_controller_enabled: bool,
72+
pub dom_async_clipboard_enabled: bool,
7273
pub dom_bluetooth_enabled: bool,
7374
pub dom_bluetooth_testing_enabled: bool,
7475
pub dom_allow_scripts_to_close_windows: bool,
@@ -246,6 +247,7 @@ impl Preferences {
246247
devtools_server_port: 0,
247248
dom_abort_controller_enabled: false,
248249
dom_allow_scripts_to_close_windows: false,
250+
dom_async_clipboard_enabled: false,
249251
dom_bluetooth_enabled: false,
250252
dom_bluetooth_testing_enabled: false,
251253
dom_canvas_capture_enabled: false,
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4+
5+
use std::ops::Deref;
6+
use std::rc::Rc;
7+
use std::str::FromStr;
8+
9+
use data_url::mime::Mime;
10+
use dom_struct::dom_struct;
11+
use js::rust::{HandleObject, MutableHandleValue};
12+
use script_bindings::record::Record;
13+
14+
use crate::dom::bindings::cell::DomRefCell;
15+
use crate::dom::bindings::codegen::Bindings::ClipboardBinding::{
16+
ClipboardItemMethods, ClipboardItemOptions, PresentationStyle,
17+
};
18+
use crate::dom::bindings::error::{Error, Fallible};
19+
use crate::dom::bindings::frozenarray::CachedFrozenArray;
20+
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
21+
use crate::dom::bindings::root::DomRoot;
22+
use crate::dom::bindings::str::DOMString;
23+
use crate::dom::promise::Promise;
24+
use crate::dom::window::Window;
25+
use crate::script_runtime::{CanGc, JSContext};
26+
27+
/// <https://w3c.github.io/clipboard-apis/#web-custom-format>
28+
const CUSTOM_FORMAT_PREFIX: &str = "web ";
29+
30+
/// <https://w3c.github.io/clipboard-apis/#representation>
31+
#[derive(JSTraceable, MallocSizeOf)]
32+
struct Representation {
33+
#[no_trace]
34+
#[ignore_malloc_size_of = "Extern type"]
35+
mime_type: Mime,
36+
is_custom: bool,
37+
#[ignore_malloc_size_of = "Rc is hard"]
38+
data: Rc<Promise>,
39+
}
40+
41+
#[dom_struct]
42+
pub(crate) struct ClipboardItem {
43+
reflector_: Reflector,
44+
representations: DomRefCell<Vec<Representation>>,
45+
presentation_style: DomRefCell<PresentationStyle>,
46+
#[ignore_malloc_size_of = "mozjs"]
47+
frozen_types: CachedFrozenArray,
48+
}
49+
50+
impl ClipboardItem {
51+
fn new_inherited() -> ClipboardItem {
52+
ClipboardItem {
53+
reflector_: Reflector::new(),
54+
representations: Default::default(),
55+
presentation_style: Default::default(),
56+
frozen_types: CachedFrozenArray::new(),
57+
}
58+
}
59+
60+
fn new(window: &Window, proto: Option<HandleObject>, can_gc: CanGc) -> DomRoot<ClipboardItem> {
61+
reflect_dom_object_with_proto(
62+
Box::new(ClipboardItem::new_inherited()),
63+
window,
64+
proto,
65+
can_gc,
66+
)
67+
}
68+
}
69+
70+
impl ClipboardItemMethods<crate::DomTypeHolder> for ClipboardItem {
71+
/// <https://w3c.github.io/clipboard-apis/#dom-clipboarditem-clipboarditem>
72+
fn Constructor(
73+
global: &Window,
74+
proto: Option<HandleObject>,
75+
can_gc: CanGc,
76+
items: Record<DOMString, Rc<Promise>>,
77+
options: &ClipboardItemOptions,
78+
) -> Fallible<DomRoot<ClipboardItem>> {
79+
// Step 1 If items is empty, then throw a TypeError.
80+
if items.is_empty() {
81+
return Err(Error::Type(String::from("No item provided")));
82+
}
83+
84+
// Step 2 If options is empty, then set options["presentationStyle"] = "unspecified".
85+
// NOTE: This is done inside bindings
86+
87+
// Step 3 Set this's clipboard item to a new clipboard item.
88+
let clipboard_item = ClipboardItem::new(global, proto, can_gc);
89+
90+
// Step 4 Set this's clipboard item's presentation style to options["presentationStyle"].
91+
*clipboard_item.presentation_style.borrow_mut() = options.presentationStyle;
92+
93+
// Step 6 For each (key, value) in items:
94+
for (key, value) in items.deref() {
95+
// Step 6.2 Let isCustom be false.
96+
97+
// Step 6.3 If key starts with `"web "` prefix, then
98+
// Step 6.3.1 Remove `"web "` prefix and assign the remaining string to key.
99+
let (key, is_custom) = match key.strip_prefix(CUSTOM_FORMAT_PREFIX) {
100+
None => (key.str(), false),
101+
// Step 6.3.2 Set isCustom true
102+
Some(stripped) => (stripped, true),
103+
};
104+
105+
// Step 6.5 Let mimeType be the result of parsing a MIME type given key.
106+
// Step 6.6 If mimeType is failure, then throw a TypeError.
107+
let mime_type =
108+
Mime::from_str(key).map_err(|_| Error::Type(String::from("Invalid mime type")))?;
109+
110+
// Step 6.7 If this's clipboard item's list of representations contains a representation
111+
// whose MIME type is mimeType and whose [representation/isCustom] is isCustom, then throw a TypeError.
112+
if clipboard_item
113+
.representations
114+
.borrow()
115+
.iter()
116+
.any(|representation| {
117+
representation.mime_type == mime_type && representation.is_custom == is_custom
118+
})
119+
{
120+
return Err(Error::Type(String::from("Tried to add a duplicate mime")));
121+
}
122+
123+
// Step 6.1 Let representation be a new representation.
124+
// Step 6.4 Set representation’s isCustom flag to isCustom.
125+
// Step 6.8 Set representation’s MIME type to mimeType.
126+
// Step 6.9 Set representation’s data to value.
127+
let representation = Representation {
128+
mime_type,
129+
is_custom,
130+
data: value.clone(),
131+
};
132+
133+
// Step 6.10 Append representation to this's clipboard item's list of representations.
134+
clipboard_item
135+
.representations
136+
.borrow_mut()
137+
.push(representation);
138+
}
139+
140+
// NOTE: The steps for creating a frozen array from the list of mimeType are done in the Types() method
141+
142+
Ok(clipboard_item)
143+
}
144+
145+
/// <https://w3c.github.io/clipboard-apis/#dom-clipboarditem-presentationstyle>
146+
fn PresentationStyle(&self) -> PresentationStyle {
147+
*self.presentation_style.borrow()
148+
}
149+
150+
/// <https://w3c.github.io/clipboard-apis/#dom-clipboarditem-types>
151+
fn Types(&self, cx: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
152+
self.frozen_types.get_or_init(
153+
|| {
154+
// Step 5 Let types be a list of DOMString.
155+
let mut types = Vec::new();
156+
157+
self.representations
158+
.borrow()
159+
.iter()
160+
.for_each(|representation| {
161+
// Step 6.11 Let mimeTypeString be the result of serializing a MIME type with mimeType.
162+
let mime_type_string = representation.mime_type.to_string();
163+
164+
// Step 6.12 If isCustom is true, prefix mimeTypeString with `"web "`.
165+
let mime_type_string = if representation.is_custom {
166+
format!("{}{}", CUSTOM_FORMAT_PREFIX, mime_type_string)
167+
} else {
168+
mime_type_string
169+
};
170+
171+
// Step 6.13 Add mimeTypeString to types.
172+
types.push(DOMString::from(mime_type_string));
173+
});
174+
types
175+
},
176+
cx,
177+
retval,
178+
can_gc,
179+
);
180+
}
181+
}

components/script/dom/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ pub(crate) mod channelsplitternode;
251251
pub(crate) mod characterdata;
252252
pub(crate) mod client;
253253
pub(crate) mod clipboardevent;
254+
pub(crate) mod clipboarditem;
254255
pub(crate) mod closeevent;
255256
pub(crate) mod comment;
256257
pub(crate) mod compositionevent;

components/script_bindings/codegen/Bindings.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ DOMInterfaces = {
8787
'canGc': ['Before', 'After', 'Remove', 'ReplaceWith']
8888
},
8989

90+
'ClipboardItem': {
91+
'canGc': ['Types']
92+
},
93+
9094
'CountQueuingStrategy': {
9195
'canGc': ['GetSize'],
9296
},
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
// https://w3c.github.io/clipboard-apis/#clipboard-item-interface
6+
7+
typedef Promise<(DOMString or Blob)> ClipboardItemData;
8+
9+
[SecureContext, Exposed=Window, Pref="dom_async_clipboard_enabled"]
10+
interface ClipboardItem {
11+
[Throws] constructor(record<DOMString, ClipboardItemData> items,
12+
optional ClipboardItemOptions options = {});
13+
14+
readonly attribute PresentationStyle presentationStyle;
15+
readonly attribute /* FrozenArray<DOMString> */ any types;
16+
17+
// Promise<Blob> getType(DOMString type);
18+
19+
// static boolean supports(DOMString type);
20+
};
21+
22+
enum PresentationStyle { "unspecified", "inline", "attachment" };
23+
24+
dictionary ClipboardItemOptions {
25+
PresentationStyle presentationStyle = "unspecified";
26+
};

ports/servoshell/prefs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ pub(crate) fn parse_command_line_arguments(args: Vec<String>) -> ArgumentParsing
548548

549549
if opt_match.opt_present("enable-experimental-web-platform-features") {
550550
vec![
551+
"dom_async_clipboard_enabled",
551552
"dom_fontface_enabled",
552553
"dom_imagebitmap_enabled",
553554
"dom_intersection_observer_enabled",

tests/wpt/meta/__dir__.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
prefs: [
2+
"dom_async_clipboard_enabled:true",
23
"dom_fontface_enabled:true",
34
"dom_imagebitmap_enabled:true",
45
"dom_intersection_observer_enabled:true",

tests/wpt/meta/clipboard-apis/clipboard-item.https.html.ini

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,6 @@
55
[ClipboardItem() succeeds with empty options]
66
expected: FAIL
77

8-
[ClipboardItem({}) fails with empty dictionary input]
9-
expected: FAIL
10-
11-
[ClipboardItem(Blob) fails]
12-
expected: FAIL
13-
14-
[ClipboardItem() fails with null input]
15-
expected: FAIL
16-
17-
[ClipboardItem() fails with no input]
18-
expected: FAIL
19-
208
[types() returns correct values]
219
expected: FAIL
2210

tests/wpt/meta/clipboard-apis/idlharness.https.window.js.ini

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,4 @@
11
[idlharness.https.window.html]
2-
[ClipboardItem interface: existence and properties of interface object]
3-
expected: FAIL
4-
5-
[ClipboardItem interface object length]
6-
expected: FAIL
7-
8-
[ClipboardItem interface object name]
9-
expected: FAIL
10-
11-
[ClipboardItem interface: existence and properties of interface prototype object]
12-
expected: FAIL
13-
14-
[ClipboardItem interface: existence and properties of interface prototype object's "constructor" property]
15-
expected: FAIL
16-
17-
[ClipboardItem interface: existence and properties of interface prototype object's @@unscopables property]
18-
expected: FAIL
19-
20-
[ClipboardItem interface: attribute presentationStyle]
21-
expected: FAIL
22-
23-
[ClipboardItem interface: attribute types]
24-
expected: FAIL
25-
262
[ClipboardItem interface: operation getType(DOMString)]
273
expected: FAIL
284

tests/wpt/mozilla/meta/MANIFEST.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13503,7 +13503,7 @@
1350313503
]
1350413504
],
1350513505
"interfaces.https.html": [
13506-
"dce05a55fd33326768635c6b3cdb193d526fccdd",
13506+
"cc6e99d8da7cd82edefd1c0b0fce984ae85462de",
1350713507
[
1350813508
null,
1350913509
{}

0 commit comments

Comments
 (0)