servo/
clipboard_delegate.rs1use servo_base::generic_channel::GenericCallback;
6
7use crate::WebView;
8
9pub struct StringRequest {
10 pub(crate) result_sender: GenericCallback<Result<String, String>>,
11 response_sent: bool,
12}
13
14impl StringRequest {
15 pub fn success(mut self, string: String) {
16 let _ = self.result_sender.send(Ok(string));
17 self.response_sent = true;
18 }
19
20 pub fn failure(mut self, message: String) {
21 let _ = self.result_sender.send(Err(message));
22 self.response_sent = true;
23 }
24}
25
26impl From<GenericCallback<Result<String, String>>> for StringRequest {
27 fn from(result_sender: GenericCallback<Result<String, String>>) -> Self {
28 Self {
29 result_sender,
30 response_sent: false,
31 }
32 }
33}
34
35impl Drop for StringRequest {
36 fn drop(&mut self) {
37 if !self.response_sent {
38 let _ = self
39 .result_sender
40 .send(Err("No response sent to request.".into()));
41 }
42 }
43}
44
45pub trait ClipboardDelegate {
50 fn clear(&self, _webview: WebView) {}
52
53 fn get_text(&self, _webview: WebView, _request: StringRequest) {}
57
58 fn set_text(&self, _webview: WebView, _new_contents: String) {}
60}
61
62pub(crate) struct DefaultClipboardDelegate;
63
64impl ClipboardDelegate for DefaultClipboardDelegate {
65 fn clear(&self, _webview: WebView) {
66 clipboard::clear();
67 }
68
69 fn get_text(&self, _webview: WebView, request: StringRequest) {
70 clipboard::get_text(request);
71 }
72
73 fn set_text(&self, _webview: WebView, new_contents: String) {
74 clipboard::set_text(new_contents);
75 }
76}
77
78mod fallback_clipboard {
79 use std::sync::{LockResult, Mutex, OnceLock};
80
81 use crate::clipboard_delegate::StringRequest;
82
83 static SHARED_FALLBACK_CLIPBOARD: OnceLock<Mutex<String>> = OnceLock::new();
86
87 fn with_shared_clipboard(callback: impl FnOnce(&mut String)) {
88 let clipboard_mutex =
89 SHARED_FALLBACK_CLIPBOARD.get_or_init(|| Mutex::new(Default::default()));
90 if let LockResult::Ok(mut string) = clipboard_mutex.lock() {
91 callback(&mut string)
92 }
93 }
94
95 pub(super) fn clear() {
96 with_shared_clipboard(|clipboard_string| {
97 clipboard_string.clear();
98 });
99 }
100
101 pub(super) fn get_text(request: StringRequest) {
102 with_shared_clipboard(move |clipboard_string| request.success(clipboard_string.clone()));
103 }
104
105 pub(super) fn set_text(new_contents: String) {
106 with_shared_clipboard(move |clipboard_string| {
107 *clipboard_string = new_contents;
108 });
109 }
110}
111
112#[cfg(all(
113 feature = "clipboard",
114 not(any(target_os = "android", target_env = "ohos"))
115))]
116mod clipboard {
117 use std::sync::OnceLock;
118
119 use arboard::Clipboard;
120 use parking_lot::Mutex;
121
122 use super::StringRequest;
123 use crate::clipboard_delegate::fallback_clipboard;
124
125 static SHARED_CLIPBOARD: OnceLock<Option<Mutex<Clipboard>>> = OnceLock::new();
130
131 fn with_shared_clipboard<ResultType>(
132 callback: impl FnOnce(&mut Clipboard) -> Result<ResultType, arboard::Error>,
133 ) -> Result<ResultType, arboard::Error> {
134 match SHARED_CLIPBOARD.get_or_init(|| Clipboard::new().ok().map(Mutex::new)) {
135 Some(clipboard_mutex) => callback(&mut clipboard_mutex.lock()),
136 None => Err(arboard::Error::ClipboardNotSupported),
137 }
138 }
139
140 pub(super) fn clear() {
141 if with_shared_clipboard(|clipboard| clipboard.clear()).is_err() {
142 fallback_clipboard::clear();
143 }
144 }
145
146 pub(super) fn get_text(request: StringRequest) {
147 if let Ok(text) = with_shared_clipboard(|clipboard| clipboard.get_text()) {
148 request.success(text);
149 return;
150 };
151 fallback_clipboard::get_text(request);
152 }
153
154 pub(super) fn set_text(new_contents: String) {
155 if with_shared_clipboard(|clipboard| clipboard.set_text(&new_contents)).is_err() {
156 fallback_clipboard::set_text(new_contents);
157 }
158 }
159}
160
161#[cfg(all(feature = "clipboard", target_env = "ohos"))]
162mod clipboard {
163 use super::StringRequest;
164 use crate::clipboard_delegate::fallback_clipboard;
165
166 pub(super) fn clear() {
167 if let Err(error) = ohos_pasteboard::clear() {
168 log::warn!(
169 "OHOS pasteboard clear failed ({error}); using in-memory fallback_clipboard"
170 );
171 fallback_clipboard::clear();
172 }
173 }
174
175 pub(super) fn get_text(request: StringRequest) {
176 match ohos_pasteboard::get_text() {
177 Ok(text) => request.success(text),
178 Err(ohos_pasteboard::Error::NoText) => request.success(String::new()),
179 Err(error) => {
180 log::warn!(
181 "OHOS pasteboard get_text failed ({error}); using in-memory fallback_clipboard"
182 );
183 fallback_clipboard::get_text(request);
184 },
185 }
186 }
187
188 pub(super) fn set_text(new_contents: String) {
189 if let Err(error) = ohos_pasteboard::set_text(&new_contents) {
190 log::warn!(
191 "OHOS pasteboard set_text failed ({error}); using in-memory fallback_clipboard"
192 );
193 fallback_clipboard::set_text(new_contents);
194 }
195 }
196}
197
198#[cfg(any(
199 not(feature = "clipboard"),
200 all(feature = "clipboard", target_os = "android")
201))]
202mod clipboard {
203 use super::StringRequest;
204 use crate::clipboard_delegate::fallback_clipboard;
205
206 pub(super) fn clear() {
207 fallback_clipboard::clear();
208 }
209
210 pub(super) fn get_text(request: StringRequest) {
211 fallback_clipboard::get_text(request);
212 }
213
214 pub(super) fn set_text(new_contents: String) {
215 fallback_clipboard::set_text(new_contents);
216 }
217}