-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: clipboard support for web client #259
Conversation
Coverage Report 🤖 ⚙️Past: New: Diff: +0.57% [this comment will be updated automatically] |
FYI: I was clippy lints regarding arithmetic side-effects, I'll fix that, other than that PR is ready for the review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Truly excellent work!
I appreciate you created a re-usable crate ironrdp-cliprdr-format
👍
In addition to the comments below, you may want to do a typos
pass.
check_invariant(self.width.abs() <= u16::MAX.into()) | ||
.ok_or_else(|| invalid_message_err!("biWidth", "width is too big"))?; | ||
check_invariant(self.height.abs() <= u16::MAX.into()) | ||
.ok_or_else(|| invalid_message_err!("biHeight", "height is too big"))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invariants should be checked when constructing the struct (when decoding or calling new), so that remaining of the code (encoding, casting…) may rely on this and just assume this to be true without checking again
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, INVARIANT:
comments should come where the variable is declared, either in the type definition, or above the let
for local variables. The rationale is that invariants should be uphold at all times, and it’s useful to keep them in mind when analyzing the flow of the code.
// INVARIANT: end_pos < headers_cursor.len() - 1
let end_pos = /* … */;
Typically, when a local invariant is defined using INVARIANT:
, the reader should not have to backtrack in order to verify it, but read forward instead.
Example in rust-analyzer code:
https://github.com/rust-lang/rust-analyzer/blob/c1c9e10f72ffd2e829d20ff1439ff49c2e121731/crates/hir-ty/src/lower.rs#L1118-L1127
When it’s not obvious why some piece of code is indeed ensuring the invariant holds, a comment may be added:
https://github.com/rust-lang/rust-analyzer/blob/c1c9e10f72ffd2e829d20ff1439ff49c2e121731/crates/hir-ty/src/lower.rs#L1143-L1144
And when explaining an assumption for doing something non obvious such as disabling a lint locally, the invariant may be referenced to without prefixing the comment with INVARIANT:
(because it is already defined elsewhere)
// Given the invariant on `end_pos`, this code can’t cause integer overflow
#[allow(clippy::arithmetic_side_effects)]
{
/* … */
}
|
||
let components = color_type.samples(); | ||
debug_assert!(components <= 4); | ||
// INVARIANT: height * width * components <= usize::MAX |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invariant bounds are better expressed as
// INVARIANT: height * width * components <= usize::MAX | |
// INVARIANT: height * width * components <= u16::MAX * u16::MAX * 4 < usize::MAX |
_ => unreachable!("possible values are restricted by header validation and logic above"), | ||
}; | ||
|
||
// INVARIANT: width * components <= usize::MAX |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stronger:
// INVARIANT: width * components <= usize::MAX | |
// INVARIANT: width * components <= u16::MAX * 4 < usize::MAX |
|
||
// INVARIANT: stride * height_unsigned <= usize::MAX. | ||
// | ||
// This should be always true, because stide can't be greater than `width_unsigned * 4`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// This should be always true, because stide can't be greater than `width_unsigned * 4`, | |
// This never overflows, because stride can't be greater than `width_unsigned * 4`, |
// INVARIANT: stride * height_unsigned <= usize::MAX. | ||
// | ||
// This should be always true, because stide can't be greater than `width_unsigned * 4`, | ||
// and `width_unsigned * height_unsigned * 4` is guaranteed to be less or equal to `usize::MAX`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The guarantee is even stronger
// and `width_unsigned * height_unsigned * 4` is guaranteed to be less or equal to `usize::MAX`. | |
// and `width_unsigned * height_unsigned * 4` is guaranteed to be lesser to `usize::MAX`. |
let width_unsigned = u16::try_from(info.width).unwrap(); | ||
let height_unsigned = u16::try_from(info.height).unwrap(); | ||
|
||
// INVARIANT: stride * height_unsigned <= usize::MAX. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// INVARIANT: stride * height_unsigned <= usize::MAX. | |
// INVARIANT: stride * height_unsigned < usize::MAX. |
check_invariant(u16::try_from(info.width).is_ok()).ok_or_else(|| BitmapError::WidthTooBig)?; | ||
check_invariant(u16::try_from(info.height).is_ok()).ok_or_else(|| BitmapError::HeightTooBig)?; | ||
|
||
let width_unsigned = u16::try_from(info.width).unwrap(); | ||
let height_unsigned = u16::try_from(info.height).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’s better style to not split the precondition check from the precondition use when possible
check_invariant(u16::try_from(info.width).is_ok()).ok_or_else(|| BitmapError::WidthTooBig)?; | |
check_invariant(u16::try_from(info.height).is_ok()).ok_or_else(|| BitmapError::HeightTooBig)?; | |
let width_unsigned = u16::try_from(info.width).unwrap(); | |
let height_unsigned = u16::try_from(info.height).unwrap(); | |
let width_unsigned: u16 = u16::try_from(info.width).map_err(|_| BitmapError::WidthTooBig)?; | |
let height_unsigned: u16 = u16::try_from(info.height).map_err(|_| BitmapError::HeightTooBig)?; |
}; | ||
|
||
// Row is in RGBA format | ||
// INVARIANT: width_unsigned * 4 <= usize::MAX |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stronger
// INVARIANT: width_unsigned * 4 <= usize::MAX | |
// INVARIANT: width_unsigned * 4 <= u16::MAX * 4 < usize::MAX |
// INVARIANT: end_pos < headers_cursor.len() - 1 | ||
#[allow(clippy::arithmetic_side_effects)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The INVARIANT:
comment should come where end_pos
is declared so the reader knows what is being enforced for this value before reading the logic itself.
// INVARIANT: end_pos < headers_cursor.len() - 1
let end_pos = /* … */;
Then, the invariant can be used to explain why it’s okay to do something non obvious such as disabling a lint:
// Given the invariant on `end_pos`, this code can’t cause integer overflow
#[allow(clippy::arithmetic_side_effects)]
{
/* … */
}
let start_fragment_pos_value = format!("{:0>10}", start_fragment_pos); | ||
let end_fragment_pos_value = format!("{:0>10}", end_fragment_pos); | ||
|
||
// INVARIANT: buffer.len() > placeholder_pos + POS_PLACEHOLDER.len() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’s generally better to use <
and <=
over >
and >=
placeholder_pos + POS_PLACEHOLDER.len() < buffer.len()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You’ll have to rebase on master to fix conflicts on iron-remote-gui.
It’s probably not much.
FYI, formatting and lints checks for TypeScript were added recently.
Here are the commands you should run when checking the code:
$ npm run check
$ npm run format
$ npm run lint
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can address the remaining comments and I’ll leave you merge yourself
5d2bc54
to
e8773d8
Compare
This PR adds clipboard support for IronRDP-web
Features:
Most complete support could be achieved on chromium-based browsers
text/plain
,text/html
,text/png
Firefox has reduced clipboard support due to browser restrictions:
text/plain
text/plain
,text/html
,text/png
Internally, the following
CLIPRDR
formats are handled and converted to/from their MIME counterparts:CF_UNICODETEXT
<=>text/plain
Registered(CF_HTML<HTML Format>)
,Registered(text/html)
<=>text/html
CF_DIB
,CF_DIBV5
,Registered(PNG)
,Registered(image/png)
<=>image/png
Closes #107
Issue: ARC-125