aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/config/prefs.rs3
-rw-r--r--components/devtools/actors/browsing_context.rs4
-rw-r--r--components/devtools/actors/console.rs10
-rw-r--r--components/devtools/actors/watcher.rs24
-rw-r--r--components/devtools/actors/worker.rs28
-rw-r--r--components/devtools/lib.rs22
-rw-r--r--components/devtools/resource.rs25
-rw-r--r--components/layout/display_list/mod.rs26
-rw-r--r--components/layout/display_list/stacking_context.rs135
-rw-r--r--components/layout/flow/mod.rs2
-rw-r--r--components/layout/flow/root.rs70
-rw-r--r--components/layout/fragment_tree/base_fragment.rs6
-rw-r--r--components/layout/fragment_tree/box_fragment.rs12
-rw-r--r--components/layout/fragment_tree/fragment_tree.rs56
-rw-r--r--components/script/dom/bindings/buffer_source.rs49
-rw-r--r--components/script/dom/element.rs92
-rw-r--r--components/script/dom/imagedata.rs49
-rw-r--r--components/script/webdriver_handlers.rs46
-rw-r--r--components/script_bindings/webidls/CanvasRenderingContext2D.webidl2
19 files changed, 297 insertions, 364 deletions
diff --git a/components/config/prefs.rs b/components/config/prefs.rs
index 96c40c91360..a9ec112e3eb 100644
--- a/components/config/prefs.rs
+++ b/components/config/prefs.rs
@@ -236,6 +236,8 @@ pub struct Preferences {
/// The user-agent to use for Servo. This can also be set via [`UserAgentPlatform`] in
/// order to set the value to the default value for the given platform.
pub user_agent: String,
+
+ pub log_filter: String,
}
impl Preferences {
@@ -398,6 +400,7 @@ impl Preferences {
threadpools_webrender_workers_max: 4,
webgl_testing_context_creation_error: false,
user_agent: String::new(),
+ log_filter: String::new(),
}
}
}
diff --git a/components/devtools/actors/browsing_context.rs b/components/devtools/actors/browsing_context.rs
index 5de0855df4a..81a00e82d47 100644
--- a/components/devtools/actors/browsing_context.rs
+++ b/components/devtools/actors/browsing_context.rs
@@ -149,10 +149,6 @@ impl ResourceAvailable for BrowsingContextActor {
fn actor_name(&self) -> String {
self.name.clone()
}
-
- fn get_streams(&self) -> &RefCell<HashMap<StreamId, TcpStream>> {
- &self.streams
- }
}
impl Actor for BrowsingContextActor {
diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs
index 3897ffa0fce..fd0bd184bfa 100644
--- a/components/devtools/actors/console.rs
+++ b/components/devtools/actors/console.rs
@@ -252,6 +252,7 @@ impl ConsoleActor {
page_error: PageError,
id: UniqueId,
registry: &ActorRegistry,
+ stream: &mut TcpStream,
) {
self.cached_events
.borrow_mut()
@@ -262,7 +263,11 @@ impl ConsoleActor {
if let Root::BrowsingContext(bc) = &self.root {
registry
.find::<BrowsingContextActor>(bc)
- .resource_available(PageErrorWrapper { page_error }, "error-message".into())
+ .resource_available(
+ PageErrorWrapper { page_error },
+ "error-message".into(),
+ stream,
+ )
};
}
}
@@ -272,6 +277,7 @@ impl ConsoleActor {
console_message: ConsoleMessage,
id: UniqueId,
registry: &ActorRegistry,
+ stream: &mut TcpStream,
) {
let log_message: ConsoleLog = console_message.into();
self.cached_events
@@ -283,7 +289,7 @@ impl ConsoleActor {
if let Root::BrowsingContext(bc) = &self.root {
registry
.find::<BrowsingContextActor>(bc)
- .resource_available(log_message, "console-message".into())
+ .resource_available(log_message, "console-message".into(), stream)
};
}
}
diff --git a/components/devtools/actors/watcher.rs b/components/devtools/actors/watcher.rs
index b0b2c755fd8..061ffc92336 100644
--- a/components/devtools/actors/watcher.rs
+++ b/components/devtools/actors/watcher.rs
@@ -31,7 +31,7 @@ use crate::actors::watcher::thread_configuration::{
ThreadConfigurationActor, ThreadConfigurationActorMsg,
};
use crate::protocol::JsonPacketStream;
-use crate::resource::{ResourceAvailable, ResourceAvailableReply};
+use crate::resource::ResourceAvailable;
use crate::{EmptyReplyMsg, StreamId, WorkerActor};
pub mod network_parent;
@@ -291,28 +291,28 @@ impl Actor for WatcherActor {
title: Some(target.title.borrow().clone()),
url: Some(target.url.borrow().clone()),
};
- target.resource_available(event, "document-event".into());
+ target.resource_available(event, "document-event".into(), stream);
}
},
"source" => {
let thread_actor = registry.find::<ThreadActor>(&target.thread);
let sources = thread_actor.source_manager.sources();
- target.resources_available(sources.iter().collect(), "source".into());
+ target.resources_available(
+ sources.iter().collect(),
+ "source".into(),
+ stream,
+ );
for worker_name in &root.workers {
let worker = registry.find::<WorkerActor>(worker_name);
let thread = registry.find::<ThreadActor>(&worker.thread);
let worker_sources = thread.source_manager.sources();
- let msg = ResourceAvailableReply {
- from: worker.name(),
- type_: "resources-available-array".into(),
- array: vec![(
- "source".to_string(),
- worker_sources.iter().cloned().collect(),
- )],
- };
- let _ = stream.write_json_packet(&msg);
+ worker.resources_available(
+ worker_sources.iter().collect(),
+ "source".into(),
+ stream,
+ );
}
},
"console-message" | "error-message" => {},
diff --git a/components/devtools/actors/worker.rs b/components/devtools/actors/worker.rs
index 68ff56fb3b2..f3ca4f2aed7 100644
--- a/components/devtools/actors/worker.rs
+++ b/components/devtools/actors/worker.rs
@@ -17,7 +17,7 @@ use servo_url::ServoUrl;
use crate::StreamId;
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::protocol::JsonPacketStream;
-use crate::resource::{ResourceAvailable, ResourceAvailableReply};
+use crate::resource::ResourceAvailable;
#[derive(Clone, Copy)]
#[allow(dead_code)]
@@ -60,10 +60,6 @@ impl ResourceAvailable for WorkerActor {
fn actor_name(&self) -> String {
self.name.clone()
}
-
- fn get_streams(&self) -> &RefCell<HashMap<StreamId, TcpStream>> {
- &self.streams
- }
}
impl Actor for WorkerActor {
@@ -133,28 +129,6 @@ impl Actor for WorkerActor {
}
}
-impl WorkerActor {
- pub(crate) fn resource_available<T: Serialize>(&self, resource: T, resource_type: String) {
- self.resources_available(vec![resource], resource_type);
- }
-
- pub(crate) fn resources_available<T: Serialize>(
- &self,
- resources: Vec<T>,
- resource_type: String,
- ) {
- let msg = ResourceAvailableReply::<T> {
- from: self.name(),
- type_: "resources-available-array".into(),
- array: vec![(resource_type, resources)],
- };
-
- for stream in self.streams.borrow_mut().values_mut() {
- let _ = stream.write_json_packet(&msg);
- }
- }
-}
-
#[derive(Serialize)]
struct DetachedReply {
from: String,
diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs
index 5fb9485e9d3..d097cb25e9d 100644
--- a/components/devtools/lib.rs
+++ b/components/devtools/lib.rs
@@ -414,7 +414,7 @@ impl DevtoolsInstance {
}
fn handle_page_error(
- &self,
+ &mut self,
pipeline_id: PipelineId,
worker_id: Option<WorkerId>,
page_error: PageError,
@@ -426,11 +426,13 @@ impl DevtoolsInstance {
let actors = self.actors.lock().unwrap();
let console_actor = actors.find::<ConsoleActor>(&console_actor_name);
let id = worker_id.map_or(UniqueId::Pipeline(pipeline_id), UniqueId::Worker);
- console_actor.handle_page_error(page_error, id, &actors);
+ for stream in self.connections.values_mut() {
+ console_actor.handle_page_error(page_error.clone(), id.clone(), &actors, stream);
+ }
}
fn handle_console_message(
- &self,
+ &mut self,
pipeline_id: PipelineId,
worker_id: Option<WorkerId>,
console_message: ConsoleMessage,
@@ -442,7 +444,9 @@ impl DevtoolsInstance {
let actors = self.actors.lock().unwrap();
let console_actor = actors.find::<ConsoleActor>(&console_actor_name);
let id = worker_id.map_or(UniqueId::Pipeline(pipeline_id), UniqueId::Worker);
- console_actor.handle_console_api(console_message, id, &actors);
+ for stream in self.connections.values_mut() {
+ console_actor.handle_console_api(console_message.clone(), id.clone(), &actors, stream);
+ }
}
fn find_console_actor(
@@ -529,7 +533,10 @@ impl DevtoolsInstance {
};
let worker_actor = actors.find::<WorkerActor>(worker_actor_name);
- worker_actor.resource_available(source, "source".into());
+
+ for stream in self.connections.values_mut() {
+ worker_actor.resource_available(&source, "source".into(), stream);
+ }
} else {
let Some(browsing_context_id) = self.pipelines.get(&pipeline_id) else {
return;
@@ -556,7 +563,10 @@ impl DevtoolsInstance {
// Notify browsing context about the new source
let browsing_context = actors.find::<BrowsingContextActor>(actor_name);
- browsing_context.resource_available(source, "source".into());
+
+ for stream in self.connections.values_mut() {
+ browsing_context.resource_available(&source, "source".into(), stream);
+ }
}
}
}
diff --git a/components/devtools/resource.rs b/components/devtools/resource.rs
index 7cef8188cc8..4e6aa4042b8 100644
--- a/components/devtools/resource.rs
+++ b/components/devtools/resource.rs
@@ -2,13 +2,10 @@
* 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 std::cell::RefCell;
-use std::collections::HashMap;
use std::net::TcpStream;
use serde::Serialize;
-use crate::StreamId;
use crate::protocol::JsonPacketStream;
#[derive(Serialize)]
@@ -22,21 +19,27 @@ pub(crate) struct ResourceAvailableReply<T: Serialize> {
pub(crate) trait ResourceAvailable {
fn actor_name(&self) -> String;
- fn get_streams(&self) -> &RefCell<HashMap<StreamId, TcpStream>>;
-
- fn resource_available<T: Serialize>(&self, resource: T, resource_type: String) {
- self.resources_available(vec![resource], resource_type);
+ fn resource_available<T: Serialize>(
+ &self,
+ resource: T,
+ resource_type: String,
+ stream: &mut TcpStream,
+ ) {
+ self.resources_available(vec![resource], resource_type, stream);
}
- fn resources_available<T: Serialize>(&self, resources: Vec<T>, resource_type: String) {
+ fn resources_available<T: Serialize>(
+ &self,
+ resources: Vec<T>,
+ resource_type: String,
+ stream: &mut TcpStream,
+ ) {
let msg = ResourceAvailableReply::<T> {
from: self.actor_name(),
type_: "resources-available-array".into(),
array: vec![(resource_type, resources)],
};
- for stream in self.get_streams().borrow_mut().values_mut() {
- let _ = stream.write_json_packet(&msg);
- }
+ let _ = stream.write_json_packet(&msg);
}
}
diff --git a/components/layout/display_list/mod.rs b/components/layout/display_list/mod.rs
index 186272cda36..d6cbb50e4a1 100644
--- a/components/layout/display_list/mod.rs
+++ b/components/layout/display_list/mod.rs
@@ -153,10 +153,6 @@ pub(crate) struct DisplayListBuilder<'a> {
/// list building functions.
current_clip_chain_id: ClipChainId,
- /// The [OpaqueNode] handle to the node used to paint the page background
- /// if the background was a canvas.
- element_for_canvas_background: OpaqueNode,
-
/// A [LayoutContext] used to get information about the device pixel ratio
/// and get handles to WebRender images.
pub context: &'a LayoutContext<'a>,
@@ -169,6 +165,11 @@ pub(crate) struct DisplayListBuilder<'a> {
/// This data is collected during the traversal of the fragment tree and used
/// to paint the highlight at the very end.
inspector_highlight: Option<InspectorHighlight>,
+
+ /// Whether or not the `<body>` element should be painted. This is false if the root `<html>`
+ /// element inherits the `<body>`'s background to paint the page canvas background.
+ /// See <https://drafts.csswg.org/css-backgrounds/#body-background>.
+ paint_body_background: bool,
}
struct InspectorHighlight {
@@ -218,12 +219,12 @@ impl DisplayList {
current_scroll_node_id: self.compositor_info.root_reference_frame_id,
current_reference_frame_scroll_node_id: self.compositor_info.root_reference_frame_id,
current_clip_chain_id: ClipChainId::INVALID,
- element_for_canvas_background: fragment_tree.canvas_background.from_element,
context,
display_list: self,
inspector_highlight: context
.highlighted_dom_node
.map(InspectorHighlight::for_node),
+ paint_body_background: true,
};
fragment_tree.build_display_list(&mut builder, root_stacking_context);
@@ -999,12 +1000,17 @@ impl<'a> BuilderForBoxFragment<'a> {
}
fn build_background(&mut self, builder: &mut DisplayListBuilder) {
- if self
- .fragment
- .base
- .is_for_node(builder.element_for_canvas_background)
+ let flags = self.fragment.base.flags;
+
+ // The root element's background is painted separately as it might inherit the `<body>`'s
+ // background.
+ if flags.intersects(FragmentFlags::IS_ROOT_ELEMENT) {
+ return;
+ }
+ // If the `<body>` background was inherited by the root element, don't paint it again here.
+ if !builder.paint_body_background &&
+ flags.intersects(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT)
{
- // This background is already painted for the canvas, don’t paint it again here.
return;
}
diff --git a/components/layout/display_list/stacking_context.rs b/components/layout/display_list/stacking_context.rs
index b044b713260..27fa73a680c 100644
--- a/components/layout/display_list/stacking_context.rs
+++ b/components/layout/display_list/stacking_context.rs
@@ -582,15 +582,42 @@ impl StackingContext {
&self,
builder: &mut DisplayListBuilder,
fragment_tree: &crate::FragmentTree,
- containing_block_rect: &PhysicalRect<Au>,
) {
- let style = if let Some(style) = &fragment_tree.canvas_background.style {
- style
- } else {
- // The root element has `display: none`,
- // or the canvas background is taken from `<body>` which has `display: none`
+ let Some(root_fragment) = fragment_tree.root_fragments.iter().find(|fragment| {
+ fragment
+ .base()
+ .is_some_and(|base| base.flags.intersects(FragmentFlags::IS_ROOT_ELEMENT))
+ }) else {
return;
};
+ let root_fragment = match root_fragment {
+ Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment,
+ _ => return,
+ }
+ .borrow();
+
+ let source_style = {
+ // > For documents whose root element is an HTML HTML element or an XHTML html element
+ // > [HTML]: if the computed value of background-image on the root element is none and its
+ // > background-color is transparent, user agents must instead propagate the computed
+ // > values of the background properties from that element’s first HTML BODY or XHTML body
+ // > child element.
+ if root_fragment.style.background_is_transparent() {
+ let body_fragment = fragment_tree.body_fragment();
+ builder.paint_body_background = body_fragment.is_none();
+ body_fragment
+ .map(|body_fragment| body_fragment.borrow().style.clone())
+ .unwrap_or(root_fragment.style.clone())
+ } else {
+ root_fragment.style.clone()
+ }
+ };
+
+ // This can happen if the root fragment does not have a `<body>` child (either because it is
+ // `display: none` or `display: contents`) or if the `<body>`'s background is transparent.
+ if source_style.background_is_transparent() {
+ return;
+ }
// The painting area is theoretically the infinite 2D plane,
// but we need a rectangle with finite coordinates.
@@ -598,14 +625,15 @@ impl StackingContext {
// If the document is smaller than the viewport (and doesn’t scroll),
// we still want to paint the rest of the viewport.
// If it’s larger, we also want to paint areas reachable after scrolling.
- let mut painting_area = fragment_tree
+ let painting_area = fragment_tree
.initial_containing_block
.union(&fragment_tree.scrollable_overflow)
.to_webrender();
- let background_color = style.resolve_color(&style.get_background().background_color);
+ let background_color =
+ source_style.resolve_color(&source_style.get_background().background_color);
if background_color.alpha > 0.0 {
- let common = builder.common_properties(painting_area, style);
+ let common = builder.common_properties(painting_area, &source_style);
let color = super::rgba(background_color);
builder
.display_list
@@ -613,97 +641,14 @@ impl StackingContext {
.push_rect(&common, painting_area, color)
}
- // `background-color` was comparatively easy,
- // but `background-image` needs a positioning area based on the root element.
- // Let’s find the corresponding fragment.
-
- // The fragment generated by the root element is the first one here, unless…
- let first_if_any = self.contents.first().or_else(|| {
- // There wasn’t any `StackingContextFragment` in the root `StackingContext`,
- // because the root element generates a stacking context. Let’s find that one.
- self.real_stacking_contexts_and_positioned_stacking_containers
- .first()
- .and_then(|first_child_stacking_context| {
- first_child_stacking_context.contents.first()
- })
- });
-
- macro_rules! debug_panic {
- ($msg: expr) => {
- if cfg!(debug_assertions) {
- panic!($msg);
- } else {
- warn!($msg);
- return;
- }
- };
- }
-
- let first_stacking_context_fragment = if let Some(first) = first_if_any {
- first
- } else {
- // This should only happen if the root element has `display: none`
- // TODO(servo#30569) revert to debug_panic!() once underlying bug is fixed
- log::warn!(
- "debug assertion failed! `CanvasBackground::for_root_element` should have returned `style: None`",
- );
- return;
- };
-
- let StackingContextContent::Fragment {
- fragment,
- scroll_node_id,
- containing_block,
- ..
- } = first_stacking_context_fragment
- else {
- debug_panic!("Expected a fragment, not a stacking container");
- };
- let box_fragment = match fragment {
- Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment,
- _ => debug_panic!("Expected a box-generated fragment"),
- };
- let box_fragment = &*box_fragment.borrow();
-
- // The `StackingContextFragment` we found is for the root DOM element:
- debug_assert_eq!(
- fragment.tag().map(|tag| tag.node),
- Some(fragment_tree.canvas_background.root_element),
- );
-
- // The root element may have a CSS transform, and we want the canvas’
- // background image to be transformed. To do so, take its `SpatialId`
- // (but not its `ClipId`)
- builder.current_scroll_node_id = *scroll_node_id;
-
- // Now we need express the painting area rectangle in the local coordinate system,
- // which differs from the top-level coordinate system based on…
-
- // Convert the painting area rectangle to the local coordinate system of this `SpatialId`
- if let Some(reference_frame_data) =
- box_fragment.reference_frame_data_if_necessary(containing_block_rect)
- {
- painting_area.min -= reference_frame_data.origin.to_webrender().to_vector();
- if let Some(transformed) = reference_frame_data
- .transform
- .inverse()
- .and_then(|inversed| inversed.outer_transformed_rect(&painting_area.to_rect()))
- {
- painting_area = transformed.to_box2d();
- } else {
- // The desired rect cannot be represented, so skip painting this background-image
- return;
- }
- }
-
let mut fragment_builder = BuilderForBoxFragment::new(
- box_fragment,
- containing_block,
+ &root_fragment,
+ &fragment_tree.initial_containing_block,
false, /* is_hit_test_for_scrollable_overflow */
false, /* is_collapsed_table_borders */
);
let painter = super::background::BackgroundPainter {
- style,
+ style: &source_style,
painting_area_override: Some(painting_area),
positioning_area_override: None,
};
diff --git a/components/layout/flow/mod.rs b/components/layout/flow/mod.rs
index 0c326c4cc6d..e23193f3904 100644
--- a/components/layout/flow/mod.rs
+++ b/components/layout/flow/mod.rs
@@ -52,7 +52,7 @@ pub mod inline;
mod root;
pub(crate) use construct::BlockContainerBuilder;
-pub use root::{BoxTree, CanvasBackground};
+pub use root::BoxTree;
#[derive(Debug, MallocSizeOf)]
pub(crate) struct BlockFormattingContext {
diff --git a/components/layout/flow/root.rs b/components/layout/flow/root.rs
index e813777b6fe..ec85f3574dc 100644
--- a/components/layout/flow/root.rs
+++ b/components/layout/flow/root.rs
@@ -12,7 +12,7 @@ use script_layout_interface::wrapper_traits::{
};
use script_layout_interface::{LayoutElementType, LayoutNodeType};
use servo_arc::Arc;
-use style::dom::{NodeInfo, OpaqueNode, TNode};
+use style::dom::{NodeInfo, TNode};
use style::properties::ComputedValues;
use style::values::computed::Overflow;
use style_traits::CSSPixel;
@@ -30,7 +30,7 @@ use crate::fragment_tree::FragmentTree;
use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::replaced::ReplacedContents;
-use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayInside};
+use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside};
use crate::taffy::{TaffyItemBox, TaffyItemBoxInner};
use crate::{DefiniteContainingBlock, PropagatedBoxTreeData};
@@ -40,9 +40,6 @@ pub struct BoxTree {
/// There may be zero if that element has `display: none`.
root: BlockFormattingContext,
- /// <https://drafts.csswg.org/css-backgrounds/#special-backgrounds>
- canvas_background: CanvasBackground,
-
/// Whether or not the viewport should be sensitive to scrolling input events in two axes
viewport_scroll_sensitivity: AxesScrollSensitivity,
}
@@ -96,7 +93,6 @@ impl BoxTree {
contents,
contains_floats,
},
- canvas_background: CanvasBackground::for_root_element(context, root_element),
// From https://www.w3.org/TR/css-overflow-3/#overflow-propagation:
// > If visible is applied to the viewport, it must be interpreted as auto.
// > If clip is applied to the viewport, it must be interpreted as hidden.
@@ -425,69 +421,7 @@ impl BoxTree {
root_fragments,
scrollable_overflow,
physical_containing_block,
- self.canvas_background.clone(),
self.viewport_scroll_sensitivity,
)
}
}
-
-/// <https://drafts.csswg.org/css-backgrounds/#root-background>
-#[derive(Clone, MallocSizeOf)]
-pub struct CanvasBackground {
- /// DOM node for the root element
- pub root_element: OpaqueNode,
-
- /// The element whose style the canvas takes background properties from (see next field).
- /// This can be the root element (same as the previous field), or the HTML `<body>` element.
- /// See <https://drafts.csswg.org/css-backgrounds/#body-background>
- pub from_element: OpaqueNode,
-
- /// The computed styles to take background properties from.
- #[conditional_malloc_size_of]
- pub style: Option<Arc<ComputedValues>>,
-}
-
-impl CanvasBackground {
- fn for_root_element(context: &LayoutContext, root_element: ServoLayoutNode<'_>) -> Self {
- let root_style = root_element.style(context);
-
- let mut style = root_style;
- let mut from_element = root_element;
-
- // https://drafts.csswg.org/css-backgrounds/#body-background
- // “if the computed value of background-image on the root element is none
- // and its background-color is transparent”
- if style.background_is_transparent() &&
- // “For documents whose root element is an HTML `HTML` element
- // or an XHTML `html` element”
- root_element.type_id() == LayoutNodeType::Element(LayoutElementType::HTMLHtmlElement) &&
- // Don’t try to access styles for an unstyled subtree
- !matches!(style.clone_display().into(), Display::None)
- {
- // “that element’s first HTML `BODY` or XHTML `body` child element”
- if let Some(body) = iter_child_nodes(root_element).find(|child| {
- child.is_element() &&
- child.type_id() ==
- LayoutNodeType::Element(LayoutElementType::HTMLBodyElement)
- }) {
- style = body.style(context);
- from_element = body;
- }
- }
-
- Self {
- root_element: root_element.opaque(),
- from_element: from_element.opaque(),
-
- // “However, if no boxes are generated for the element
- // whose background would be used for the canvas
- // (for example, if the root element has display: none),
- // then the canvas background is transparent.”
- style: if let Display::GeneratingBox(_) = style.clone_display().into() {
- Some(style)
- } else {
- None
- },
- }
- }
-}
diff --git a/components/layout/fragment_tree/base_fragment.rs b/components/layout/fragment_tree/base_fragment.rs
index 0cf6ee511cb..ff5df44c225 100644
--- a/components/layout/fragment_tree/base_fragment.rs
+++ b/components/layout/fragment_tree/base_fragment.rs
@@ -32,10 +32,8 @@ impl BaseFragment {
}
}
- /// Returns true if this fragment is non-anonymous and it is for the given
- /// OpaqueNode, regardless of the pseudo element.
- pub(crate) fn is_for_node(&self, node: OpaqueNode) -> bool {
- self.tag.map(|tag| tag.node == node).unwrap_or(false)
+ pub(crate) fn is_anonymous(&self) -> bool {
+ self.tag.is_none()
}
}
diff --git a/components/layout/fragment_tree/box_fragment.rs b/components/layout/fragment_tree/box_fragment.rs
index 4cd8f278498..596556b296c 100644
--- a/components/layout/fragment_tree/box_fragment.rs
+++ b/components/layout/fragment_tree/box_fragment.rs
@@ -16,7 +16,7 @@ use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
use style::values::specified::box_::DisplayOutside;
-use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment};
+use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment, FragmentFlags};
use crate::ArcRefCell;
use crate::display_list::ToWebRender;
use crate::formatting_contexts::Baselines;
@@ -243,6 +243,16 @@ impl BoxFragment {
self.margin + self.border + self.padding
}
+ pub(crate) fn is_root_element(&self) -> bool {
+ self.base.flags.intersects(FragmentFlags::IS_ROOT_ELEMENT)
+ }
+
+ pub(crate) fn is_body_element_of_html_element_root(&self) -> bool {
+ self.base
+ .flags
+ .intersects(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT)
+ }
+
pub fn print(&self, tree: &mut PrintTree) {
tree.new_level(format!(
"Box\
diff --git a/components/layout/fragment_tree/fragment_tree.rs b/components/layout/fragment_tree/fragment_tree.rs
index 1499a50dacf..979bd0090fc 100644
--- a/components/layout/fragment_tree/fragment_tree.rs
+++ b/components/layout/fragment_tree/fragment_tree.rs
@@ -11,10 +11,10 @@ use malloc_size_of_derive::MallocSizeOf;
use style::animation::AnimationSetKey;
use webrender_api::units;
-use super::{ContainingBlockManager, Fragment};
+use super::{BoxFragment, ContainingBlockManager, Fragment};
+use crate::ArcRefCell;
use crate::context::LayoutContext;
use crate::display_list::StackingContext;
-use crate::flow::CanvasBackground;
use crate::geom::PhysicalRect;
#[derive(MallocSizeOf)]
@@ -36,9 +36,6 @@ pub struct FragmentTree {
/// The containing block used in the layout of this fragment tree.
pub(crate) initial_containing_block: PhysicalRect<Au>,
- /// <https://drafts.csswg.org/css-backgrounds/#special-backgrounds>
- pub(crate) canvas_background: CanvasBackground,
-
/// Whether or not the viewport is sensitive to scroll input events.
pub viewport_scroll_sensitivity: AxesScrollSensitivity,
}
@@ -49,14 +46,12 @@ impl FragmentTree {
root_fragments: Vec<Fragment>,
scrollable_overflow: PhysicalRect<Au>,
initial_containing_block: PhysicalRect<Au>,
- canvas_background: CanvasBackground,
viewport_scroll_sensitivity: AxesScrollSensitivity,
) -> Self {
let fragment_tree = Self {
root_fragments,
scrollable_overflow,
initial_containing_block,
- canvas_background,
viewport_scroll_sensitivity,
};
@@ -102,11 +97,7 @@ impl FragmentTree {
root_stacking_context: &StackingContext,
) {
// Paint the canvas’ background (if any) before/under everything else
- root_stacking_context.build_canvas_background_display_list(
- builder,
- self,
- &self.initial_containing_block,
- );
+ root_stacking_context.build_canvas_background_display_list(builder, self);
root_stacking_context.build_display_list(builder);
}
@@ -160,4 +151,45 @@ impl FragmentTree {
scroll_area
}
}
+
+ /// Find the `<body>` element's [`Fragment`], if it exists in this [`FragmentTree`].
+ pub(crate) fn body_fragment(&self) -> Option<ArcRefCell<BoxFragment>> {
+ fn find_body(children: &[Fragment]) -> Option<ArcRefCell<BoxFragment>> {
+ children.iter().find_map(|fragment| {
+ match fragment {
+ Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
+ let borrowed_box_fragment = box_fragment.borrow();
+ if borrowed_box_fragment.is_body_element_of_html_element_root() {
+ return Some(box_fragment.clone());
+ }
+
+ // The fragment for the `<body>` element is typically a child of the root (though,
+ // not if it's absolutely positioned), so we need to recurse into the children of
+ // the root to find it.
+ //
+ // Additionally, recurse into any anonymous fragments, as the `<body>` fragment may
+ // have created anonymous parents (for instance by creating an inline formatting context).
+ if borrowed_box_fragment.is_root_element() ||
+ borrowed_box_fragment.base.is_anonymous()
+ {
+ find_body(&borrowed_box_fragment.children)
+ } else {
+ None
+ }
+ },
+ Fragment::Positioning(positioning_context)
+ if positioning_context.borrow().base.is_anonymous() =>
+ {
+ // If the `<body>` element is a `display: inline` then it might be nested inside of a
+ // `PositioningFragment` for the purposes of putting it on the first line of the implied
+ // inline formatting context.
+ find_body(&positioning_context.borrow().children)
+ },
+ _ => None,
+ }
+ })
+ }
+
+ find_body(&self.root_fragments)
+ }
}
diff --git a/components/script/dom/bindings/buffer_source.rs b/components/script/dom/bindings/buffer_source.rs
index dd6984e1eab..14a71532e9d 100644
--- a/components/script/dom/bindings/buffer_source.rs
+++ b/components/script/dom/bindings/buffer_source.rs
@@ -37,7 +37,7 @@ use js::rust::{
#[cfg(feature = "webgpu")]
use js::typedarray::{ArrayBuffer, HeapArrayBuffer};
use js::typedarray::{
- ArrayBufferU8, ArrayBufferView, ArrayBufferViewU8, CreateWith, TypedArray, TypedArrayElement,
+ ArrayBufferU8, ArrayBufferViewU8, CreateWith, TypedArray, TypedArrayElement,
TypedArrayElementCreator,
};
@@ -63,36 +63,25 @@ pub(crate) enum BufferSource {
ArrayBuffer(Box<Heap<*mut JSObject>>),
}
-pub(crate) fn new_initialized_heap_buffer_source<T>(
- init: HeapTypedArrayInit,
+pub(crate) fn create_heap_buffer_source_with_length<T>(
+ cx: JSContext,
+ len: u32,
can_gc: CanGc,
-) -> Result<HeapBufferSource<T>, ()>
+) -> Fallible<HeapBufferSource<T>>
where
T: TypedArrayElement + TypedArrayElementCreator,
T::Element: Clone + Copy,
{
- let heap_buffer_source = match init {
- HeapTypedArrayInit::Buffer(buffer_source) => HeapBufferSource {
- buffer_source,
- phantom: PhantomData,
- },
- HeapTypedArrayInit::Info { len, cx } => {
- rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
- let typed_array_result =
- create_buffer_source_with_length::<T>(cx, len as usize, array.handle_mut(), can_gc);
- if typed_array_result.is_err() {
- return Err(());
- }
-
- HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(Heap::boxed(*array.handle())))
- },
- };
- Ok(heap_buffer_source)
-}
+ rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
+ let typed_array_result =
+ create_buffer_source_with_length::<T>(cx, len as usize, array.handle_mut(), can_gc);
+ if typed_array_result.is_err() {
+ return Err(Error::JSFailed);
+ }
-pub(crate) enum HeapTypedArrayInit {
- Buffer(BufferSource),
- Info { len: u32, cx: JSContext },
+ Ok(HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(
+ Heap::boxed(*array.handle()),
+ )))
}
pub(crate) struct HeapBufferSource<T> {
@@ -131,11 +120,11 @@ where
}
pub(crate) fn from_view(
- chunk: CustomAutoRooterGuard<ArrayBufferView>,
- ) -> HeapBufferSource<ArrayBufferViewU8> {
- HeapBufferSource::<ArrayBufferViewU8>::new(BufferSource::ArrayBufferView(Heap::boxed(
- unsafe { *chunk.underlying_object() },
- )))
+ chunk: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
+ ) -> HeapBufferSource<T> {
+ HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(Heap::boxed(unsafe {
+ *chunk.underlying_object()
+ })))
}
pub(crate) fn default() -> Self {
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index 7f38e55fb14..7770d0c8fa5 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -12,6 +12,7 @@ use std::rc::Rc;
use std::str::FromStr;
use std::{fmt, mem};
+use content_security_policy as csp;
use cssparser::match_ignore_ascii_case;
use devtools_traits::AttrInfo;
use dom_struct::dom_struct;
@@ -2120,6 +2121,59 @@ impl Element {
node.owner_doc().element_attr_will_change(self, attr);
}
+ /// <https://html.spec.whatwg.org/multipage/#the-style-attribute>
+ fn update_style_attribute(&self, attr: &Attr, mutation: AttributeMutation) {
+ let doc = self.upcast::<Node>().owner_doc();
+ // Modifying the `style` attribute might change style.
+ *self.style_attribute.borrow_mut() = match mutation {
+ AttributeMutation::Set(..) => {
+ // This is the fast path we use from
+ // CSSStyleDeclaration.
+ //
+ // Juggle a bit to keep the borrow checker happy
+ // while avoiding the extra clone.
+ let is_declaration = matches!(*attr.value(), AttrValue::Declaration(..));
+
+ let block = if is_declaration {
+ let mut value = AttrValue::String(String::new());
+ attr.swap_value(&mut value);
+ let (serialization, block) = match value {
+ AttrValue::Declaration(s, b) => (s, b),
+ _ => unreachable!(),
+ };
+ let mut value = AttrValue::String(serialization);
+ attr.swap_value(&mut value);
+ block
+ } else {
+ let win = self.owner_window();
+ let source = &**attr.value();
+ // However, if the Should element's inline behavior be blocked by
+ // Content Security Policy? algorithm returns "Blocked" when executed
+ // upon the attribute's element, "style attribute", and the attribute's value,
+ // then the style rules defined in the attribute's value must not be applied to the element. [CSP]
+ if doc.should_elements_inline_type_behavior_be_blocked(
+ self,
+ csp::InlineCheckType::StyleAttribute,
+ source,
+ ) == csp::CheckResult::Blocked
+ {
+ return;
+ }
+ Arc::new(doc.style_shared_lock().wrap(parse_style_attribute(
+ source,
+ &UrlExtraData(doc.base_url().get_arc()),
+ win.css_error_reporter(),
+ doc.quirks_mode(),
+ CssRuleType::Style,
+ )))
+ };
+
+ Some(block)
+ },
+ AttributeMutation::Removed => None,
+ };
+ }
+
// https://dom.spec.whatwg.org/#insert-adjacent
pub(crate) fn insert_adjacent(
&self,
@@ -3800,43 +3854,7 @@ impl VirtualMethods for Element {
&local_name!("tabindex") | &local_name!("draggable") | &local_name!("hidden") => {
self.update_sequentially_focusable_status(can_gc)
},
- &local_name!("style") => {
- // Modifying the `style` attribute might change style.
- *self.style_attribute.borrow_mut() = match mutation {
- AttributeMutation::Set(..) => {
- // This is the fast path we use from
- // CSSStyleDeclaration.
- //
- // Juggle a bit to keep the borrow checker happy
- // while avoiding the extra clone.
- let is_declaration = matches!(*attr.value(), AttrValue::Declaration(..));
-
- let block = if is_declaration {
- let mut value = AttrValue::String(String::new());
- attr.swap_value(&mut value);
- let (serialization, block) = match value {
- AttrValue::Declaration(s, b) => (s, b),
- _ => unreachable!(),
- };
- let mut value = AttrValue::String(serialization);
- attr.swap_value(&mut value);
- block
- } else {
- let win = self.owner_window();
- Arc::new(doc.style_shared_lock().wrap(parse_style_attribute(
- &attr.value(),
- &UrlExtraData(doc.base_url().get_arc()),
- win.css_error_reporter(),
- doc.quirks_mode(),
- CssRuleType::Style,
- )))
- };
-
- Some(block)
- },
- AttributeMutation::Removed => None,
- };
- },
+ &local_name!("style") => self.update_style_attribute(attr, mutation),
&local_name!("id") => {
*self.id_attribute.borrow_mut() = mutation.new_value(attr).and_then(|value| {
let value = value.as_atom();
diff --git a/components/script/dom/imagedata.rs b/components/script/dom/imagedata.rs
index bd45a80fce2..a891064952a 100644
--- a/components/script/dom/imagedata.rs
+++ b/components/script/dom/imagedata.rs
@@ -9,13 +9,13 @@ use std::vec::Vec;
use dom_struct::dom_struct;
use euclid::default::{Rect, Size2D};
use ipc_channel::ipc::IpcSharedMemory;
-use js::jsapi::{Heap, JSObject};
+use js::gc::CustomAutoRooterGuard;
+use js::jsapi::JSObject;
use js::rust::HandleObject;
use js::typedarray::{ClampedU8, CreateWith, Uint8ClampedArray};
-use super::bindings::buffer_source::{
- BufferSource, HeapBufferSource, HeapTypedArrayInit, new_initialized_heap_buffer_source,
-};
+use super::bindings::buffer_source::{HeapBufferSource, create_heap_buffer_source_with_length};
+use crate::dom::bindings::buffer_source::create_buffer_source;
use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::ImageDataMethods;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
@@ -55,31 +55,31 @@ impl ImageData {
rooted!(in (*cx) let mut js_object = ptr::null_mut::<JSObject>());
if let Some(ref mut d) = data {
d.resize(len as usize, 0);
+
+ let typed_array =
+ create_buffer_source::<ClampedU8>(cx, &d[..], js_object.handle_mut(), can_gc)
+ .map_err(|_| Error::JSFailed)?;
+
let data = CreateWith::Slice(&d[..]);
Uint8ClampedArray::create(*cx, data, js_object.handle_mut()).unwrap();
- Self::new_with_jsobject(global, None, width, Some(height), js_object.get(), can_gc)
+ auto_root!(in(*cx) let data = typed_array);
+ Self::new_with_data(global, None, width, Some(height), data, can_gc)
} else {
- Self::new_without_jsobject(global, None, width, height, can_gc)
+ Self::new_without_data(global, None, width, height, can_gc)
}
}
}
#[allow(unsafe_code)]
- fn new_with_jsobject(
+ fn new_with_data(
global: &GlobalScope,
proto: Option<HandleObject>,
width: u32,
opt_height: Option<u32>,
- jsobject: *mut JSObject,
+ data: CustomAutoRooterGuard<Uint8ClampedArray>,
can_gc: CanGc,
) -> Fallible<DomRoot<ImageData>> {
- let heap_typed_array = match new_initialized_heap_buffer_source::<ClampedU8>(
- HeapTypedArrayInit::Buffer(BufferSource::ArrayBufferView(Heap::boxed(jsobject))),
- can_gc,
- ) {
- Ok(heap_typed_array) => heap_typed_array,
- Err(_) => return Err(Error::JSFailed),
- };
+ let heap_typed_array = HeapBufferSource::<ClampedU8>::from_view(data);
let typed_array = match heap_typed_array.get_typed_array() {
Ok(array) => array,
@@ -117,13 +117,14 @@ impl ImageData {
))
}
- fn new_without_jsobject(
+ fn new_without_data(
global: &GlobalScope,
proto: Option<HandleObject>,
width: u32,
height: u32,
can_gc: CanGc,
) -> Fallible<DomRoot<ImageData>> {
+ // If one or both of sw and sh are zero, then throw an "IndexSizeError" DOMException.
if width == 0 || height == 0 {
return Err(Error::IndexSize);
}
@@ -139,13 +140,8 @@ impl ImageData {
let cx = GlobalScope::get_cx();
- let heap_typed_array = match new_initialized_heap_buffer_source::<ClampedU8>(
- HeapTypedArrayInit::Info { len, cx },
- can_gc,
- ) {
- Ok(heap_typed_array) => heap_typed_array,
- Err(_) => return Err(Error::JSFailed),
- };
+ let heap_typed_array = create_heap_buffer_source_with_length::<ClampedU8>(cx, len, can_gc)?;
+
let imagedata = Box::new(ImageData {
reflector_: Reflector::new(),
width,
@@ -198,20 +194,19 @@ impl ImageDataMethods<crate::DomTypeHolder> for ImageData {
width: u32,
height: u32,
) -> Fallible<DomRoot<Self>> {
- Self::new_without_jsobject(global, proto, width, height, can_gc)
+ Self::new_without_data(global, proto, width, height, can_gc)
}
/// <https://html.spec.whatwg.org/multipage/#dom-imagedata-with-data>
fn Constructor_(
- _cx: JSContext,
global: &GlobalScope,
proto: Option<HandleObject>,
can_gc: CanGc,
- jsobject: *mut JSObject,
+ data: CustomAutoRooterGuard<Uint8ClampedArray>,
width: u32,
opt_height: Option<u32>,
) -> Fallible<DomRoot<Self>> {
- Self::new_with_jsobject(global, proto, width, opt_height, jsobject, can_gc)
+ Self::new_with_data(global, proto, width, opt_height, data, can_gc)
}
/// <https://html.spec.whatwg.org/multipage/#dom-imagedata-width>
diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs
index f90f3024a70..330ae4f0788 100644
--- a/components/script/webdriver_handlers.rs
+++ b/components/script/webdriver_handlers.rs
@@ -199,7 +199,7 @@ unsafe fn is_arguments_object(cx: *mut JSContext, value: HandleValue) -> bool {
jsstring_to_str(cx, class_name) == "[object Arguments]"
}
-#[derive(Eq, Hash, PartialEq)]
+#[derive(Clone, Eq, Hash, PartialEq)]
struct HashableJSVal(u64);
impl From<HandleValue<'_>> for HashableJSVal {
@@ -209,6 +209,7 @@ impl From<HandleValue<'_>> for HashableJSVal {
}
#[allow(unsafe_code)]
+/// <https://w3c.github.io/webdriver/#dfn-json-deserialize>
pub(crate) fn jsval_to_webdriver(
cx: SafeJSContext,
global_scope: &GlobalScope,
@@ -231,12 +232,6 @@ unsafe fn jsval_to_webdriver_inner(
val: HandleValue,
seen: &mut HashSet<HashableJSVal>,
) -> WebDriverJSResult {
- let hashable = val.into();
- if seen.contains(&hashable) {
- return Err(WebDriverJSError::JSError);
- }
- seen.insert(hashable);
-
let _ac = enter_realm(global_scope);
if val.get().is_undefined() {
Ok(WebDriverJSValue::Undefined)
@@ -268,14 +263,26 @@ unsafe fn jsval_to_webdriver_inner(
_ => unreachable!(),
};
Ok(WebDriverJSValue::String(String::from(string)))
- } else if val.get().is_object() {
+ }
+ // https://w3c.github.io/webdriver/#dfn-clone-an-object
+ else if val.get().is_object() {
+ let hashable = val.into();
+ // Step 1. If value is in `seen`, return error with error code javascript error.
+ if seen.contains(&hashable) {
+ return Err(WebDriverJSError::JSError);
+ }
+ //Step 2. Append value to `seen`.
+ seen.insert(hashable.clone());
+
rooted!(in(cx) let object = match FromJSValConvertible::from_jsval(cx, val, ()).unwrap() {
ConversionResult::Success(object) => object,
_ => unreachable!(),
});
let _ac = JSAutoRealm::new(cx, *object);
- if is_array_like::<crate::DomTypeHolder>(cx, val) || is_arguments_object(cx, val) {
+ let return_val = if is_array_like::<crate::DomTypeHolder>(cx, val) ||
+ is_arguments_object(cx, val)
+ {
let mut result: Vec<WebDriverJSValue> = Vec::new();
let length = match get_property::<u32>(
@@ -298,7 +305,7 @@ unsafe fn jsval_to_webdriver_inner(
return Err(WebDriverJSError::JSError);
},
};
-
+ // Step 4. For each enumerable property in value, run the following substeps:
for i in 0..length {
rooted!(in(cx) let mut item = UndefinedValue());
match get_property_jsval(cx, object.handle(), &i.to_string(), item.handle_mut()) {
@@ -319,7 +326,6 @@ unsafe fn jsval_to_webdriver_inner(
},
}
}
-
Ok(WebDriverJSValue::ArrayLike(result))
} else if let Ok(element) = root_from_object::<Element>(*object, cx) {
Ok(WebDriverJSValue::Element(WebElement(
@@ -328,7 +334,7 @@ unsafe fn jsval_to_webdriver_inner(
} else if let Ok(window) = root_from_object::<Window>(*object, cx) {
let window_proxy = window.window_proxy();
if window_proxy.is_browsing_context_discarded() {
- Err(WebDriverJSError::StaleElementReference)
+ return Err(WebDriverJSError::StaleElementReference);
} else if window_proxy.browsing_context_id() == window_proxy.webview_id() {
Ok(WebDriverJSValue::Window(WebWindow(
window.Document().upcast::<Node>().unique_id(),
@@ -348,7 +354,12 @@ unsafe fn jsval_to_webdriver_inner(
&HandleValueArray::empty(),
value.handle_mut(),
) {
- jsval_to_webdriver_inner(cx, global_scope, value.handle(), seen)
+ Ok(jsval_to_webdriver_inner(
+ cx,
+ global_scope,
+ value.handle(),
+ seen,
+ )?)
} else {
throw_dom_exception(
SafeJSContext::from_ptr(cx),
@@ -356,7 +367,7 @@ unsafe fn jsval_to_webdriver_inner(
Error::JSFailed,
CanGc::note(),
);
- Err(WebDriverJSError::JSError)
+ return Err(WebDriverJSError::JSError);
}
} else {
let mut result = HashMap::new();
@@ -408,9 +419,12 @@ unsafe fn jsval_to_webdriver_inner(
}
}
}
-
Ok(WebDriverJSValue::Object(result))
- }
+ };
+ // Step 5. Remove the last element of `seen`.
+ seen.remove(&hashable);
+ // Step 6. Return success with data `result`.
+ return_val
} else {
Err(WebDriverJSError::UnknownType)
}
diff --git a/components/script_bindings/webidls/CanvasRenderingContext2D.webidl b/components/script_bindings/webidls/CanvasRenderingContext2D.webidl
index 0c4960fe6ad..46d91683577 100644
--- a/components/script_bindings/webidls/CanvasRenderingContext2D.webidl
+++ b/components/script_bindings/webidls/CanvasRenderingContext2D.webidl
@@ -254,7 +254,7 @@ interface CanvasPattern {
Serializable]
interface ImageData {
[Throws] constructor(unsigned long sw, unsigned long sh/*, optional ImageDataSettings settings = {}*/);
- [Throws] constructor(/* Uint8ClampedArray */ object data, unsigned long sw, optional unsigned long sh
+ [Throws] constructor(Uint8ClampedArray data, unsigned long sw, optional unsigned long sh
/*, optional ImageDataSettings settings = {}*/);
readonly attribute unsigned long width;