mirror of
https://github.com/Devolutions/IronRDP.git
synced 2025-08-04 15:18:17 +00:00
refactor(ironrdp-web): implement JsValue conversion from ClipboardTransaction and ClipboardContent
This commit is contained in:
parent
ec1832bba0
commit
e7c4bcd416
8 changed files with 76 additions and 42 deletions
|
@ -1,13 +1,15 @@
|
|||
use crate::IronError;
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::js_sys;
|
||||
|
||||
pub trait ClipboardTransaction {
|
||||
type ClipboardContent: ClipboardContent;
|
||||
type Error: IronError;
|
||||
|
||||
fn init() -> Self;
|
||||
fn add_content(&mut self, content: Self::ClipboardContent);
|
||||
fn is_empty(&self) -> bool;
|
||||
fn contents(&self) -> js_sys::Array;
|
||||
fn contents(&self) -> Result<js_sys::Array, Self::Error>;
|
||||
}
|
||||
|
||||
pub trait ClipboardContent {
|
||||
|
|
|
@ -5,7 +5,7 @@ pub trait IronError {
|
|||
fn kind(&self) -> IronErrorKind;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[wasm_bindgen]
|
||||
pub enum IronErrorKind {
|
||||
/// Catch-all error kind
|
||||
|
|
|
@ -331,8 +331,8 @@ macro_rules! export {
|
|||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn content(&self) -> js_sys::Array {
|
||||
iron_remote_desktop::ClipboardTransaction::contents(&self.0)
|
||||
pub fn content(&self) -> Result<js_sys::Array, IronError> {
|
||||
iron_remote_desktop::ClipboardTransaction::contents(&self.0).map_err(IronError)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
|
||||
mod transaction;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use futures_channel::mpsc;
|
||||
use iron_remote_desktop::{ClipboardContent as _, ClipboardTransaction as _};
|
||||
use ironrdp::cliprdr::backend::{ClipboardMessage, CliprdrBackend};
|
||||
|
@ -25,13 +24,14 @@ use ironrdp::cliprdr::pdu::{
|
|||
use ironrdp_cliprdr_format::bitmap::{dib_to_png, dibv5_to_png, png_to_cf_dibv5};
|
||||
use ironrdp_cliprdr_format::html::{cf_html_to_plain_html, plain_html_to_cf_html};
|
||||
use ironrdp_core::{impl_as_any, IntoOwned};
|
||||
use std::collections::HashMap;
|
||||
use transaction::ClipboardContentValue;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::session::RdpInputEvent;
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub(crate) use transaction::{RdpClipboardTransaction, RdpClipboardContent};
|
||||
pub(crate) use transaction::{ClipboardTransaction, ClipboardContent};
|
||||
|
||||
const MIME_TEXT: &str = "text/plain";
|
||||
const MIME_HTML: &str = "text/html";
|
||||
|
@ -104,7 +104,7 @@ impl WasmClipboardMessageProxy {
|
|||
/// Messages sent by the JS code or CLIPRDR to the backend implementation.
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum WasmClipboardBackendMessage {
|
||||
LocalClipboardChanged(RdpClipboardTransaction),
|
||||
LocalClipboardChanged(ClipboardTransaction),
|
||||
RemoteDataRequest(ClipboardFormatId),
|
||||
|
||||
RemoteClipboardChanged(Vec<ClipboardFormat>),
|
||||
|
@ -117,8 +117,8 @@ pub(crate) enum WasmClipboardBackendMessage {
|
|||
/// Clipboard backend implementation for web. This object should be created once per session and
|
||||
/// kept alive until session is terminated.
|
||||
pub(crate) struct WasmClipboard {
|
||||
local_clipboard: Option<RdpClipboardTransaction>,
|
||||
remote_clipboard: RdpClipboardTransaction,
|
||||
local_clipboard: Option<ClipboardTransaction>,
|
||||
remote_clipboard: ClipboardTransaction,
|
||||
|
||||
remote_mapping: HashMap<ClipboardFormatId, String>,
|
||||
remote_formats_to_read: Vec<ClipboardFormatId>,
|
||||
|
@ -138,7 +138,7 @@ impl WasmClipboard {
|
|||
pub(crate) fn new(message_proxy: WasmClipboardMessageProxy, js_callbacks: JsClipboardCallbacks) -> Self {
|
||||
Self {
|
||||
local_clipboard: None,
|
||||
remote_clipboard: RdpClipboardTransaction::init(),
|
||||
remote_clipboard: ClipboardTransaction::init(),
|
||||
proxy: message_proxy,
|
||||
js_callbacks,
|
||||
|
||||
|
@ -156,7 +156,7 @@ impl WasmClipboard {
|
|||
|
||||
fn handle_local_clipboard_changed(
|
||||
&mut self,
|
||||
transaction: RdpClipboardTransaction,
|
||||
transaction: ClipboardTransaction,
|
||||
) -> anyhow::Result<Vec<ClipboardFormat>> {
|
||||
let mut formats = Vec::new();
|
||||
transaction.contents().iter().for_each(|content| {
|
||||
|
@ -372,21 +372,21 @@ impl WasmClipboard {
|
|||
|
||||
let content = match pending_format {
|
||||
ClipboardFormatId::CF_UNICODETEXT => match response.to_unicode_string() {
|
||||
Ok(text) => Some(RdpClipboardContent::new_text(MIME_TEXT, &text)),
|
||||
Ok(text) => Some(ClipboardContent::new_text(MIME_TEXT, &text)),
|
||||
Err(err) => {
|
||||
error!("CF_UNICODETEXT decode error: {}", err);
|
||||
None
|
||||
}
|
||||
},
|
||||
ClipboardFormatId::CF_DIB => match dib_to_png(response.data()) {
|
||||
Ok(png) => Some(RdpClipboardContent::new_binary(MIME_PNG, &png)),
|
||||
Ok(png) => Some(ClipboardContent::new_binary(MIME_PNG, &png)),
|
||||
Err(err) => {
|
||||
warn!("DIB decode error: {}", err);
|
||||
None
|
||||
}
|
||||
},
|
||||
ClipboardFormatId::CF_DIBV5 => match dibv5_to_png(response.data()) {
|
||||
Ok(png) => Some(RdpClipboardContent::new_binary(MIME_PNG, &png)),
|
||||
Ok(png) => Some(ClipboardContent::new_binary(MIME_PNG, &png)),
|
||||
Err(err) => {
|
||||
warn!("DIBv5 decode error: {}", err);
|
||||
None
|
||||
|
@ -396,21 +396,21 @@ impl WasmClipboard {
|
|||
let format_name = self.remote_mapping.get(®istered).map(|s| s.as_str());
|
||||
match format_name {
|
||||
Some(FORMAT_WIN_HTML_NAME) => match cf_html_to_plain_html(response.data()) {
|
||||
Ok(text) => Some(RdpClipboardContent::new_text(MIME_HTML, text)),
|
||||
Ok(text) => Some(ClipboardContent::new_text(MIME_HTML, text)),
|
||||
Err(err) => {
|
||||
warn!("CF_HTML decode error: {}", err);
|
||||
None
|
||||
}
|
||||
},
|
||||
Some(FORMAT_MIME_HTML_NAME) => match response.to_string() {
|
||||
Ok(text) => Some(RdpClipboardContent::new_text(MIME_HTML, &text)),
|
||||
Ok(text) => Some(ClipboardContent::new_text(MIME_HTML, &text)),
|
||||
Err(err) => {
|
||||
warn!("text/html decode error: {}", err);
|
||||
None
|
||||
}
|
||||
},
|
||||
Some(FORMAT_MIME_PNG_NAME) | Some(FORMAT_PNG_NAME) => {
|
||||
Some(RdpClipboardContent::new_binary(MIME_PNG, response.data()))
|
||||
Some(ClipboardContent::new_binary(MIME_PNG, response.data()))
|
||||
}
|
||||
_ => {
|
||||
// Not supported format
|
||||
|
@ -438,7 +438,10 @@ impl WasmClipboard {
|
|||
// Set clipboard when all formats were read
|
||||
self.js_callbacks
|
||||
.on_remote_clipboard_changed
|
||||
.call1(&JsValue::NULL, &JsValue::from(transaction))
|
||||
.call1(
|
||||
&JsValue::NULL,
|
||||
&transaction.to_js_value().map_err(|e| anyhow!("{:?}", e))?,
|
||||
)
|
||||
.expect("failed to call JS callback");
|
||||
}
|
||||
|
||||
|
@ -507,7 +510,7 @@ impl WasmClipboard {
|
|||
} else {
|
||||
// If no initial clipboard callback was set, send empty format list instead
|
||||
return self.process_event(WasmClipboardBackendMessage::LocalClipboardChanged(
|
||||
RdpClipboardTransaction::init(),
|
||||
ClipboardTransaction::init(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,42 @@
|
|||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use crate::error::IronError;
|
||||
use anyhow::anyhow;
|
||||
use js_sys::{Object, Reflect};
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
/// Object which represents complete clipboard transaction with multiple MIME types.
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(crate) struct RdpClipboardTransaction {
|
||||
contents: Vec<RdpClipboardContent>,
|
||||
pub(crate) struct ClipboardTransaction {
|
||||
contents: Vec<ClipboardContent>,
|
||||
}
|
||||
|
||||
impl RdpClipboardTransaction {
|
||||
pub(crate) fn contents(&self) -> &[RdpClipboardContent] {
|
||||
impl ClipboardTransaction {
|
||||
pub(crate) fn contents(&self) -> &[ClipboardContent] {
|
||||
&self.contents
|
||||
}
|
||||
|
||||
pub(crate) fn clear(&mut self) {
|
||||
self.contents.clear();
|
||||
}
|
||||
|
||||
pub(crate) fn to_js_value(&self) -> Result<JsValue, IronError> {
|
||||
let js_object = Object::new();
|
||||
|
||||
Reflect::set(
|
||||
&js_object,
|
||||
&JsValue::from("contents"),
|
||||
&iron_remote_desktop::ClipboardTransaction::contents(self)
|
||||
.map_err(|e| anyhow!("{:?}", e))?
|
||||
.into(),
|
||||
)
|
||||
.map_err(|e| anyhow!("JS error: {:?}", e))?;
|
||||
|
||||
Ok(js_object.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl iron_remote_desktop::ClipboardTransaction for RdpClipboardTransaction {
|
||||
type ClipboardContent = RdpClipboardContent;
|
||||
impl iron_remote_desktop::ClipboardTransaction for ClipboardTransaction {
|
||||
type ClipboardContent = ClipboardContent;
|
||||
type Error = IronError;
|
||||
|
||||
fn init() -> Self {
|
||||
Self { contents: Vec::new() }
|
||||
|
@ -33,17 +50,18 @@ impl iron_remote_desktop::ClipboardTransaction for RdpClipboardTransaction {
|
|||
self.contents.is_empty()
|
||||
}
|
||||
|
||||
fn contents(&self) -> js_sys::Array {
|
||||
js_sys::Array::from_iter(
|
||||
fn contents(&self) -> Result<js_sys::Array, Self::Error> {
|
||||
Ok(js_sys::Array::from_iter(
|
||||
self.contents
|
||||
.iter()
|
||||
.map(|content: &RdpClipboardContent| JsValue::from(content.clone())),
|
||||
)
|
||||
.map(|content| content.to_js_value())
|
||||
.collect::<Result<Vec<_>, Self::Error>>()?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<RdpClipboardContent> for RdpClipboardTransaction {
|
||||
fn from_iter<T: IntoIterator<Item = RdpClipboardContent>>(iter: T) -> Self {
|
||||
impl FromIterator<ClipboardContent> for ClipboardTransaction {
|
||||
fn from_iter<T: IntoIterator<Item = ClipboardContent>>(iter: T) -> Self {
|
||||
Self {
|
||||
contents: iter.into_iter().collect(),
|
||||
}
|
||||
|
@ -66,14 +84,13 @@ impl ClipboardContentValue {
|
|||
}
|
||||
|
||||
/// Object which represents single clipboard format represented standard MIME type.
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct RdpClipboardContent {
|
||||
pub(crate) struct ClipboardContent {
|
||||
mime_type: String,
|
||||
value: ClipboardContentValue,
|
||||
}
|
||||
|
||||
impl RdpClipboardContent {
|
||||
impl ClipboardContent {
|
||||
pub(crate) fn mime_type(&self) -> &str {
|
||||
&self.mime_type
|
||||
}
|
||||
|
@ -81,9 +98,20 @@ impl RdpClipboardContent {
|
|||
pub(crate) fn value(&self) -> &ClipboardContentValue {
|
||||
&self.value
|
||||
}
|
||||
|
||||
fn to_js_value(&self) -> Result<JsValue, IronError> {
|
||||
let js_object = Object::new();
|
||||
|
||||
Reflect::set(&js_object, &JsValue::from("mime_type"), &JsValue::from(&self.mime_type))
|
||||
.map_err(|e| anyhow!("JS error: {:?}", e))?;
|
||||
Reflect::set(&js_object, &JsValue::from("value"), &self.value.value())
|
||||
.map_err(|e| anyhow!("JS error: {:?}", e))?;
|
||||
|
||||
Ok(js_object.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl iron_remote_desktop::ClipboardContent for RdpClipboardContent {
|
||||
impl iron_remote_desktop::ClipboardContent for ClipboardContent {
|
||||
fn new_text(mime_type: &str, text: &str) -> Self {
|
||||
Self {
|
||||
mime_type: mime_type.into(),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use iron_remote_desktop::IronErrorKind;
|
||||
use ironrdp::connector::{self, sspi, ConnectorErrorKind};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct IronError {
|
||||
kind: IronErrorKind,
|
||||
source: anyhow::Error,
|
||||
|
|
|
@ -30,8 +30,8 @@ impl RemoteDesktopApi for Api {
|
|||
type SessionTerminationInfo = session::SessionTerminationInfo;
|
||||
type DeviceEvent = input::DeviceEvent;
|
||||
type InputTransaction = input::InputTransaction;
|
||||
type ClipboardTransaction = clipboard::RdpClipboardTransaction;
|
||||
type ClipboardContent = clipboard::RdpClipboardContent;
|
||||
type ClipboardTransaction = clipboard::ClipboardTransaction;
|
||||
type ClipboardContent = clipboard::ClipboardContent;
|
||||
type Error = error::IronError;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ use web_sys::HtmlCanvasElement;
|
|||
|
||||
use crate::canvas::Canvas;
|
||||
use crate::clipboard;
|
||||
use crate::clipboard::{RdpClipboardTransaction, WasmClipboard, WasmClipboardBackend, WasmClipboardBackendMessage};
|
||||
use crate::clipboard::{ClipboardTransaction, WasmClipboard, WasmClipboardBackend, WasmClipboardBackendMessage};
|
||||
use crate::error::IronError;
|
||||
use crate::image::extract_partial_image;
|
||||
use crate::input::InputTransaction;
|
||||
|
@ -447,7 +447,7 @@ impl Session {
|
|||
impl iron_remote_desktop::Session for Session {
|
||||
type SessionTerminationInfo = SessionTerminationInfo;
|
||||
type InputTransaction = InputTransaction;
|
||||
type ClipboardTransaction = RdpClipboardTransaction;
|
||||
type ClipboardTransaction = ClipboardTransaction;
|
||||
type Error = IronError;
|
||||
|
||||
async fn run(&self) -> Result<Self::SessionTerminationInfo, Self::Error> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue