Skip to content
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

Web: forbid additional functions in favor of caching them #3219

Merged
merged 1 commit into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ disallowed-methods = [
{ path = "web_sys::HtmlCanvasElement::set_height", reason = "Winit shouldn't touch the internal canvas size" },
{ path = "web_sys::Window::document", reason = "cache this to reduce calls to JS" },
{ path = "web_sys::Window::get_computed_style", reason = "cache this to reduce calls to JS" },
{ path = "web_sys::HtmlElement::style", reason = "cache this to reduce calls to JS" },
{ path = "web_sys::Element::request_fullscreen", reason = "Doesn't account for compatibility with Safari" },
{ path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" },
{ path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
Expand Down
55 changes: 44 additions & 11 deletions src/platform_impl/web/web_sys/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,18 @@ pub struct Common {
pub document: Document,
/// Note: resizing the HTMLCanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained.
pub raw: HtmlCanvasElement,
style: CssStyleDeclaration,
style: Style,
old_size: Rc<Cell<PhysicalSize<u32>>>,
current_size: Rc<Cell<PhysicalSize<u32>>>,
fullscreen_handler: Rc<FullscreenHandler>,
}

#[derive(Clone)]
pub struct Style {
read: CssStyleDeclaration,
write: CssStyleDeclaration,
}

impl Canvas {
pub fn create(
id: WindowId,
Expand Down Expand Up @@ -90,12 +96,7 @@ impl Canvas {
.map_err(|_| os_error!(OsError("Failed to set a tabindex".to_owned())))?;
}

#[allow(clippy::disallowed_methods)]
let style = window
.get_computed_style(&canvas)
.expect("Failed to obtain computed style")
// this can't fail: we aren't using a pseudo-element
.expect("Invalid pseudo-element");
let style = Style::new(&window, &canvas);

let common = Common {
window: window.clone(),
Expand Down Expand Up @@ -178,9 +179,7 @@ impl Canvas {
y: bounds.y(),
};

if self.document().contains(Some(self.raw()))
&& self.style().get_property_value("display").unwrap() != "none"
{
if self.document().contains(Some(self.raw())) && self.style().get("display") != "none" {
position.x += super::style_size_property(self.style(), "border-left-width")
+ super::style_size_property(self.style(), "padding-left");
position.y += super::style_size_property(self.style(), "border-top-width")
Expand Down Expand Up @@ -226,7 +225,7 @@ impl Canvas {
}

#[inline]
pub fn style(&self) -> &CssStyleDeclaration {
pub fn style(&self) -> &Style {
&self.common.style
}

Expand Down Expand Up @@ -563,3 +562,37 @@ impl Common {
})
}
}

impl Style {
fn new(window: &web_sys::Window, canvas: &HtmlCanvasElement) -> Self {
#[allow(clippy::disallowed_methods)]
let read = window
.get_computed_style(canvas)
.expect("Failed to obtain computed style")
// this can't fail: we aren't using a pseudo-element
.expect("Invalid pseudo-element");

#[allow(clippy::disallowed_methods)]
let write = canvas.style();

Self { read, write }
}

pub(crate) fn get(&self, property: &str) -> String {
self.read
.get_property_value(property)
.expect("Invalid property")
}

pub(crate) fn remove(&self, property: &str) {
self.write
.remove_property(property)
.expect("Property is read only");
}

pub(crate) fn set(&self, property: &str, value: &str) {
self.write
.set_property(property, value)
.expect("Property is read only");
}
}
72 changes: 27 additions & 45 deletions src/platform_impl/web/web_sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@ mod resize_scaling;
mod schedule;

pub use self::canvas::Canvas;
use self::canvas::Style;
pub use self::event::ButtonsState;
pub use self::event_handle::EventListenerHandle;
pub use self::resize_scaling::ResizeScaleHandle;
pub use self::schedule::Schedule;

use crate::dpi::{LogicalPosition, LogicalSize};
use wasm_bindgen::closure::Closure;
use web_sys::{
CssStyleDeclaration, Document, HtmlCanvasElement, PageTransitionEvent, VisibilityState,
};
use web_sys::{Document, HtmlCanvasElement, PageTransitionEvent, VisibilityState};

pub fn throw(msg: &str) {
wasm_bindgen::throw_str(msg);
Expand Down Expand Up @@ -51,8 +50,8 @@ pub fn scale_factor(window: &web_sys::Window) -> f64 {
window.device_pixel_ratio()
}

fn fix_canvas_size(style: &CssStyleDeclaration, mut size: LogicalSize<f64>) -> LogicalSize<f64> {
if style.get_property_value("box-sizing").unwrap() == "border-box" {
fn fix_canvas_size(style: &Style, mut size: LogicalSize<f64>) -> LogicalSize<f64> {
if style.get("box-sizing") == "border-box" {
size.width += style_size_property(style, "border-left-width")
+ style_size_property(style, "border-right-width")
+ style_size_property(style, "padding-left")
Expand All @@ -69,76 +68,68 @@ fn fix_canvas_size(style: &CssStyleDeclaration, mut size: LogicalSize<f64>) -> L
pub fn set_canvas_size(
document: &Document,
raw: &HtmlCanvasElement,
style: &CssStyleDeclaration,
style: &Style,
new_size: LogicalSize<f64>,
) {
if !document.contains(Some(raw)) || style.get_property_value("display").unwrap() == "none" {
if !document.contains(Some(raw)) || style.get("display") == "none" {
return;
}

let new_size = fix_canvas_size(style, new_size);

set_canvas_style_property(raw, "width", &format!("{}px", new_size.width));
set_canvas_style_property(raw, "height", &format!("{}px", new_size.height));
style.set("width", &format!("{}px", new_size.width));
style.set("height", &format!("{}px", new_size.height));
}

pub fn set_canvas_min_size(
document: &Document,
raw: &HtmlCanvasElement,
style: &CssStyleDeclaration,
style: &Style,
dimensions: Option<LogicalSize<f64>>,
) {
if let Some(dimensions) = dimensions {
if !document.contains(Some(raw)) || style.get_property_value("display").unwrap() == "none" {
if !document.contains(Some(raw)) || style.get("display") == "none" {
return;
}

let new_size = fix_canvas_size(style, dimensions);

set_canvas_style_property(raw, "min-width", &format!("{}px", new_size.width));
set_canvas_style_property(raw, "min-height", &format!("{}px", new_size.height));
style.set("min-width", &format!("{}px", new_size.width));
style.set("min-height", &format!("{}px", new_size.height));
} else {
style
.remove_property("min-width")
.expect("Property is read only");
style
.remove_property("min-height")
.expect("Property is read only");
style.remove("min-width");
style.remove("min-height");
}
}

pub fn set_canvas_max_size(
document: &Document,
raw: &HtmlCanvasElement,
style: &CssStyleDeclaration,
style: &Style,
dimensions: Option<LogicalSize<f64>>,
) {
if let Some(dimensions) = dimensions {
if !document.contains(Some(raw)) || style.get_property_value("display").unwrap() == "none" {
if !document.contains(Some(raw)) || style.get("display") == "none" {
return;
}

let new_size = fix_canvas_size(style, dimensions);

set_canvas_style_property(raw, "max-width", &format!("{}px", new_size.width));
set_canvas_style_property(raw, "max-height", &format!("{}px", new_size.height));
style.set("max-width", &format!("{}px", new_size.width));
style.set("max-height", &format!("{}px", new_size.height));
} else {
style
.remove_property("max-width")
.expect("Property is read only");
style
.remove_property("max-height")
.expect("Property is read only");
style.remove("max-width");
style.remove("max-height");
}
}

pub fn set_canvas_position(
document: &Document,
raw: &HtmlCanvasElement,
style: &CssStyleDeclaration,
style: &Style,
mut position: LogicalPosition<f64>,
) {
if document.contains(Some(raw)) && style.get_property_value("display").unwrap() != "none" {
if document.contains(Some(raw)) && style.get("display") != "none" {
position.x -= style_size_property(style, "margin-left")
+ style_size_property(style, "border-left-width")
+ style_size_property(style, "padding-left");
Expand All @@ -147,30 +138,21 @@ pub fn set_canvas_position(
+ style_size_property(style, "padding-top");
}

set_canvas_style_property(raw, "position", "fixed");
set_canvas_style_property(raw, "left", &format!("{}px", position.x));
set_canvas_style_property(raw, "top", &format!("{}px", position.y));
style.set("position", "fixed");
style.set("left", &format!("{}px", position.x));
style.set("top", &format!("{}px", position.y));
}

/// This function will panic if the element is not inserted in the DOM
/// or is not a CSS property that represents a size in pixel.
pub fn style_size_property(style: &CssStyleDeclaration, property: &str) -> f64 {
let prop = style
.get_property_value(property)
.expect("Found invalid property");
pub fn style_size_property(style: &Style, property: &str) -> f64 {
let prop = style.get(property);
prop.strip_suffix("px")
.expect("Element was not inserted into the DOM or is not a size in pixel")
.parse()
.expect("CSS property is not a size in pixel")
}

pub fn set_canvas_style_property(raw: &HtmlCanvasElement, property: &str, value: &str) {
let style = raw.style();
style
.set_property(property, value)
.unwrap_or_else(|err| panic!("error: {err:?}\nFailed to set {property}"))
}

pub fn is_dark_mode(window: &web_sys::Window) -> Option<bool> {
window
.match_media("(prefers-color-scheme: dark)")
Expand Down
23 changes: 9 additions & 14 deletions src/platform_impl/web/web_sys/resize_scaling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ use js_sys::{Array, Object};
use wasm_bindgen::prelude::{wasm_bindgen, Closure};
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{
CssStyleDeclaration, Document, HtmlCanvasElement, MediaQueryList, ResizeObserver,
ResizeObserverBoxOptions, ResizeObserverEntry, ResizeObserverOptions, ResizeObserverSize,
Window,
Document, HtmlCanvasElement, MediaQueryList, ResizeObserver, ResizeObserverBoxOptions,
ResizeObserverEntry, ResizeObserverOptions, ResizeObserverSize, Window,
};

use crate::dpi::{LogicalSize, PhysicalSize};

use super::super::backend;
use super::canvas::Style;
use super::media_query_handle::MediaQueryListHandle;

use std::cell::{Cell, RefCell};
Expand All @@ -22,7 +22,7 @@ impl ResizeScaleHandle {
window: Window,
document: Document,
canvas: HtmlCanvasElement,
style: CssStyleDeclaration,
style: Style,
scale_handler: S,
resize_handler: R,
) -> Self
Expand Down Expand Up @@ -51,7 +51,7 @@ struct ResizeScaleInternal {
window: Window,
document: Document,
canvas: HtmlCanvasElement,
style: CssStyleDeclaration,
style: Style,
mql: MediaQueryListHandle,
observer: ResizeObserver,
_observer_closure: Closure<dyn FnMut(Array, ResizeObserver)>,
Expand All @@ -65,7 +65,7 @@ impl ResizeScaleInternal {
window: Window,
document: Document,
canvas: HtmlCanvasElement,
style: CssStyleDeclaration,
style: Style,
scale_handler: S,
resize_handler: R,
) -> Rc<RefCell<Self>>
Expand Down Expand Up @@ -152,9 +152,7 @@ impl ResizeScaleInternal {
}

fn notify(&mut self) {
if !self.document.contains(Some(&self.canvas))
|| self.style.get_property_value("display").unwrap() == "none"
{
if !self.document.contains(Some(&self.canvas)) || self.style.get("display") == "none" {
let size = PhysicalSize::new(0, 0);

if self.notify_scale.replace(false) {
Expand All @@ -180,7 +178,7 @@ impl ResizeScaleInternal {
backend::style_size_property(&self.style, "height"),
);

if self.style.get_property_value("box-sizing").unwrap() == "border-box" {
if self.style.get("box-sizing") == "border-box" {
size.width -= backend::style_size_property(&self.style, "border-left-width")
+ backend::style_size_property(&self.style, "border-right-width")
+ backend::style_size_property(&self.style, "padding-left")
Expand Down Expand Up @@ -246,10 +244,7 @@ impl ResizeScaleInternal {
.get(0)
.unchecked_into();

let writing_mode = self
.style
.get_property_value("writing-mode")
.expect("`writing-mode` is a valid CSS property");
let writing_mode = self.style.get("writing-mode");

// means the canvas is not inserted into the DOM
if writing_mode.is_empty() {
Expand Down
13 changes: 6 additions & 7 deletions src/platform_impl/web/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ impl Inner {
#[inline]
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
*self.previous_pointer.borrow_mut() = cursor.name();
backend::set_canvas_style_property(self.canvas.borrow().raw(), "cursor", cursor.name());
self.canvas.borrow().style().set("cursor", cursor.name());
}

#[inline]
Expand All @@ -223,13 +223,12 @@ impl Inner {
#[inline]
pub fn set_cursor_visible(&self, visible: bool) {
if !visible {
backend::set_canvas_style_property(self.canvas.borrow().raw(), "cursor", "none");
self.canvas.borrow().style().set("cursor", "none");
} else {
backend::set_canvas_style_property(
self.canvas.borrow().raw(),
"cursor",
&self.previous_pointer.borrow(),
);
self.canvas
.borrow()
.style()
.set("cursor", &self.previous_pointer.borrow());
}
}

Expand Down
Loading