use servo_base::generic_channel::GenericCallback;
use crate::WebView;
pub struct StringRequest {
pub(crate) result_sender: GenericCallback<Result<String, String>>,
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<GenericCallback<Result<String, String>>> for StringRequest {
fn from(result_sender: GenericCallback<Result<String, String>>) -> 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()));
}
}
}
pub trait ClipboardDelegate {
fn clear(&self, _webview: WebView) {}
fn get_text(&self, _webview: WebView, _request: StringRequest) {}
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);
}
}
mod fallback_clipboard {
use std::sync::{LockResult, Mutex, OnceLock};
use crate::clipboard_delegate::StringRequest;
static SHARED_FALLBACK_CLIPBOARD: OnceLock<Mutex<String>> = OnceLock::new();
fn with_shared_clipboard(callback: impl FnOnce(&mut String)) {
let clipboard_mutex =
SHARED_FALLBACK_CLIPBOARD.get_or_init(|| Mutex::new(Default::default()));
if let LockResult::Ok(mut string) = clipboard_mutex.lock() {
callback(&mut string)
}
}
pub(super) fn clear() {
with_shared_clipboard(|clipboard_string| {
clipboard_string.clear();
});
}
pub(super) fn get_text(request: StringRequest) {
with_shared_clipboard(move |clipboard_string| request.success(clipboard_string.clone()));
}
pub(super) fn set_text(new_contents: String) {
with_shared_clipboard(move |clipboard_string| {
*clipboard_string = 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;
use crate::clipboard_delegate::fallback_clipboard;
static SHARED_CLIPBOARD: OnceLock<Option<Mutex<Clipboard>>> = OnceLock::new();
fn with_shared_clipboard<ResultType>(
callback: impl FnOnce(&mut Clipboard) -> Result<ResultType, arboard::Error>,
) -> Result<ResultType, arboard::Error> {
match SHARED_CLIPBOARD.get_or_init(|| Clipboard::new().ok().map(Mutex::new)) {
Some(clipboard_mutex) => callback(&mut clipboard_mutex.lock()),
None => Err(arboard::Error::ClipboardNotSupported),
}
}
pub(super) fn clear() {
if with_shared_clipboard(|clipboard| clipboard.clear()).is_err() {
fallback_clipboard::clear();
}
}
pub(super) fn get_text(request: StringRequest) {
if let Ok(text) = with_shared_clipboard(|clipboard| clipboard.get_text()) {
request.success(text);
return;
};
fallback_clipboard::get_text(request);
}
pub(super) fn set_text(new_contents: String) {
if with_shared_clipboard(|clipboard| clipboard.set_text(&new_contents)).is_err() {
fallback_clipboard::set_text(new_contents);
}
}
}
#[cfg(any(not(feature = "clipboard"), target_os = "android", target_env = "ohos"))]
mod clipboard {
use super::StringRequest;
use crate::clipboard_delegate::fallback_clipboard;
pub(super) fn clear() {
fallback_clipboard::clear();
}
pub(super) fn get_text(request: StringRequest) {
fallback_clipboard::get_text(request);
}
pub(super) fn set_text(new_contents: String) {
fallback_clipboard::set_text(new_contents);
}
}