script/dom/
clipboarditem.rs1use std::ops::Deref;
6use std::rc::Rc;
7use std::str::FromStr;
8
9use data_url::mime::Mime;
10use dom_struct::dom_struct;
11use js::rust::{HandleObject, MutableHandleValue};
12use script_bindings::record::Record;
13
14use crate::dom::bindings::cell::DomRefCell;
15use crate::dom::bindings::codegen::Bindings::ClipboardBinding::{
16 ClipboardItemMethods, ClipboardItemOptions, PresentationStyle,
17};
18use crate::dom::bindings::error::{Error, Fallible};
19use crate::dom::bindings::frozenarray::CachedFrozenArray;
20use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::bindings::str::DOMString;
23use crate::dom::promise::Promise;
24use crate::dom::window::Window;
25use crate::script_runtime::{CanGc, JSContext};
26
27const CUSTOM_FORMAT_PREFIX: &str = "web ";
29
30#[derive(JSTraceable, MallocSizeOf)]
32pub(super) struct Representation {
33 #[no_trace]
34 #[ignore_malloc_size_of = "Extern type"]
35 pub mime_type: Mime,
36 pub is_custom: bool,
37 #[ignore_malloc_size_of = "Rc is hard"]
38 pub data: Rc<Promise>,
39}
40
41#[dom_struct]
42pub(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
50impl 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
70impl ClipboardItemMethods<crate::DomTypeHolder> for ClipboardItem {
71 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 if items.is_empty() {
81 return Err(Error::Type(String::from("No item provided")));
82 }
83
84 let clipboard_item = ClipboardItem::new(global, proto, can_gc);
89
90 *clipboard_item.presentation_style.borrow_mut() = options.presentationStyle;
92
93 for (key, value) in items.deref() {
95 let (key, is_custom) = match key.strip_prefix(CUSTOM_FORMAT_PREFIX) {
100 None => (key.str(), false),
101 Some(stripped) => (stripped, true),
103 };
104
105 let mime_type =
108 Mime::from_str(key).map_err(|_| Error::Type(String::from("Invalid mime type")))?;
109
110 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 let representation = Representation {
128 mime_type,
129 is_custom,
130 data: value.clone(),
131 };
132
133 clipboard_item
135 .representations
136 .borrow_mut()
137 .push(representation);
138 }
139
140 Ok(clipboard_item)
143 }
144
145 fn PresentationStyle(&self) -> PresentationStyle {
147 *self.presentation_style.borrow()
148 }
149
150 fn Types(&self, cx: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
152 self.frozen_types.get_or_init(
153 || {
154 let mut types = Vec::new();
156
157 self.representations
158 .borrow()
159 .iter()
160 .for_each(|representation| {
161 let mime_type_string = representation.mime_type.to_string();
163
164 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 types.push(DOMString::from(mime_type_string));
173 });
174 types
175 },
176 cx,
177 retval,
178 can_gc,
179 );
180 }
181}