/* 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 ipc_channel::ipc::IpcSender; use crate::WebView; pub struct StringRequest { pub(crate) result_sender: IpcSender>, response_sent: bool, } impl StringRequest { pub fn success(mut self, string: String) { let _ = self.result_sender.send(Ok(string)); self.response_sent = true; } pub fn failure(mut self, message: String) { let _ = self.result_sender.send(Err(message)); self.response_sent = true; } } impl From>> for StringRequest { fn from(result_sender: IpcSender>) -> Self { Self { result_sender, response_sent: false, } } } impl Drop for StringRequest { fn drop(&mut self) { if !self.response_sent { let _ = self .result_sender .send(Err("No response sent to request.".into())); } } } /// A delegate that is responsible for accessing the system clipboard. On Mac, Windows, and /// Linux if the `clipboard` feature is enabled, a default delegate is automatically used /// that implements clipboard support. An embedding application can override this delegate /// by using this trait. pub trait ClipboardDelegate { /// A request to clear all contents of the system clipboard. fn clear(&self, _webview: WebView) {} /// A request to get the text contents of the system clipboard. Once the contents are /// retrieved the embedder should call [`StringRequest::success`] with the text or /// [`StringRequest::failure`] with a failure message. fn get_text(&self, _webview: WebView, _request: StringRequest) {} /// A request to set the text contents of the system clipboard to `new_contents`. fn set_text(&self, _webview: WebView, _new_contents: String) {} } pub(crate) struct DefaultClipboardDelegate; impl ClipboardDelegate for DefaultClipboardDelegate { fn clear(&self, _webview: WebView) { clipboard::clear(); } fn get_text(&self, _webview: WebView, request: StringRequest) { clipboard::get_text(request); } fn set_text(&self, _webview: WebView, new_contents: String) { clipboard::set_text(new_contents); } } #[cfg(all( feature = "clipboard", not(any(target_os = "android", target_env = "ohos")) ))] mod clipboard { use std::sync::OnceLock; use arboard::Clipboard; use parking_lot::Mutex; use super::StringRequest; /// A shared clipboard for use by the [`DefaultClipboardDelegate`]. This is protected by /// a mutex so that it can only be used by one thread at a time. The `arboard` documentation /// suggests that more than one thread shouldn't try to access the Windows clipboard at a /// time. See . static SHARED_CLIPBOARD: OnceLock>> = OnceLock::new(); fn with_shared_clipboard(callback: impl FnOnce(&mut Clipboard)) { if let Some(clipboard_mutex) = SHARED_CLIPBOARD.get_or_init(|| Clipboard::new().ok().map(Mutex::new)) { callback(&mut clipboard_mutex.lock()) } } pub(super) fn clear() { with_shared_clipboard(|clipboard| { let _ = clipboard.clear(); }); } pub(super) fn get_text(request: StringRequest) { with_shared_clipboard(move |clipboard| match clipboard.get_text() { Ok(text) => request.success(text), Err(error) => request.failure(format!("{error:?}")), }); } pub(super) fn set_text(new_contents: String) { with_shared_clipboard(move |clipboard| { let _ = clipboard.set_text(new_contents); }); } } #[cfg(any(not(feature = "clipboard"), target_os = "android", target_env = "ohos"))] mod clipboard { use super::StringRequest; pub(super) fn clear() {} pub(super) fn get_text(_: StringRequest) {} pub(super) fn set_text(_: String) {} }