diff options
Diffstat (limited to 'components')
-rw-r--r-- | components/config/prefs.rs | 3 | ||||
-rw-r--r-- | components/devtools/actors/browsing_context.rs | 4 | ||||
-rw-r--r-- | components/devtools/actors/console.rs | 10 | ||||
-rw-r--r-- | components/devtools/actors/watcher.rs | 24 | ||||
-rw-r--r-- | components/devtools/actors/worker.rs | 28 | ||||
-rw-r--r-- | components/devtools/lib.rs | 22 | ||||
-rw-r--r-- | components/devtools/resource.rs | 25 | ||||
-rw-r--r-- | components/layout/display_list/mod.rs | 26 | ||||
-rw-r--r-- | components/layout/display_list/stacking_context.rs | 135 | ||||
-rw-r--r-- | components/layout/flow/mod.rs | 2 | ||||
-rw-r--r-- | components/layout/flow/root.rs | 70 | ||||
-rw-r--r-- | components/layout/fragment_tree/base_fragment.rs | 6 | ||||
-rw-r--r-- | components/layout/fragment_tree/box_fragment.rs | 12 | ||||
-rw-r--r-- | components/layout/fragment_tree/fragment_tree.rs | 56 | ||||
-rw-r--r-- | components/script/dom/bindings/buffer_source.rs | 49 | ||||
-rw-r--r-- | components/script/dom/element.rs | 92 | ||||
-rw-r--r-- | components/script/dom/imagedata.rs | 49 | ||||
-rw-r--r-- | components/script/webdriver_handlers.rs | 46 | ||||
-rw-r--r-- | components/script_bindings/webidls/CanvasRenderingContext2D.webidl | 2 |
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; |