diff --git a/Cargo.lock b/Cargo.lock index 3c99cb089ed4c..d945407f3ec30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5242,6 +5242,7 @@ dependencies = [ "accountable-refcell", "app_units", "arrayvec 0.7.1", + "atomic_refcell", "backtrace", "base64 0.10.1", "bitflags", @@ -5263,6 +5264,7 @@ dependencies = [ "euclid", "fnv", "fxhash", + "gfx_traits", "headers", "html5ever", "http", @@ -5295,6 +5297,7 @@ dependencies = [ "phf_shared 0.8.0", "pixels", "profile_traits", + "range", "ref_filter_map", "regex", "script_layout_interface", diff --git a/components/layout/data.rs b/components/layout/data.rs index 65d96bae2441e..7744b08a57316 100644 --- a/components/layout/data.rs +++ b/components/layout/data.rs @@ -4,6 +4,7 @@ use crate::construct::ConstructionResult; use atomic_refcell::AtomicRefCell; +use script_layout_interface::wrapper_traits::LayoutDataTrait; use script_layout_interface::StyleData; pub struct StyleAndLayoutData<'dom> { @@ -14,6 +15,7 @@ pub struct StyleAndLayoutData<'dom> { } /// Data that layout associates with a node. +#[derive(Clone)] pub struct LayoutData { /// The current results of flow construction for this node. This is either a /// flow or a `ConstructionItem`. See comments in `construct.rs` for more @@ -32,9 +34,11 @@ pub struct LayoutData { pub flags: LayoutDataFlags, } -impl LayoutData { +impl LayoutDataTrait for LayoutData {} + +impl Default for LayoutData { /// Creates new layout data. - pub fn new() -> LayoutData { + fn default() -> LayoutData { Self { flow_construction_result: ConstructionResult::None, before_flow_construction_result: ConstructionResult::None, diff --git a/components/layout_2020/element_data.rs b/components/layout_2020/element_data.rs index f0b1b1984322b..8963f191d6f6b 100644 --- a/components/layout_2020/element_data.rs +++ b/components/layout_2020/element_data.rs @@ -6,6 +6,7 @@ use crate::cell::ArcRefCell; use crate::flexbox::FlexLevelBox; use crate::flow::inline::InlineLevelBox; use crate::flow::BlockLevelBox; +use script_layout_interface::wrapper_traits::LayoutDataTrait; #[derive(Default)] pub struct LayoutDataForElement { @@ -20,3 +21,5 @@ pub(super) enum LayoutBox { InlineLevel(ArcRefCell), FlexLevel(ArcRefCell), } + +impl LayoutDataTrait for LayoutDataForElement {} diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index d6fb814b758eb..bf4ff1b85bc7a 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -11,8 +11,6 @@ #[macro_use] extern crate crossbeam_channel; #[macro_use] -extern crate html5ever; -#[macro_use] extern crate layout; #[macro_use] extern crate lazy_static; @@ -21,9 +19,6 @@ extern crate log; #[macro_use] extern crate profile_traits; -mod dom_wrapper; - -use crate::dom_wrapper::{ServoLayoutDocument, ServoLayoutElement, ServoLayoutNode}; use app_units::Au; use crossbeam_channel::{Receiver, Sender}; use embedder_traits::resources::{self, Resource}; @@ -47,7 +42,6 @@ use layout::display_list::{IndexableText, ToLayout}; use layout::flow::{Flow, FlowFlags, GetBaseFlow, ImmutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow_ref::FlowRef; use layout::incremental::{RelayoutMode, SpecialRestyleDamage}; -use layout::layout_debug; use layout::parallel; use layout::query::{ process_client_rect_query, process_content_box_request, process_content_boxes_request, @@ -62,6 +56,7 @@ use layout::traversal::{ RecalcStyleAndConstructFlows, }; use layout::wrapper::LayoutNodeLayoutData; +use layout::{layout_debug, LayoutData}; use layout_traits::LayoutThreadFactory; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use metrics::{PaintTimeMetrics, ProfilerMetadataFactory, ProgressiveWebMetric}; @@ -75,6 +70,7 @@ use parking_lot::RwLock; use profile_traits::mem::{self as profile_mem, Report, ReportKind, ReportsChan}; use profile_traits::time::{self as profile_time, profile, TimerMetadata}; use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType}; +use script::layout_dom::{ServoLayoutDocument, ServoLayoutElement, ServoLayoutNode}; use script_layout_interface::message::{LayoutThreadInit, Msg, NodesFromPointQueryType, Reflow}; use script_layout_interface::message::{QueryMsg, ReflowComplete, ReflowGoal, ScriptReflow}; use script_layout_interface::rpc::TextIndexResponse; @@ -976,7 +972,7 @@ impl LayoutThread { &self, data: &Reflow, reflow_goal: &ReflowGoal, - document: Option<&ServoLayoutDocument>, + document: Option<&ServoLayoutDocument>, layout_root: &mut dyn Flow, layout_context: &mut LayoutContext, rw_data: &mut LayoutThreadData, @@ -1302,11 +1298,16 @@ impl LayoutThread { let elements_with_snapshot: Vec<_> = restyles .iter() .filter(|r| r.1.snapshot.is_some()) - .map(|r| unsafe { ServoLayoutNode::new(&r.0).as_element().unwrap() }) + .map(|r| unsafe { + ServoLayoutNode::::new(&r.0) + .as_element() + .unwrap() + }) .collect(); for (el, restyle) in restyles { - let el = unsafe { ServoLayoutNode::new(&el).as_element().unwrap() }; + let el: ServoLayoutElement = + unsafe { ServoLayoutNode::new(&el).as_element().unwrap() }; // If we haven't styled this node yet, we don't need to track a // restyle. @@ -1357,10 +1358,9 @@ impl LayoutThread { let traversal = RecalcStyleAndConstructFlows::new(layout_context); let token = { - let shared = - >::shared_context( - &traversal, - ); + let shared = , + >>::shared_context(&traversal); RecalcStyleAndConstructFlows::pre_traverse(dirty_root, shared) }; @@ -1373,7 +1373,7 @@ impl LayoutThread { || { // Perform CSS selector matching and flow construction. let root = driver::traverse_dom::< - ServoLayoutElement, + ServoLayoutElement, RecalcStyleAndConstructFlows, >(&traversal, token, thread_pool); unsafe { @@ -1482,17 +1482,17 @@ impl LayoutThread { process_node_scroll_area_request(node, root_flow); }, &QueryMsg::NodeScrollIdQuery(node) => { - let node = unsafe { ServoLayoutNode::new(&node) }; + let node: ServoLayoutNode = unsafe { ServoLayoutNode::new(&node) }; rw_data.scroll_id_response = Some(process_node_scroll_id_request(self.id, node)); }, &QueryMsg::ResolvedStyleQuery(node, ref pseudo, ref property) => { - let node = unsafe { ServoLayoutNode::new(&node) }; + let node: ServoLayoutNode = unsafe { ServoLayoutNode::new(&node) }; rw_data.resolved_style_response = process_resolved_style_request(context, node, pseudo, property, root_flow); }, &QueryMsg::ResolvedFontStyleQuery(node, ref property, ref value) => { - let node = unsafe { ServoLayoutNode::new(&node) }; + let node: ServoLayoutNode = unsafe { ServoLayoutNode::new(&node) }; let url = self.url.clone(); rw_data.resolved_font_style_response = process_resolved_font_style_request( context, @@ -1528,7 +1528,7 @@ impl LayoutThread { results.iter().map(|result| result.node).collect() }, &QueryMsg::ElementInnerTextQuery(node) => { - let node = unsafe { ServoLayoutNode::new(&node) }; + let node: ServoLayoutNode = unsafe { ServoLayoutNode::new(&node) }; rw_data.element_inner_text_response = process_element_inner_text_query(node, &rw_data.indexable_text); }, @@ -1633,7 +1633,7 @@ impl LayoutThread { root_flow: &mut FlowRef, data: &Reflow, reflow_goal: &ReflowGoal, - document: Option<&ServoLayoutDocument>, + document: Option<&ServoLayoutDocument>, rw_data: &mut LayoutThreadData, context: &mut LayoutContext, ) { @@ -1742,7 +1742,7 @@ impl LayoutThread { data: &Reflow, mut root_flow: &mut FlowRef, reflow_goal: &ReflowGoal, - document: Option<&ServoLayoutDocument>, + document: Option<&ServoLayoutDocument>, rw_data: &mut LayoutThreadData, layout_context: &mut LayoutContext, ) { diff --git a/components/layout_thread_2020/dom_wrapper.rs b/components/layout_thread_2020/dom_wrapper.rs deleted file mode 100644 index 2b5f30f3cd972..0000000000000 --- a/components/layout_thread_2020/dom_wrapper.rs +++ /dev/null @@ -1,1537 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -//! A safe wrapper for DOM nodes that prevents layout from mutating the DOM, from letting DOM nodes -//! escape, and from generally doing anything that it isn't supposed to. This is accomplished via -//! a simple whitelist of allowed operations, along with some lifetime magic to prevent nodes from -//! escaping. -//! -//! As a security wrapper is only as good as its whitelist, be careful when adding operations to -//! this list. The cardinal rules are: -//! -//! 1. Layout is not allowed to mutate the DOM. -//! -//! 2. Layout is not allowed to see anything with `LayoutDom` in the name, because it could hang -//! onto these objects and cause use-after-free. -//! -//! When implementing wrapper functions, be careful that you do not touch the borrow flags, or you -//! will race and cause spurious thread failure. (Note that I do not believe these races are -//! exploitable, but they'll result in brokenness nonetheless.) -//! -//! Rules of the road for this file: -//! -//! * Do not call any methods on DOM nodes without checking to see whether they use borrow flags. -//! -//! o Instead of `get_attr()`, use `.get_attr_val_for_layout()`. -//! -//! o Instead of `html_element_in_html_document()`, use -//! `html_element_in_html_document_for_layout()`. - -#![allow(unsafe_code)] - -use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; -use gfx_traits::ByteIndex; -use html5ever::{LocalName, Namespace}; -use layout::element_data::LayoutDataForElement; -use layout::wrapper::GetStyleAndLayoutData; -use msg::constellation_msg::{BrowsingContextId, PipelineId}; -use net_traits::image::base::{Image, ImageMetadata}; -use range::Range; -use script::layout_exports::NodeFlags; -use script::layout_exports::ShadowRoot; -use script::layout_exports::{ - CharacterDataTypeId, DocumentFragmentTypeId, ElementTypeId, HTMLElementTypeId, NodeTypeId, - TextTypeId, -}; -use script::layout_exports::{Document, Element, Node, Text}; -use script::layout_exports::{LayoutCharacterDataHelpers, LayoutDocumentHelpers}; -use script::layout_exports::{ - LayoutDom, LayoutElementHelpers, LayoutNodeHelpers, LayoutShadowRootHelpers, -}; -use script_layout_interface::wrapper_traits::{ - DangerousThreadSafeLayoutNode, GetStyleAndOpaqueLayoutData, LayoutNode, -}; -use script_layout_interface::wrapper_traits::{ - PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode, -}; -use script_layout_interface::{ - HTMLCanvasData, HTMLMediaData, LayoutNodeType, StyleAndOpaqueLayoutData, -}; -use script_layout_interface::{SVGSVGData, StyleData, TrustedNodeAddress}; -use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint}; -use selectors::matching::VisitedHandlingMode; -use selectors::matching::{ElementSelectorFlags, MatchingContext, QuirksMode}; -use selectors::sink::Push; -use servo_arc::{Arc, ArcBorrow}; -use servo_atoms::Atom; -use servo_url::ServoUrl; -use std::borrow::Cow; -use std::fmt; -use std::fmt::Debug; -use std::hash::{Hash, Hasher}; -use std::sync::atomic::Ordering; -use std::sync::Arc as StdArc; -use style::animation::AnimationSetKey; -use style::applicable_declarations::ApplicableDeclarationBlock; -use style::attr::AttrValue; -use style::context::SharedStyleContext; -use style::data::ElementData; -use style::dom::{DomChildren, LayoutIterator, NodeInfo, OpaqueNode}; -use style::dom::{TDocument, TElement, TNode, TShadowRoot}; -use style::element_state::*; -use style::font_metrics::ServoMetricsProvider; -use style::media_queries::Device; -use style::properties::{ComputedValues, PropertyDeclarationBlock}; -use style::selector_parser::{extended_filtering, PseudoElement, SelectorImpl}; -use style::selector_parser::{AttrValue as SelectorAttrValue, Lang, NonTSPseudoClass}; -use style::shared_lock::{ - Locked as StyleLocked, SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard, -}; -use style::str::is_whitespace; -use style::stylist::CascadeData; -use style::values::{AtomIdent, AtomString}; -use style::CaseSensitivityExt; - -#[derive(Clone, Copy)] -pub struct ServoLayoutNode<'dom> { - /// The wrapped node. - node: LayoutDom<'dom, Node>, -} - -// Those are supposed to be sound, but they aren't because the entire system -// between script and layout so far has been designed to work around their -// absence. Switching the entire thing to the inert crate infra will help. - -unsafe impl Send for ServoLayoutNode<'_> {} -unsafe impl Sync for ServoLayoutNode<'_> {} - -impl<'ln> Debug for ServoLayoutNode<'ln> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Some(el) = self.as_element() { - el.fmt(f) - } else { - if self.is_text_node() { - write!(f, " ({:#x})", self.opaque().0) - } else { - write!(f, " ({:#x})", self.opaque().0) - } - } - } -} - -impl<'a> PartialEq for ServoLayoutNode<'a> { - #[inline] - fn eq(&self, other: &ServoLayoutNode) -> bool { - self.node == other.node - } -} - -impl<'ln> ServoLayoutNode<'ln> { - fn from_layout_js(n: LayoutDom<'ln, Node>) -> Self { - ServoLayoutNode { node: n } - } - - pub unsafe fn new(address: &TrustedNodeAddress) -> Self { - ServoLayoutNode::from_layout_js(LayoutDom::from_trusted_node_address(*address)) - } - - fn script_type_id(&self) -> NodeTypeId { - self.node.type_id_for_layout() - } -} - -impl<'ln> NodeInfo for ServoLayoutNode<'ln> { - fn is_element(&self) -> bool { - self.node.is_element_for_layout() - } - - fn is_text_node(&self) -> bool { - self.script_type_id() == - NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) - } -} - -#[derive(Clone, Copy, PartialEq)] -pub struct ServoShadowRoot<'dom> { - /// The wrapped shadow root. - shadow_root: LayoutDom<'dom, ShadowRoot>, -} - -impl<'lr> Debug for ServoShadowRoot<'lr> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.as_node().fmt(f) - } -} - -impl<'lr> TShadowRoot for ServoShadowRoot<'lr> { - type ConcreteNode = ServoLayoutNode<'lr>; - - fn as_node(&self) -> Self::ConcreteNode { - ServoLayoutNode::from_layout_js(self.shadow_root.upcast()) - } - - fn host(&self) -> ServoLayoutElement<'lr> { - ServoLayoutElement::from_layout_js(self.shadow_root.get_host_for_layout()) - } - - fn style_data<'a>(&self) -> Option<&'a CascadeData> - where - Self: 'a, - { - Some(&self.shadow_root.get_style_data_for_layout()) - } -} - -impl<'lr> ServoShadowRoot<'lr> { - fn from_layout_js(shadow_root: LayoutDom<'lr, ShadowRoot>) -> Self { - ServoShadowRoot { shadow_root } - } - - pub unsafe fn flush_stylesheets( - &self, - device: &Device, - quirks_mode: QuirksMode, - guard: &SharedRwLockReadGuard, - ) { - self.shadow_root - .flush_stylesheets::(device, quirks_mode, guard) - } -} - -impl<'ln> TNode for ServoLayoutNode<'ln> { - type ConcreteDocument = ServoLayoutDocument<'ln>; - type ConcreteElement = ServoLayoutElement<'ln>; - type ConcreteShadowRoot = ServoShadowRoot<'ln>; - - fn parent_node(&self) -> Option { - self.node - .composed_parent_node_ref() - .map(Self::from_layout_js) - } - - fn first_child(&self) -> Option { - self.node.first_child_ref().map(Self::from_layout_js) - } - - fn last_child(&self) -> Option { - self.node.last_child_ref().map(Self::from_layout_js) - } - - fn prev_sibling(&self) -> Option { - self.node.prev_sibling_ref().map(Self::from_layout_js) - } - - fn next_sibling(&self) -> Option { - self.node.next_sibling_ref().map(Self::from_layout_js) - } - - fn owner_doc(&self) -> Self::ConcreteDocument { - ServoLayoutDocument::from_layout_js(self.node.owner_doc_for_layout()) - } - - fn traversal_parent(&self) -> Option> { - let parent = self.parent_node()?; - if let Some(shadow) = parent.as_shadow_root() { - return Some(shadow.host()); - }; - parent.as_element() - } - - fn opaque(&self) -> OpaqueNode { - unsafe { self.get_jsmanaged().opaque() } - } - - fn debug_id(self) -> usize { - self.opaque().0 - } - - fn as_element(&self) -> Option> { - as_element(self.node) - } - - fn as_document(&self) -> Option> { - self.node - .downcast() - .map(ServoLayoutDocument::from_layout_js) - } - - fn as_shadow_root(&self) -> Option> { - self.node.downcast().map(ServoShadowRoot::from_layout_js) - } - - fn is_in_document(&self) -> bool { - unsafe { self.node.get_flag(NodeFlags::IS_IN_DOC) } - } -} - -impl<'ln> LayoutNode<'ln> for ServoLayoutNode<'ln> { - type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'ln>; - - fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode { - ServoThreadSafeLayoutNode::new(*self) - } - - fn type_id(&self) -> LayoutNodeType { - self.script_type_id().into() - } - - unsafe fn initialize_data(&self) { - if self.get_style_and_layout_data().is_none() { - let opaque = StyleAndOpaqueLayoutData::new( - StyleData::new(), - AtomicRefCell::new(LayoutDataForElement::default()), - ); - self.init_style_and_opaque_layout_data(opaque); - }; - } - - unsafe fn init_style_and_opaque_layout_data(&self, data: Box) { - self.get_jsmanaged().init_style_and_opaque_layout_data(data); - } - - unsafe fn take_style_and_opaque_layout_data(&self) -> Box { - self.get_jsmanaged().take_style_and_opaque_layout_data() - } - - fn is_connected(&self) -> bool { - unsafe { self.node.get_flag(NodeFlags::IS_CONNECTED) } - } -} - -impl<'dom> GetStyleAndOpaqueLayoutData<'dom> for ServoLayoutNode<'dom> { - fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> { - unsafe { self.get_jsmanaged().get_style_and_opaque_layout_data() } - } -} - -impl<'dom> GetStyleAndOpaqueLayoutData<'dom> for ServoLayoutElement<'dom> { - fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> { - self.as_node().get_style_and_opaque_layout_data() - } -} - -impl<'dom> GetStyleAndOpaqueLayoutData<'dom> for ServoThreadSafeLayoutNode<'dom> { - fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> { - self.node.get_style_and_opaque_layout_data() - } -} - -impl<'dom> GetStyleAndOpaqueLayoutData<'dom> for ServoThreadSafeLayoutElement<'dom> { - fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> { - self.element.as_node().get_style_and_opaque_layout_data() - } -} - -impl<'ln> ServoLayoutNode<'ln> { - /// Returns the interior of this node as a `LayoutDom`. This is highly unsafe for layout to - /// call and as such is marked `unsafe`. - pub unsafe fn get_jsmanaged(&self) -> LayoutDom<'ln, Node> { - self.node - } -} - -// A wrapper around documents that ensures ayout can only ever access safe properties. -#[derive(Clone, Copy)] -pub struct ServoLayoutDocument<'dom> { - document: LayoutDom<'dom, Document>, -} - -impl<'ld> TDocument for ServoLayoutDocument<'ld> { - type ConcreteNode = ServoLayoutNode<'ld>; - - fn as_node(&self) -> Self::ConcreteNode { - ServoLayoutNode::from_layout_js(self.document.upcast()) - } - - fn quirks_mode(&self) -> QuirksMode { - self.document.quirks_mode() - } - - fn is_html_document(&self) -> bool { - self.document.is_html_document_for_layout() - } - - fn shared_lock(&self) -> &StyleSharedRwLock { - self.document.style_shared_lock() - } -} - -impl<'ld> ServoLayoutDocument<'ld> { - pub fn root_element(&self) -> Option> { - self.as_node() - .dom_children() - .flat_map(|n| n.as_element()) - .next() - } - - pub fn needs_paint_from_layout(&self) { - unsafe { self.document.needs_paint_from_layout() } - } - - pub fn will_paint(&self) { - unsafe { self.document.will_paint() } - } - - pub fn style_shared_lock(&self) -> &StyleSharedRwLock { - self.document.style_shared_lock() - } - - pub fn shadow_roots(&self) -> Vec { - unsafe { - self.document - .shadow_roots() - .iter() - .map(|sr| { - debug_assert!(sr.upcast::().get_flag(NodeFlags::IS_CONNECTED)); - ServoShadowRoot::from_layout_js(*sr) - }) - .collect() - } - } - - pub fn flush_shadow_roots_stylesheets( - &self, - device: &Device, - quirks_mode: QuirksMode, - guard: &SharedRwLockReadGuard, - ) { - unsafe { - if !self.document.shadow_roots_styles_changed() { - return; - } - self.document.flush_shadow_roots_stylesheets(); - for shadow_root in self.shadow_roots() { - shadow_root.flush_stylesheets(device, quirks_mode, guard); - } - } - } - - pub fn from_layout_js(doc: LayoutDom<'ld, Document>) -> Self { - ServoLayoutDocument { document: doc } - } -} - -/// A wrapper around elements that ensures layout can only ever access safe properties. -#[derive(Clone, Copy)] -pub struct ServoLayoutElement<'dom> { - element: LayoutDom<'dom, Element>, -} - -impl<'le> fmt::Debug for ServoLayoutElement<'le> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "<{}", self.element.local_name())?; - if let Some(id) = self.id() { - write!(f, " id={}", id)?; - } - write!(f, "> ({:#x})", self.as_node().opaque().0) - } -} - -impl<'dom> ServoLayoutElement<'dom> { - /// Returns true if this element is the body child of an html element root element. - fn is_body_element_of_html_element_root(&self) -> bool { - if self.element.local_name() != &local_name!("body") { - return false; - } - - self.parent_element() - .map(|element| { - element.is_root() && element.element.local_name() == &local_name!("html") - }) - .unwrap_or(false) - } - - /// Returns the parent element of this element, if it has one. - fn parent_element(&self) -> Option { - self.element - .upcast() - .composed_parent_node_ref() - .and_then(as_element) - } - - // Returns true is this is the root element. - fn is_root(&self) -> bool { - match self.as_node().parent_node() { - None => false, - Some(node) => match node.script_type_id() { - NodeTypeId::Document(_) => true, - _ => false, - }, - } - } -} - -impl<'le> TElement for ServoLayoutElement<'le> { - type ConcreteNode = ServoLayoutNode<'le>; - type TraversalChildrenIterator = DomChildren; - - type FontMetricsProvider = ServoMetricsProvider; - - fn as_node(&self) -> ServoLayoutNode<'le> { - ServoLayoutNode::from_layout_js(self.element.upcast()) - } - - fn traversal_children(&self) -> LayoutIterator { - LayoutIterator(if let Some(shadow) = self.shadow_root() { - shadow.as_node().dom_children() - } else { - self.as_node().dom_children() - }) - } - - fn is_html_element(&self) -> bool { - self.element.is_html_element() - } - - fn is_mathml_element(&self) -> bool { - *self.element.namespace() == ns!(mathml) - } - - fn is_svg_element(&self) -> bool { - *self.element.namespace() == ns!(svg) - } - - fn has_part_attr(&self) -> bool { - false - } - - fn exports_any_part(&self) -> bool { - false - } - - fn style_attribute(&self) -> Option>> { - unsafe { - (*self.element.style_attribute()) - .as_ref() - .map(|x| x.borrow_arc()) - } - } - - fn may_have_animations(&self) -> bool { - true - } - - fn animation_rule( - &self, - context: &SharedStyleContext, - ) -> Option>> { - let node = self.as_node(); - let document = node.owner_doc(); - context.animations.get_animation_declarations( - &AnimationSetKey::new_for_non_pseudo(node.opaque()), - context.current_time_for_animations, - document.style_shared_lock(), - ) - } - - fn transition_rule( - &self, - context: &SharedStyleContext, - ) -> Option>> { - let node = self.as_node(); - let document = node.owner_doc(); - context.animations.get_transition_declarations( - &AnimationSetKey::new_for_non_pseudo(node.opaque()), - context.current_time_for_animations, - document.style_shared_lock(), - ) - } - - fn state(&self) -> ElementState { - self.element.get_state_for_layout() - } - - #[inline] - fn has_attr(&self, namespace: &style::Namespace, attr: &style::LocalName) -> bool { - self.get_attr(namespace, attr).is_some() - } - - #[inline] - fn id(&self) -> Option<&Atom> { - unsafe { (*self.element.id_attribute()).as_ref() } - } - - #[inline(always)] - fn each_class(&self, mut callback: F) - where - F: FnMut(&AtomIdent), - { - if let Some(ref classes) = self.element.get_classes_for_layout() { - for class in *classes { - callback(AtomIdent::cast(class)) - } - } - } - - fn has_dirty_descendants(&self) -> bool { - unsafe { - self.as_node() - .node - .get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) - } - } - - fn has_snapshot(&self) -> bool { - unsafe { self.as_node().node.get_flag(NodeFlags::HAS_SNAPSHOT) } - } - - fn handled_snapshot(&self) -> bool { - unsafe { self.as_node().node.get_flag(NodeFlags::HANDLED_SNAPSHOT) } - } - - unsafe fn set_handled_snapshot(&self) { - self.as_node() - .node - .set_flag(NodeFlags::HANDLED_SNAPSHOT, true); - } - - unsafe fn set_dirty_descendants(&self) { - debug_assert!(self.as_node().is_connected()); - self.as_node() - .node - .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true) - } - - unsafe fn unset_dirty_descendants(&self) { - self.as_node() - .node - .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false) - } - - fn store_children_to_process(&self, n: isize) { - let data = self.get_style_data().unwrap(); - data.parallel - .children_to_process - .store(n, Ordering::Relaxed); - } - - fn did_process_child(&self) -> isize { - let data = self.get_style_data().unwrap(); - let old_value = data - .parallel - .children_to_process - .fetch_sub(1, Ordering::Relaxed); - debug_assert!(old_value >= 1); - old_value - 1 - } - - unsafe fn clear_data(&self) { - if self.get_style_and_layout_data().is_some() { - drop(self.as_node().take_style_and_opaque_layout_data()); - } - } - - unsafe fn ensure_data(&self) -> AtomicRefMut { - self.as_node().initialize_data(); - self.mutate_data().unwrap() - } - - /// Whether there is an ElementData container. - fn has_data(&self) -> bool { - self.get_style_data().is_some() - } - - /// Immutably borrows the ElementData. - fn borrow_data(&self) -> Option> { - self.get_style_data().map(|data| data.element_data.borrow()) - } - - /// Mutably borrows the ElementData. - fn mutate_data(&self) -> Option> { - self.get_style_data() - .map(|data| data.element_data.borrow_mut()) - } - - fn skip_item_display_fixup(&self) -> bool { - false - } - - unsafe fn set_selector_flags(&self, flags: ElementSelectorFlags) { - self.element.insert_selector_flags(flags); - } - - fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool { - self.element.has_selector_flags(flags) - } - - fn has_animations(&self, context: &SharedStyleContext) -> bool { - // This is not used for pseudo elements currently so we can pass None. - return self.has_css_animations(context, /* pseudo_element = */ None) || - self.has_css_transitions(context, /* pseudo_element = */ None); - } - - fn has_css_animations( - &self, - context: &SharedStyleContext, - pseudo_element: Option, - ) -> bool { - let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element); - context.animations.has_active_animations(&key) - } - - fn has_css_transitions( - &self, - context: &SharedStyleContext, - pseudo_element: Option, - ) -> bool { - let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element); - context.animations.has_active_transitions(&key) - } - - #[inline] - fn lang_attr(&self) -> Option { - self.get_attr(&ns!(xml), &local_name!("lang")) - .or_else(|| self.get_attr(&ns!(), &local_name!("lang"))) - .map(|v| SelectorAttrValue::from(v as &str)) - } - - fn match_element_lang( - &self, - override_lang: Option>, - value: &Lang, - ) -> bool { - // Servo supports :lang() from CSS Selectors 4, which can take a comma- - // separated list of language tags in the pseudo-class, and which - // performs RFC 4647 extended filtering matching on them. - // - // FIXME(heycam): This is wrong, since extended_filtering accepts - // a string containing commas (separating each language tag in - // a list) but the pseudo-class instead should be parsing and - // storing separate or s for each language tag. - // - // FIXME(heycam): Look at `element`'s document's Content-Language - // HTTP header for language tags to match `value` against. To - // do this, we should make `get_lang_for_layout` return an Option, - // so we can decide when to fall back to the Content-Language check. - let element_lang = match override_lang { - Some(Some(lang)) => lang, - Some(None) => AtomString::default(), - None => AtomString::from(&*self.element.get_lang_for_layout()), - }; - extended_filtering(&element_lang, &*value) - } - - fn is_html_document_body_element(&self) -> bool { - self.is_body_element_of_html_element_root() - } - - fn synthesize_presentational_hints_for_legacy_attributes( - &self, - _visited_handling: VisitedHandlingMode, - hints: &mut V, - ) where - V: Push, - { - self.element - .synthesize_presentational_hints_for_legacy_attributes(hints); - } - - /// The shadow root this element is a host of. - fn shadow_root(&self) -> Option> { - self.element - .get_shadow_root_for_layout() - .map(ServoShadowRoot::from_layout_js) - } - - /// The shadow root which roots the subtree this element is contained in. - fn containing_shadow(&self) -> Option> { - self.element - .upcast() - .containing_shadow_root_for_layout() - .map(ServoShadowRoot::from_layout_js) - } - - fn local_name(&self) -> &LocalName { - self.element.local_name() - } - - fn namespace(&self) -> &Namespace { - self.element.namespace() - } -} - -impl<'le> PartialEq for ServoLayoutElement<'le> { - fn eq(&self, other: &Self) -> bool { - self.as_node() == other.as_node() - } -} - -impl<'le> Hash for ServoLayoutElement<'le> { - fn hash(&self, state: &mut H) { - self.element.hash(state); - } -} - -impl<'le> Eq for ServoLayoutElement<'le> {} - -impl<'le> ServoLayoutElement<'le> { - fn from_layout_js(el: LayoutDom<'le, Element>) -> Self { - ServoLayoutElement { element: el } - } - - #[inline] - fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> { - self.element.get_attr_for_layout(namespace, name) - } - - #[inline] - fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str> { - self.element.get_attr_val_for_layout(namespace, name) - } - - fn get_style_data(&self) -> Option<&StyleData> { - self.get_style_and_opaque_layout_data() - .map(|data| &data.style_data) - } - - pub unsafe fn unset_snapshot_flags(&self) { - self.as_node() - .node - .set_flag(NodeFlags::HAS_SNAPSHOT | NodeFlags::HANDLED_SNAPSHOT, false); - } - - pub unsafe fn set_has_snapshot(&self) { - self.as_node().node.set_flag(NodeFlags::HAS_SNAPSHOT, true); - } -} - -fn as_element<'dom>(node: LayoutDom<'dom, Node>) -> Option> { - node.downcast().map(ServoLayoutElement::from_layout_js) -} - -impl<'le> ::selectors::Element for ServoLayoutElement<'le> { - type Impl = SelectorImpl; - - fn opaque(&self) -> ::selectors::OpaqueElement { - ::selectors::OpaqueElement::new(unsafe { &*(self.as_node().opaque().0 as *const ()) }) - } - - fn parent_element(&self) -> Option> { - self.element - .upcast() - .composed_parent_node_ref() - .and_then(as_element) - } - - fn parent_node_is_shadow_root(&self) -> bool { - match self.as_node().parent_node() { - None => false, - Some(node) => { - node.script_type_id() == - NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot) - }, - } - } - - fn containing_shadow_host(&self) -> Option { - self.containing_shadow().map(|s| s.host()) - } - - fn prev_sibling_element(&self) -> Option> { - let mut node = self.as_node(); - while let Some(sibling) = node.prev_sibling() { - if let Some(element) = sibling.as_element() { - return Some(element); - } - node = sibling; - } - None - } - - fn next_sibling_element(&self) -> Option> { - let mut node = self.as_node(); - while let Some(sibling) = node.next_sibling() { - if let Some(element) = sibling.as_element() { - return Some(element); - } - node = sibling; - } - None - } - - fn attr_matches( - &self, - ns: &NamespaceConstraint<&style::Namespace>, - local_name: &style::LocalName, - operation: &AttrSelectorOperation<&AtomString>, - ) -> bool { - match *ns { - NamespaceConstraint::Specific(ref ns) => self - .get_attr_enum(ns, local_name) - .map_or(false, |value| value.eval_selector(operation)), - NamespaceConstraint::Any => self - .element - .get_attr_vals_for_layout(local_name) - .iter() - .any(|value| value.eval_selector(operation)), - } - } - - fn is_root(&self) -> bool { - ServoLayoutElement::is_root(self) - } - - fn is_empty(&self) -> bool { - self.as_node() - .dom_children() - .all(|node| match node.script_type_id() { - NodeTypeId::Element(..) => false, - NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => { - node.node.downcast().unwrap().data_for_layout().is_empty() - }, - _ => true, - }) - } - - #[inline] - fn has_local_name(&self, name: &LocalName) -> bool { - self.element.local_name() == name - } - - #[inline] - fn has_namespace(&self, ns: &Namespace) -> bool { - self.element.namespace() == ns - } - - #[inline] - fn is_same_type(&self, other: &Self) -> bool { - self.element.local_name() == other.element.local_name() && - self.element.namespace() == other.element.namespace() - } - - fn is_pseudo_element(&self) -> bool { - false - } - - fn match_pseudo_element( - &self, - _pseudo: &PseudoElement, - _context: &mut MatchingContext, - ) -> bool { - false - } - - fn match_non_ts_pseudo_class( - &self, - pseudo_class: &NonTSPseudoClass, - _: &mut MatchingContext, - _: &mut F, - ) -> bool - where - F: FnMut(&Self, ElementSelectorFlags), - { - match *pseudo_class { - // https://github.com/servo/servo/issues/8718 - NonTSPseudoClass::Link | NonTSPseudoClass::AnyLink => self.is_link(), - NonTSPseudoClass::Visited => false, - - NonTSPseudoClass::Lang(ref lang) => self.match_element_lang(None, &*lang), - - NonTSPseudoClass::ServoNonZeroBorder => { - match self - .element - .get_attr_for_layout(&ns!(), &local_name!("border")) - { - None | Some(&AttrValue::UInt(_, 0)) => false, - _ => true, - } - }, - NonTSPseudoClass::ReadOnly => !self - .element - .get_state_for_layout() - .contains(pseudo_class.state_flag()), - - NonTSPseudoClass::Active | - NonTSPseudoClass::Focus | - NonTSPseudoClass::Fullscreen | - NonTSPseudoClass::Hover | - NonTSPseudoClass::Defined | - NonTSPseudoClass::Enabled | - NonTSPseudoClass::Disabled | - NonTSPseudoClass::Checked | - NonTSPseudoClass::Indeterminate | - NonTSPseudoClass::ReadWrite | - NonTSPseudoClass::PlaceholderShown | - NonTSPseudoClass::Target => self - .element - .get_state_for_layout() - .contains(pseudo_class.state_flag()), - } - } - - #[inline] - fn is_link(&self) -> bool { - match self.as_node().script_type_id() { - // https://html.spec.whatwg.org/multipage/#selector-link - NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLAnchorElement, - )) | - NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) | - NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => { - self.element - .get_attr_val_for_layout(&ns!(), &local_name!("href")) - .is_some() - }, - _ => false, - } - } - - #[inline] - fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool { - unsafe { - (*self.element.id_attribute()) - .as_ref() - .map_or(false, |atom| case_sensitivity.eq_atom(atom, id)) - } - } - - #[inline] - fn is_part(&self, _name: &AtomIdent) -> bool { - false - } - - fn imported_part(&self, _: &AtomIdent) -> Option { - None - } - - #[inline] - fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool { - self.element.has_class_for_layout(name, case_sensitivity) - } - - fn is_html_slot_element(&self) -> bool { - self.element.is_html_element() && self.local_name() == &local_name!("slot") - } - - fn is_html_element_in_html_document(&self) -> bool { - self.element.is_html_element() && self.as_node().owner_doc().is_html_document() - } -} - -#[derive(Clone, Copy, Debug)] -pub struct ServoThreadSafeLayoutNode<'ln> { - /// The wrapped node. - node: ServoLayoutNode<'ln>, - - /// The pseudo-element type, with (optionally) - /// a specified display value to override the stylesheet. - pseudo: PseudoElementType, -} - -impl<'a> PartialEq for ServoThreadSafeLayoutNode<'a> { - #[inline] - fn eq(&self, other: &ServoThreadSafeLayoutNode<'a>) -> bool { - self.node == other.node - } -} - -impl<'ln> DangerousThreadSafeLayoutNode<'ln> for ServoThreadSafeLayoutNode<'ln> { - unsafe fn dangerous_first_child(&self) -> Option { - self.get_jsmanaged() - .first_child_ref() - .map(ServoLayoutNode::from_layout_js) - .map(Self::new) - } - unsafe fn dangerous_next_sibling(&self) -> Option { - self.get_jsmanaged() - .next_sibling_ref() - .map(ServoLayoutNode::from_layout_js) - .map(Self::new) - } -} - -impl<'ln> ServoThreadSafeLayoutNode<'ln> { - /// Creates a new `ServoThreadSafeLayoutNode` from the given `ServoLayoutNode`. - pub fn new(node: ServoLayoutNode<'ln>) -> Self { - ServoThreadSafeLayoutNode { - node: node.clone(), - pseudo: PseudoElementType::Normal, - } - } - - /// Returns the interior of this node as a `LayoutDom`. This is highly unsafe for layout to - /// call and as such is marked `unsafe`. - unsafe fn get_jsmanaged(&self) -> LayoutDom<'ln, Node> { - self.node.get_jsmanaged() - } -} - -impl<'ln> NodeInfo for ServoThreadSafeLayoutNode<'ln> { - fn is_element(&self) -> bool { - self.node.is_element() - } - - fn is_text_node(&self) -> bool { - self.node.is_text_node() - } -} - -impl<'ln> ThreadSafeLayoutNode<'ln> for ServoThreadSafeLayoutNode<'ln> { - type ConcreteNode = ServoLayoutNode<'ln>; - type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'ln>; - type ConcreteElement = ServoLayoutElement<'ln>; - type ChildrenIterator = ThreadSafeLayoutNodeChildrenIterator; - - fn opaque(&self) -> OpaqueNode { - unsafe { self.get_jsmanaged().opaque() } - } - - fn type_id(&self) -> Option { - if self.pseudo == PseudoElementType::Normal { - Some(self.node.type_id()) - } else { - None - } - } - - fn parent_style(&self) -> Arc { - let parent = self.node.parent_node().unwrap().as_element().unwrap(); - let parent_data = parent.borrow_data().unwrap(); - parent_data.styles.primary().clone() - } - - fn debug_id(self) -> usize { - self.node.debug_id() - } - - fn children(&self) -> LayoutIterator { - if let Some(shadow) = self.node.as_element().and_then(|e| e.shadow_root()) { - return LayoutIterator(ThreadSafeLayoutNodeChildrenIterator::new( - shadow.as_node().to_threadsafe(), - )); - } - LayoutIterator(ThreadSafeLayoutNodeChildrenIterator::new(*self)) - } - - fn as_element(&self) -> Option> { - self.node - .as_element() - .map(|el| ServoThreadSafeLayoutElement { - element: el, - pseudo: self.pseudo, - }) - } - - fn get_style_and_opaque_layout_data(self) -> Option<&'ln StyleAndOpaqueLayoutData> { - self.node.get_style_and_opaque_layout_data() - } - - fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool { - unsafe { - let text: LayoutDom = match self.get_jsmanaged().downcast() { - Some(text) => text, - None => return false, - }; - - if !is_whitespace(text.upcast().data_for_layout()) { - return false; - } - - // NB: See the rules for `white-space` here: - // - // http://www.w3.org/TR/CSS21/text.html#propdef-white-space - // - // If you implement other values for this property, you will almost certainly - // want to update this check. - !self - .style(context) - .get_inherited_text() - .white_space - .preserve_newlines() - } - } - - unsafe fn unsafe_get(self) -> Self::ConcreteNode { - self.node - } - - fn node_text_content(self) -> Cow<'ln, str> { - unsafe { self.get_jsmanaged().text_content() } - } - - fn selection(&self) -> Option> { - let this = unsafe { self.get_jsmanaged() }; - - this.selection().map(|range| { - Range::new( - ByteIndex(range.start as isize), - ByteIndex(range.len() as isize), - ) - }) - } - - fn image_url(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.image_url() - } - - fn image_density(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.image_density() - } - - fn image_data(&self) -> Option<(Option>, Option)> { - let this = unsafe { self.get_jsmanaged() }; - this.image_data() - } - - fn canvas_data(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.canvas_data() - } - - fn media_data(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.media_data() - } - - fn svg_data(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.svg_data() - } - - // Can return None if the iframe has no nested browsing context - fn iframe_browsing_context_id(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.iframe_browsing_context_id() - } - - // Can return None if the iframe has no nested browsing context - fn iframe_pipeline_id(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.iframe_pipeline_id() - } - - fn get_colspan(&self) -> u32 { - unsafe { - self.get_jsmanaged() - .downcast::() - .unwrap() - .get_colspan() - } - } - - fn get_rowspan(&self) -> u32 { - unsafe { - self.get_jsmanaged() - .downcast::() - .unwrap() - .get_rowspan() - } - } -} - -pub struct ThreadSafeLayoutNodeChildrenIterator { - current_node: Option, - parent_node: ConcreteNode, -} - -impl<'dom, ConcreteNode> ThreadSafeLayoutNodeChildrenIterator -where - ConcreteNode: DangerousThreadSafeLayoutNode<'dom>, -{ - pub fn new(parent: ConcreteNode) -> Self { - let first_child: Option = match parent.get_pseudo_element_type() { - PseudoElementType::Normal => parent - .get_before_pseudo() - .or_else(|| parent.get_details_summary_pseudo()) - .or_else(|| unsafe { parent.dangerous_first_child() }), - PseudoElementType::DetailsContent | PseudoElementType::DetailsSummary => unsafe { - parent.dangerous_first_child() - }, - _ => None, - }; - ThreadSafeLayoutNodeChildrenIterator { - current_node: first_child, - parent_node: parent, - } - } -} - -impl<'dom, ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator -where - ConcreteNode: DangerousThreadSafeLayoutNode<'dom>, -{ - type Item = ConcreteNode; - fn next(&mut self) -> Option { - use selectors::Element; - match self.parent_node.get_pseudo_element_type() { - PseudoElementType::Before | PseudoElementType::After => None, - - PseudoElementType::DetailsSummary => { - let mut current_node = self.current_node.clone(); - loop { - let next_node = if let Some(ref node) = current_node { - if let Some(element) = node.as_element() { - if element.has_local_name(&local_name!("summary")) && - element.has_namespace(&ns!(html)) - { - self.current_node = None; - return Some(node.clone()); - } - } - unsafe { node.dangerous_next_sibling() } - } else { - self.current_node = None; - return None; - }; - current_node = next_node; - } - }, - - PseudoElementType::DetailsContent => { - let node = self.current_node.clone(); - let node = node.and_then(|node| { - if node.is_element() && - node.as_element() - .unwrap() - .has_local_name(&local_name!("summary")) && - node.as_element().unwrap().has_namespace(&ns!(html)) - { - unsafe { node.dangerous_next_sibling() } - } else { - Some(node) - } - }); - self.current_node = node.and_then(|node| unsafe { node.dangerous_next_sibling() }); - node - }, - - PseudoElementType::Normal => { - let node = self.current_node.clone(); - if let Some(ref node) = node { - self.current_node = match node.get_pseudo_element_type() { - PseudoElementType::Before => self - .parent_node - .get_details_summary_pseudo() - .or_else(|| unsafe { self.parent_node.dangerous_first_child() }) - .or_else(|| self.parent_node.get_after_pseudo()), - PseudoElementType::Normal => unsafe { node.dangerous_next_sibling() } - .or_else(|| self.parent_node.get_after_pseudo()), - PseudoElementType::DetailsSummary => { - self.parent_node.get_details_content_pseudo() - }, - PseudoElementType::DetailsContent => self.parent_node.get_after_pseudo(), - PseudoElementType::After => None, - }; - } - node - }, - } - } -} - -/// A wrapper around elements that ensures layout can only -/// ever access safe properties and cannot race on elements. -#[derive(Clone, Copy, Debug)] -pub struct ServoThreadSafeLayoutElement<'le> { - element: ServoLayoutElement<'le>, - - /// The pseudo-element type, with (optionally) - /// a specified display value to override the stylesheet. - pseudo: PseudoElementType, -} - -impl<'le> ThreadSafeLayoutElement<'le> for ServoThreadSafeLayoutElement<'le> { - type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'le>; - type ConcreteElement = ServoLayoutElement<'le>; - - fn as_node(&self) -> ServoThreadSafeLayoutNode<'le> { - ServoThreadSafeLayoutNode { - node: self.element.as_node(), - pseudo: self.pseudo.clone(), - } - } - - fn get_pseudo_element_type(&self) -> PseudoElementType { - self.pseudo - } - - fn with_pseudo(&self, pseudo: PseudoElementType) -> Self { - ServoThreadSafeLayoutElement { - element: self.element.clone(), - pseudo, - } - } - - fn type_id(&self) -> Option { - self.as_node().type_id() - } - - unsafe fn unsafe_get(self) -> ServoLayoutElement<'le> { - self.element - } - - fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> { - self.element.get_attr_enum(namespace, name) - } - - fn get_attr<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str> { - self.element.get_attr(namespace, name) - } - - fn style_data(&self) -> AtomicRef { - match self.element.borrow_data() { - Some(data) => data, - None => panic!("could not find styles for <{}>", self.element.local_name()), - } - } - - fn is_shadow_host(&self) -> bool { - self.element.shadow_root().is_some() - } - - fn is_body_element_of_html_element_root(&self) -> bool { - self.element.is_body_element_of_html_element_root() - } -} - -/// This implementation of `::selectors::Element` is used for implementing lazy -/// pseudo-elements. -/// -/// Lazy pseudo-elements in Servo only allows selectors using safe properties, -/// i.e., local_name, attributes, so they can only be used for **private** -/// pseudo-elements (like `::-servo-details-content`). -/// -/// Probably a few more of this functions can be implemented (like `has_class`, etc.), -/// but they have no use right now. -/// -/// Note that the element implementation is needed only for selector matching, -/// not for inheritance (styles are inherited appropiately). -impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { - type Impl = SelectorImpl; - - fn opaque(&self) -> ::selectors::OpaqueElement { - ::selectors::OpaqueElement::new(unsafe { &*(self.as_node().opaque().0 as *const ()) }) - } - - fn is_pseudo_element(&self) -> bool { - false - } - - fn parent_element(&self) -> Option { - warn!("ServoThreadSafeLayoutElement::parent_element called"); - None - } - - fn parent_node_is_shadow_root(&self) -> bool { - false - } - - fn containing_shadow_host(&self) -> Option { - None - } - - // Skips non-element nodes - fn prev_sibling_element(&self) -> Option { - warn!("ServoThreadSafeLayoutElement::prev_sibling_element called"); - None - } - - // Skips non-element nodes - fn next_sibling_element(&self) -> Option { - warn!("ServoThreadSafeLayoutElement::next_sibling_element called"); - None - } - - fn is_html_slot_element(&self) -> bool { - self.element.is_html_slot_element() - } - - fn is_html_element_in_html_document(&self) -> bool { - debug!("ServoThreadSafeLayoutElement::is_html_element_in_html_document called"); - true - } - - #[inline] - fn has_local_name(&self, name: &LocalName) -> bool { - self.element.local_name() == name - } - - #[inline] - fn has_namespace(&self, ns: &Namespace) -> bool { - self.element.namespace() == ns - } - - #[inline] - fn is_same_type(&self, other: &Self) -> bool { - self.element.local_name() == other.element.local_name() && - self.element.namespace() == other.element.namespace() - } - - fn match_pseudo_element( - &self, - _pseudo: &PseudoElement, - _context: &mut MatchingContext, - ) -> bool { - false - } - - fn attr_matches( - &self, - ns: &NamespaceConstraint<&style::Namespace>, - local_name: &style::LocalName, - operation: &AttrSelectorOperation<&AtomString>, - ) -> bool { - match *ns { - NamespaceConstraint::Specific(ref ns) => self - .get_attr_enum(ns, local_name) - .map_or(false, |value| value.eval_selector(operation)), - NamespaceConstraint::Any => { - let values = self.element.element.get_attr_vals_for_layout(local_name); - values.iter().any(|v| v.eval_selector(operation)) - }, - } - } - - fn match_non_ts_pseudo_class( - &self, - _: &NonTSPseudoClass, - _: &mut MatchingContext, - _: &mut F, - ) -> bool - where - F: FnMut(&Self, ElementSelectorFlags), - { - // NB: This could maybe be implemented - warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called"); - false - } - - fn is_link(&self) -> bool { - warn!("ServoThreadSafeLayoutElement::is_link called"); - false - } - - fn has_id(&self, _id: &AtomIdent, _case_sensitivity: CaseSensitivity) -> bool { - debug!("ServoThreadSafeLayoutElement::has_id called"); - false - } - - #[inline] - fn is_part(&self, _name: &AtomIdent) -> bool { - debug!("ServoThreadSafeLayoutElement::is_part called"); - false - } - - fn imported_part(&self, _: &AtomIdent) -> Option { - debug!("ServoThreadSafeLayoutElement::imported_part called"); - None - } - - fn has_class(&self, _name: &AtomIdent, _case_sensitivity: CaseSensitivity) -> bool { - debug!("ServoThreadSafeLayoutElement::has_class called"); - false - } - - fn is_empty(&self) -> bool { - warn!("ServoThreadSafeLayoutElement::is_empty called"); - false - } - - fn is_root(&self) -> bool { - warn!("ServoThreadSafeLayoutElement::is_root called"); - false - } -} diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index df7f2e908274d..ddfbc69222f23 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -11,17 +11,12 @@ #[macro_use] extern crate crossbeam_channel; #[macro_use] -extern crate html5ever; -#[macro_use] extern crate lazy_static; #[macro_use] extern crate log; #[macro_use] extern crate profile_traits; -mod dom_wrapper; - -use crate::dom_wrapper::{ServoLayoutDocument, ServoLayoutElement, ServoLayoutNode}; use app_units::Au; use crossbeam_channel::{Receiver, Sender}; use embedder_traits::resources::{self, Resource}; @@ -35,6 +30,7 @@ use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use layout::context::LayoutContext; use layout::display_list::{DisplayListBuilder, WebRenderImageInfo}; +use layout::element_data::LayoutDataForElement; use layout::layout_debug; use layout::query::{ process_content_box_request, process_content_boxes_request, process_resolved_font_style_query, @@ -60,6 +56,7 @@ use parking_lot::RwLock; use profile_traits::mem::{self as profile_mem, Report, ReportKind, ReportsChan}; use profile_traits::time::{self as profile_time, profile, TimerMetadata}; use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType}; +use script::layout_dom::{ServoLayoutDocument, ServoLayoutElement, ServoLayoutNode}; use script_layout_interface::message::{LayoutThreadInit, Msg, NodesFromPointQueryType}; use script_layout_interface::message::{QueryMsg, ReflowComplete, ReflowGoal, ScriptReflow}; use script_layout_interface::rpc::TextIndexResponse; @@ -804,7 +801,7 @@ impl LayoutThread { data: &mut ScriptReflowResult, possibly_locked_rw_data: &mut RwData<'a, 'b>, ) { - let document = unsafe { ServoLayoutNode::new(&data.document) }; + let document = unsafe { ServoLayoutNode::::new(&data.document) }; let document = document.as_document().unwrap(); let mut rw_data = possibly_locked_rw_data.lock(); @@ -959,11 +956,19 @@ impl LayoutThread { let elements_with_snapshot: Vec<_> = restyles .iter() .filter(|r| r.1.snapshot.is_some()) - .map(|r| unsafe { ServoLayoutNode::new(&r.0).as_element().unwrap() }) + .map(|r| unsafe { + ServoLayoutNode::::new(&r.0) + .as_element() + .unwrap() + }) .collect(); for (el, restyle) in restyles { - let el = unsafe { ServoLayoutNode::new(&el).as_element().unwrap() }; + let el = unsafe { + ServoLayoutNode::::new(&el) + .as_element() + .unwrap() + }; // If we haven't styled this node yet, we don't need to track a // restyle. @@ -999,14 +1004,16 @@ impl LayoutThread { ); let dirty_root = unsafe { - ServoLayoutNode::new(&data.dirty_root.unwrap()) + ServoLayoutNode::::new(&data.dirty_root.unwrap()) .as_element() .unwrap() }; let traversal = RecalcStyle::new(layout_context); let token = { - let shared = DomTraversal::::shared_context(&traversal); + let shared = DomTraversal::>::shared_context( + &traversal, + ); RecalcStyle::pre_traverse(dirty_root, shared) }; @@ -1014,7 +1021,8 @@ impl LayoutThread { let rayon_pool = rayon_pool.as_ref(); if token.should_traverse() { - let dirty_root = driver::traverse_dom(&traversal, token, rayon_pool).as_node(); + let dirty_root: ServoLayoutNode = + driver::traverse_dom(&traversal, token, rayon_pool).as_node(); let root_node = root_element.as_node(); let mut box_tree = self.box_tree.borrow_mut(); @@ -1126,12 +1134,12 @@ impl LayoutThread { process_node_scroll_area_request(node, self.fragment_tree.borrow().clone()); }, &QueryMsg::NodeScrollIdQuery(node) => { - let node = unsafe { ServoLayoutNode::new(&node) }; + let node = unsafe { ServoLayoutNode::::new(&node) }; rw_data.scroll_id_response = Some(process_node_scroll_id_request(self.id, node)); }, &QueryMsg::ResolvedStyleQuery(node, ref pseudo, ref property) => { - let node = unsafe { ServoLayoutNode::new(&node) }; + let node = unsafe { ServoLayoutNode::::new(&node) }; let fragment_tree = self.fragment_tree.borrow().clone(); rw_data.resolved_style_response = process_resolved_style_request( context, @@ -1142,7 +1150,7 @@ impl LayoutThread { ); }, &QueryMsg::ResolvedFontStyleQuery(node, ref property, ref value) => { - let node = unsafe { ServoLayoutNode::new(&node) }; + let node = unsafe { ServoLayoutNode::::new(&node) }; rw_data.resolved_font_style_response = process_resolved_font_style_query(node, property, value); }, @@ -1172,7 +1180,7 @@ impl LayoutThread { results.iter().map(|result| result.node).collect() }, &QueryMsg::ElementInnerTextQuery(node) => { - let node = unsafe { ServoLayoutNode::new(&node) }; + let node = unsafe { ServoLayoutNode::::new(&node) }; rw_data.element_inner_text_response = process_element_inner_text_query(node); }, &QueryMsg::InnerWindowDimensionsQuery(_browsing_context_id) => { @@ -1232,7 +1240,7 @@ impl LayoutThread { &self, fragment_tree: Arc, reflow_goal: &ReflowGoal, - document: Option<&ServoLayoutDocument>, + document: Option<&ServoLayoutDocument>, context: &mut LayoutContext, ) { Self::cancel_animations_for_nodes_not_in_fragment_tree( diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 94a8a3f33b4e2..bbe1f4f78bc86 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -33,6 +33,7 @@ serde_json = "1.0" accountable-refcell = { version = "0.2.0", optional = true } app_units = "0.7" arrayvec = "0.7" +atomic_refcell = "0.1" backtrace = "0.3" base64 = "0.10.1" bitflags = "1.0" @@ -54,6 +55,7 @@ enum-iterator = "0.3" euclid = "0.22" fnv = "1.0" fxhash = "0.2" +gfx_traits = { path = "../gfx_traits" } headers = "0.3" html5ever = "0.26" http = "0.2" @@ -83,6 +85,7 @@ percent-encoding = "2.0" phf = "0.8" pixels = { path = "../pixels" } profile_traits = { path = "../profile_traits" } +range = { path = "../range" } ref_filter_map = "1.0.1" regex = "1.1" script_layout_interface = { path = "../script_layout_interface" } diff --git a/components/script/layout_dom/document.rs b/components/script/layout_dom/document.rs new file mode 100644 index 0000000000000..e723f731cabe7 --- /dev/null +++ b/components/script/layout_dom/document.rs @@ -0,0 +1,114 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 crate::dom::bindings::root::LayoutDom; +use crate::dom::document::{Document, LayoutDocumentHelpers}; +use crate::dom::node::{LayoutNodeHelpers, Node, NodeFlags}; +use crate::layout_dom::ServoLayoutElement; +use crate::layout_dom::ServoLayoutNode; +use crate::layout_dom::ServoShadowRoot; +use script_layout_interface::wrapper_traits::LayoutDataTrait; +use selectors::matching::QuirksMode; +use std::marker::PhantomData; +use style::dom::{TDocument, TNode}; +use style::media_queries::Device; +use style::shared_lock::{ + SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard as StyleSharedRwLockReadGuard, +}; + +// A wrapper around documents that ensures ayout can only ever access safe properties. +pub struct ServoLayoutDocument<'dom, LayoutDataType: LayoutDataTrait> { + /// The wrapped private DOM Document + document: LayoutDom<'dom, Document>, + + /// A PhantomData that is used to track the type of the stored layout data. + phantom: PhantomData, +} + +impl<'dom, LayoutDataType: LayoutDataTrait> Clone for ServoLayoutDocument<'dom, LayoutDataType> { + fn clone(&self) -> Self { + *self + } +} +impl<'dom, LayoutDataType: LayoutDataTrait> Copy for ServoLayoutDocument<'dom, LayoutDataType> {} + +impl<'ld, LayoutDataType: LayoutDataTrait> ::style::dom::TDocument + for ServoLayoutDocument<'ld, LayoutDataType> +{ + type ConcreteNode = ServoLayoutNode<'ld, LayoutDataType>; + + fn as_node(&self) -> Self::ConcreteNode { + ServoLayoutNode::from_layout_js(self.document.upcast()) + } + + fn quirks_mode(&self) -> QuirksMode { + self.document.quirks_mode() + } + + fn is_html_document(&self) -> bool { + self.document.is_html_document_for_layout() + } + + fn shared_lock(&self) -> &StyleSharedRwLock { + self.document.style_shared_lock() + } +} + +impl<'ld, LayoutDataType: LayoutDataTrait> ServoLayoutDocument<'ld, LayoutDataType> { + pub fn root_element(&self) -> Option> { + self.as_node() + .dom_children() + .flat_map(|n| n.as_element()) + .next() + } + + pub fn needs_paint_from_layout(&self) { + unsafe { self.document.needs_paint_from_layout() } + } + + pub fn will_paint(&self) { + unsafe { self.document.will_paint() } + } + + pub fn style_shared_lock(&self) -> &StyleSharedRwLock { + self.document.style_shared_lock() + } + + pub fn shadow_roots(&self) -> Vec> { + unsafe { + self.document + .shadow_roots() + .iter() + .map(|sr| { + debug_assert!(sr.upcast::().get_flag(NodeFlags::IS_CONNECTED)); + ServoShadowRoot::from_layout_js(*sr) + }) + .collect() + } + } + + pub fn flush_shadow_roots_stylesheets( + &self, + device: &Device, + quirks_mode: QuirksMode, + guard: &StyleSharedRwLockReadGuard, + ) { + unsafe { + if !self.document.shadow_roots_styles_changed() { + return; + } + self.document.flush_shadow_roots_stylesheets(); + for shadow_root in self.shadow_roots() { + shadow_root.flush_stylesheets(device, quirks_mode, guard); + } + } + } + + pub fn from_layout_js(doc: LayoutDom<'ld, Document>) -> Self { + ServoLayoutDocument { + document: doc, + phantom: PhantomData, + } + } +} diff --git a/components/layout_thread/dom_wrapper.rs b/components/script/layout_dom/element.rs similarity index 50% rename from components/layout_thread/dom_wrapper.rs rename to components/script/layout_dom/element.rs index 35e7fa3b8ec73..9b9f8c4790b0e 100644 --- a/components/layout_thread/dom_wrapper.rs +++ b/components/script/layout_dom/element.rs @@ -2,425 +2,127 @@ * 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/. */ -//! A safe wrapper for DOM nodes that prevents layout from mutating the DOM, from letting DOM nodes -//! escape, and from generally doing anything that it isn't supposed to. This is accomplished via -//! a simple whitelist of allowed operations, along with some lifetime magic to prevent nodes from -//! escaping. -//! -//! As a security wrapper is only as good as its whitelist, be careful when adding operations to -//! this list. The cardinal rules are: -//! -//! 1. Layout is not allowed to mutate the DOM. -//! -//! 2. Layout is not allowed to see anything with `LayoutDom` in the name, because it could hang -//! onto these objects and cause use-after-free. -//! -//! When implementing wrapper functions, be careful that you do not touch the borrow flags, or you -//! will race and cause spurious thread failure. (Note that I do not believe these races are -//! exploitable, but they'll result in brokenness nonetheless.) -//! -//! Rules of the road for this file: -//! -//! * Do not call any methods on DOM nodes without checking to see whether they use borrow flags. -//! -//! o Instead of `get_attr()`, use `.get_attr_val_for_layout()`. -//! -//! o Instead of `html_element_in_html_document()`, use -//! `html_element_in_html_document_for_layout()`. - -#![allow(unsafe_code)] - -use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; -use gfx_traits::ByteIndex; -use html5ever::{LocalName, Namespace}; -use layout::data::LayoutData; -use layout::wrapper::GetStyleAndLayoutData; -use msg::constellation_msg::{BrowsingContextId, PipelineId}; -use net_traits::image::base::{Image, ImageMetadata}; -use range::Range; -use script::layout_exports::NodeFlags; -use script::layout_exports::ShadowRoot; -use script::layout_exports::{ - CharacterDataTypeId, DocumentFragmentTypeId, ElementTypeId, HTMLElementTypeId, NodeTypeId, - TextTypeId, -}; -use script::layout_exports::{Document, Element, Node, Text}; -use script::layout_exports::{LayoutCharacterDataHelpers, LayoutDocumentHelpers}; -use script::layout_exports::{ - LayoutDom, LayoutElementHelpers, LayoutNodeHelpers, LayoutShadowRootHelpers, -}; -use script_layout_interface::wrapper_traits::{ - DangerousThreadSafeLayoutNode, GetStyleAndOpaqueLayoutData, LayoutNode, +use crate::dom::bindings::inheritance::{ + CharacterDataTypeId, DocumentFragmentTypeId, ElementTypeId, }; +use crate::dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId, TextTypeId}; +use crate::dom::bindings::root::LayoutDom; +use crate::dom::characterdata::LayoutCharacterDataHelpers; +use crate::dom::element::{Element, LayoutElementHelpers}; +use crate::dom::node::{LayoutNodeHelpers, NodeFlags}; +use atomic_refcell::{AtomicRef, AtomicRefMut}; +use html5ever::{LocalName, Namespace}; use script_layout_interface::wrapper_traits::{ - PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode, -}; -use script_layout_interface::{ - HTMLCanvasData, HTMLMediaData, LayoutNodeType, StyleAndOpaqueLayoutData, + GetStyleAndOpaqueLayoutData, LayoutDataTrait, LayoutNode, PseudoElementType, + ThreadSafeLayoutElement, ThreadSafeLayoutNode, }; -use script_layout_interface::{SVGSVGData, StyleData, TrustedNodeAddress}; +use script_layout_interface::{LayoutNodeType, StyleAndOpaqueLayoutData, StyleData}; use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint}; -use selectors::matching::VisitedHandlingMode; -use selectors::matching::{ElementSelectorFlags, MatchingContext, QuirksMode}; +use selectors::matching::{ElementSelectorFlags, MatchingContext, VisitedHandlingMode}; use selectors::sink::Push; use servo_arc::{Arc, ArcBorrow}; use servo_atoms::Atom; -use servo_url::ServoUrl; -use std::borrow::Cow; use std::fmt; -use std::fmt::Debug; use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; use std::sync::atomic::Ordering; -use std::sync::Arc as StdArc; use style::animation::AnimationSetKey; use style::applicable_declarations::ApplicableDeclarationBlock; use style::attr::AttrValue; use style::context::SharedStyleContext; use style::data::ElementData; -use style::dom::{DomChildren, LayoutIterator, NodeInfo, OpaqueNode}; -use style::dom::{TDocument, TElement, TNode, TShadowRoot}; +use style::dom::{DomChildren, LayoutIterator, TDocument, TElement, TNode, TShadowRoot}; use style::element_state::*; use style::font_metrics::ServoMetricsProvider; -use style::media_queries::Device; -use style::properties::{ComputedValues, PropertyDeclarationBlock}; -use style::selector_parser::{extended_filtering, PseudoElement, SelectorImpl}; -use style::selector_parser::{AttrValue as SelectorAttrValue, Lang, NonTSPseudoClass}; -use style::shared_lock::{ - Locked as StyleLocked, SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard, +use style::properties::PropertyDeclarationBlock; +use style::selector_parser::{ + extended_filtering, AttrValue as SelectorAttrValue, Lang, NonTSPseudoClass, PseudoElement, + SelectorImpl, }; -use style::str::is_whitespace; -use style::stylist::CascadeData; +use style::shared_lock::Locked as StyleLocked; use style::values::{AtomIdent, AtomString}; use style::CaseSensitivityExt; -#[derive(Clone, Copy)] -pub struct ServoLayoutNode<'dom> { - /// The wrapped node. - node: LayoutDom<'dom, Node>, -} - -impl<'ln> Debug for ServoLayoutNode<'ln> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Some(el) = self.as_element() { - el.fmt(f) - } else { - if self.is_text_node() { - write!(f, " ({:#x})", self.opaque().0) - } else { - write!(f, " ({:#x})", self.opaque().0) - } - } - } -} - -impl<'a> PartialEq for ServoLayoutNode<'a> { - #[inline] - fn eq(&self, other: &ServoLayoutNode) -> bool { - self.node == other.node - } -} - -impl<'ln> ServoLayoutNode<'ln> { - fn from_layout_js(n: LayoutDom<'ln, Node>) -> Self { - ServoLayoutNode { node: n } - } - - pub unsafe fn new(address: &TrustedNodeAddress) -> Self { - ServoLayoutNode::from_layout_js(LayoutDom::from_trusted_node_address(*address)) - } - - fn script_type_id(&self) -> NodeTypeId { - self.node.type_id_for_layout() - } -} - -impl<'ln> NodeInfo for ServoLayoutNode<'ln> { - fn is_element(&self) -> bool { - self.node.is_element_for_layout() - } - - fn is_text_node(&self) -> bool { - self.script_type_id() == - NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) - } -} - -#[derive(Clone, Copy, PartialEq)] -pub struct ServoShadowRoot<'dom> { - /// The wrapped shadow root. - shadow_root: LayoutDom<'dom, ShadowRoot>, -} - -impl<'lr> Debug for ServoShadowRoot<'lr> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.as_node().fmt(f) - } -} - -impl<'lr> TShadowRoot for ServoShadowRoot<'lr> { - type ConcreteNode = ServoLayoutNode<'lr>; - - fn as_node(&self) -> Self::ConcreteNode { - ServoLayoutNode::from_layout_js(self.shadow_root.upcast()) - } - - fn host(&self) -> ServoLayoutElement<'lr> { - ServoLayoutElement::from_layout_js(self.shadow_root.get_host_for_layout()) - } - - fn style_data<'a>(&self) -> Option<&'a CascadeData> - where - Self: 'a, - { - Some(&self.shadow_root.get_style_data_for_layout()) - } -} - -impl<'lr> ServoShadowRoot<'lr> { - fn from_layout_js(shadow_root: LayoutDom<'lr, ShadowRoot>) -> Self { - ServoShadowRoot { shadow_root } - } - - pub unsafe fn flush_stylesheets( - &self, - device: &Device, - quirks_mode: QuirksMode, - guard: &SharedRwLockReadGuard, - ) { - self.shadow_root - .flush_stylesheets::(device, quirks_mode, guard) - } -} - -impl<'ln> TNode for ServoLayoutNode<'ln> { - type ConcreteDocument = ServoLayoutDocument<'ln>; - type ConcreteElement = ServoLayoutElement<'ln>; - type ConcreteShadowRoot = ServoShadowRoot<'ln>; - - fn parent_node(&self) -> Option { - self.node - .composed_parent_node_ref() - .map(Self::from_layout_js) - } - - fn first_child(&self) -> Option { - self.node.first_child_ref().map(Self::from_layout_js) - } - - fn last_child(&self) -> Option { - self.node.last_child_ref().map(Self::from_layout_js) - } - - fn prev_sibling(&self) -> Option { - self.node.prev_sibling_ref().map(Self::from_layout_js) - } - - fn next_sibling(&self) -> Option { - self.node.next_sibling_ref().map(Self::from_layout_js) - } - - fn owner_doc(&self) -> Self::ConcreteDocument { - ServoLayoutDocument::from_layout_js(self.node.owner_doc_for_layout()) - } - - fn traversal_parent(&self) -> Option> { - let parent = self.parent_node()?; - if let Some(shadow) = parent.as_shadow_root() { - return Some(shadow.host()); - }; - parent.as_element() - } +use crate::layout_dom::ServoLayoutNode; +use crate::layout_dom::ServoShadowRoot; +use crate::layout_dom::ServoThreadSafeLayoutNode; - fn opaque(&self) -> OpaqueNode { - self.get_jsmanaged().opaque() - } - - fn debug_id(self) -> usize { - self.opaque().0 - } - - fn as_element(&self) -> Option> { - as_element(self.node) - } - - fn as_document(&self) -> Option> { - self.node - .downcast() - .map(ServoLayoutDocument::from_layout_js) - } - - fn as_shadow_root(&self) -> Option> { - self.node.downcast().map(ServoShadowRoot::from_layout_js) - } - - fn is_in_document(&self) -> bool { - unsafe { self.node.get_flag(NodeFlags::IS_IN_DOC) } - } -} - -impl<'ln> LayoutNode<'ln> for ServoLayoutNode<'ln> { - type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'ln>; - - fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode { - ServoThreadSafeLayoutNode::new(*self) - } - - fn type_id(&self) -> LayoutNodeType { - self.script_type_id().into() - } - - unsafe fn initialize_data(&self) { - if self.get_style_and_layout_data().is_none() { - let opaque = StyleAndOpaqueLayoutData::new( - StyleData::new(), - AtomicRefCell::new(LayoutData::new()), - ); - self.init_style_and_opaque_layout_data(opaque); - }; - } - - unsafe fn init_style_and_opaque_layout_data(&self, data: Box) { - self.get_jsmanaged().init_style_and_opaque_layout_data(data); - } - - unsafe fn take_style_and_opaque_layout_data(&self) -> Box { - self.get_jsmanaged().take_style_and_opaque_layout_data() - } - - fn is_connected(&self) -> bool { - unsafe { self.node.get_flag(NodeFlags::IS_CONNECTED) } - } -} +/// A wrapper around elements that ensures layout can only ever access safe properties. +pub struct ServoLayoutElement<'dom, LayoutDataType: LayoutDataTrait> { + /// The wrapped private DOM Element. + element: LayoutDom<'dom, Element>, -impl<'dom> GetStyleAndOpaqueLayoutData<'dom> for ServoLayoutNode<'dom> { - fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> { - self.get_jsmanaged().get_style_and_opaque_layout_data() - } + /// A PhantomData that is used to track the type of the stored layout data. + phantom: PhantomData, } -impl<'dom> GetStyleAndOpaqueLayoutData<'dom> for ServoLayoutElement<'dom> { - fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> { - self.as_node().get_style_and_opaque_layout_data() +// These impls are required because `derive` has trouble with PhantomData. +// See https://github.com/rust-lang/rust/issues/52079 +impl<'dom, LayoutDataType: LayoutDataTrait> Clone for ServoLayoutElement<'dom, LayoutDataType> { + fn clone(&self) -> Self { + *self } } - -impl<'dom> GetStyleAndOpaqueLayoutData<'dom> for ServoThreadSafeLayoutNode<'dom> { - fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> { - self.node.get_style_and_opaque_layout_data() +impl<'dom, LayoutDataType: LayoutDataTrait> Copy for ServoLayoutElement<'dom, LayoutDataType> {} +impl<'dom, LayoutDataType: LayoutDataTrait> PartialEq for ServoLayoutElement<'dom, LayoutDataType> { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_node() == other.as_node() } } -impl<'dom> GetStyleAndOpaqueLayoutData<'dom> for ServoThreadSafeLayoutElement<'dom> { - fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> { - self.element.as_node().get_style_and_opaque_layout_data() +// `Hash` + `Eq` + `Debug` are required by ::style::dom::TElement. +impl<'dom, LayoutDataType: LayoutDataTrait> Eq for ServoLayoutElement<'dom, LayoutDataType> {} +impl<'dom, LayoutDataType: LayoutDataTrait> Hash for ServoLayoutElement<'dom, LayoutDataType> { + fn hash(&self, state: &mut H) { + self.element.hash(state); } } - -impl<'dom> ServoLayoutNode<'dom> { - /// Returns the interior of this node as a `LayoutDom`. - pub fn get_jsmanaged(self) -> LayoutDom<'dom, Node> { - self.node +impl<'dom, LayoutDataType: LayoutDataTrait> fmt::Debug + for ServoLayoutElement<'dom, LayoutDataType> +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "<{}", self.element.local_name())?; + if let Some(id) = self.id() { + write!(f, " id={}", id)?; + } + write!(f, "> ({:#x})", self.as_node().opaque().0) } } -// A wrapper around documents that ensures ayout can only ever access safe properties. -#[derive(Clone, Copy)] -pub struct ServoLayoutDocument<'dom> { - document: LayoutDom<'dom, Document>, -} - -impl<'ld> TDocument for ServoLayoutDocument<'ld> { - type ConcreteNode = ServoLayoutNode<'ld>; - - fn as_node(&self) -> Self::ConcreteNode { - ServoLayoutNode::from_layout_js(self.document.upcast()) +impl<'dom, LayoutDataType: LayoutDataTrait> ServoLayoutElement<'dom, LayoutDataType> { + pub(super) fn from_layout_js(el: LayoutDom<'dom, Element>) -> Self { + ServoLayoutElement { + element: el, + phantom: PhantomData, + } } - fn quirks_mode(&self) -> QuirksMode { - self.document.quirks_mode() + #[inline] + fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> { + self.element.get_attr_for_layout(namespace, name) } - fn is_html_document(&self) -> bool { - self.document.is_html_document_for_layout() + #[inline] + fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str> { + self.element.get_attr_val_for_layout(namespace, name) } - fn shared_lock(&self) -> &StyleSharedRwLock { - self.document.style_shared_lock() + fn get_style_data(&self) -> Option<&StyleData> { + self.get_style_and_opaque_layout_data() + .map(|data| &data.style_data) } -} -impl<'ld> ServoLayoutDocument<'ld> { - pub fn root_element(&self) -> Option> { + pub unsafe fn unset_snapshot_flags(&self) { self.as_node() - .dom_children() - .flat_map(|n| n.as_element()) - .next() - } - - pub fn needs_paint_from_layout(&self) { - unsafe { self.document.needs_paint_from_layout() } - } - - pub fn will_paint(&self) { - unsafe { self.document.will_paint() } - } - - pub fn style_shared_lock(&self) -> &StyleSharedRwLock { - self.document.style_shared_lock() - } - - pub fn shadow_roots(&self) -> Vec { - unsafe { - self.document - .shadow_roots() - .iter() - .map(|sr| { - debug_assert!(sr.upcast::().get_flag(NodeFlags::IS_CONNECTED)); - ServoShadowRoot::from_layout_js(*sr) - }) - .collect() - } - } - - pub fn flush_shadow_roots_stylesheets( - &self, - device: &Device, - quirks_mode: QuirksMode, - guard: &SharedRwLockReadGuard, - ) { - unsafe { - if !self.document.shadow_roots_styles_changed() { - return; - } - self.document.flush_shadow_roots_stylesheets(); - for shadow_root in self.shadow_roots() { - shadow_root.flush_stylesheets(device, quirks_mode, guard); - } - } - } - - pub fn from_layout_js(doc: LayoutDom<'ld, Document>) -> Self { - ServoLayoutDocument { document: doc } + .node + .set_flag(NodeFlags::HAS_SNAPSHOT | NodeFlags::HANDLED_SNAPSHOT, false); } -} - -/// A wrapper around elements that ensures layout can only ever access safe properties. -#[derive(Clone, Copy)] -pub struct ServoLayoutElement<'dom> { - element: LayoutDom<'dom, Element>, -} -impl<'le> fmt::Debug for ServoLayoutElement<'le> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "<{}", self.element.local_name())?; - if let Some(id) = self.id() { - write!(f, " id={}", id)?; - } - write!(f, "> ({:#x})", self.as_node().opaque().0) + pub unsafe fn set_has_snapshot(&self) { + self.as_node().node.set_flag(NodeFlags::HAS_SNAPSHOT, true); } -} -impl<'dom> ServoLayoutElement<'dom> { /// Returns true if this element is the body child of an html element root element. fn is_body_element_of_html_element_root(&self) -> bool { if self.element.local_name() != &local_name!("body") { @@ -439,7 +141,7 @@ impl<'dom> ServoLayoutElement<'dom> { self.element .upcast() .composed_parent_node_ref() - .and_then(as_element) + .and_then(|node| node.downcast().map(ServoLayoutElement::from_layout_js)) } fn is_root(&self) -> bool { @@ -453,13 +155,23 @@ impl<'dom> ServoLayoutElement<'dom> { } } -impl<'le> TElement for ServoLayoutElement<'le> { - type ConcreteNode = ServoLayoutNode<'le>; +impl<'dom, LayoutDataType: LayoutDataTrait> GetStyleAndOpaqueLayoutData<'dom> + for ServoLayoutElement<'dom, LayoutDataType> +{ + fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> { + self.as_node().get_style_and_opaque_layout_data() + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> style::dom::TElement + for ServoLayoutElement<'dom, LayoutDataType> +{ + type ConcreteNode = ServoLayoutNode<'dom, LayoutDataType>; type TraversalChildrenIterator = DomChildren; type FontMetricsProvider = ServoMetricsProvider; - fn as_node(&self) -> ServoLayoutNode<'le> { + fn as_node(&self) -> ServoLayoutNode<'dom, LayoutDataType> { ServoLayoutNode::from_layout_js(self.element.upcast()) } @@ -608,7 +320,7 @@ impl<'le> TElement for ServoLayoutElement<'le> { } unsafe fn clear_data(&self) { - if self.get_style_and_layout_data().is_some() { + if self.get_style_and_opaque_layout_data().is_some() { drop(self.as_node().take_style_and_opaque_layout_data()); } } @@ -719,14 +431,14 @@ impl<'le> TElement for ServoLayoutElement<'le> { } /// The shadow root this element is a host of. - fn shadow_root(&self) -> Option> { + fn shadow_root(&self) -> Option> { self.element .get_shadow_root_for_layout() .map(ServoShadowRoot::from_layout_js) } /// The shadow root which roots the subtree this element is contained in. - fn containing_shadow(&self) -> Option> { + fn containing_shadow(&self) -> Option> { self.element .upcast() .containing_shadow_root_for_layout() @@ -742,63 +454,16 @@ impl<'le> TElement for ServoLayoutElement<'le> { } } -impl<'le> PartialEq for ServoLayoutElement<'le> { - fn eq(&self, other: &Self) -> bool { - self.as_node() == other.as_node() - } -} - -impl<'le> Hash for ServoLayoutElement<'le> { - fn hash(&self, state: &mut H) { - self.element.hash(state); - } -} - -impl<'le> Eq for ServoLayoutElement<'le> {} - -impl<'le> ServoLayoutElement<'le> { - fn from_layout_js(el: LayoutDom<'le, Element>) -> Self { - ServoLayoutElement { element: el } - } - - #[inline] - fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> { - self.element.get_attr_for_layout(namespace, name) - } - - #[inline] - fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str> { - self.element.get_attr_val_for_layout(namespace, name) - } - - fn get_style_data(&self) -> Option<&StyleData> { - self.get_style_and_opaque_layout_data() - .map(|data| &data.style_data) - } - - pub unsafe fn unset_snapshot_flags(&self) { - self.as_node() - .node - .set_flag(NodeFlags::HAS_SNAPSHOT | NodeFlags::HANDLED_SNAPSHOT, false); - } - - pub unsafe fn set_has_snapshot(&self) { - self.as_node().node.set_flag(NodeFlags::HAS_SNAPSHOT, true); - } -} - -fn as_element<'dom>(node: LayoutDom<'dom, Node>) -> Option> { - node.downcast().map(ServoLayoutElement::from_layout_js) -} - -impl<'le> ::selectors::Element for ServoLayoutElement<'le> { +impl<'dom, LayoutDataType: LayoutDataTrait> ::selectors::Element + for ServoLayoutElement<'dom, LayoutDataType> +{ type Impl = SelectorImpl; fn opaque(&self) -> ::selectors::OpaqueElement { ::selectors::OpaqueElement::new(unsafe { &*(self.as_node().opaque().0 as *const ()) }) } - fn parent_element(&self) -> Option> { + fn parent_element(&self) -> Option { ServoLayoutElement::parent_element(self) } @@ -816,7 +481,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { self.containing_shadow().map(|s| s.host()) } - fn prev_sibling_element(&self) -> Option> { + fn prev_sibling_element(&self) -> Option { let mut node = self.as_node(); while let Some(sibling) = node.prev_sibling() { if let Some(element) = sibling.as_element() { @@ -827,7 +492,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { None } - fn next_sibling_element(&self) -> Option> { + fn next_sibling_element(&self) -> Option> { let mut node = self.as_node(); while let Some(sibling) = node.next_sibling() { if let Some(element) = sibling.as_element() { @@ -997,335 +662,44 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { } } -#[derive(Clone, Copy, Debug)] -pub struct ServoThreadSafeLayoutNode<'ln> { - /// The wrapped node. - node: ServoLayoutNode<'ln>, +/// A wrapper around elements that ensures layout can only +/// ever access safe properties and cannot race on elements. +pub struct ServoThreadSafeLayoutElement<'dom, LayoutDataType: LayoutDataTrait> { + pub(super) element: ServoLayoutElement<'dom, LayoutDataType>, /// The pseudo-element type, with (optionally) /// a specified display value to override the stylesheet. - pseudo: PseudoElementType, -} - -impl<'a> PartialEq for ServoThreadSafeLayoutNode<'a> { - #[inline] - fn eq(&self, other: &ServoThreadSafeLayoutNode<'a>) -> bool { - self.node == other.node - } -} - -impl<'ln> DangerousThreadSafeLayoutNode<'ln> for ServoThreadSafeLayoutNode<'ln> { - unsafe fn dangerous_first_child(&self) -> Option { - self.get_jsmanaged() - .first_child_ref() - .map(ServoLayoutNode::from_layout_js) - .map(Self::new) - } - unsafe fn dangerous_next_sibling(&self) -> Option { - self.get_jsmanaged() - .next_sibling_ref() - .map(ServoLayoutNode::from_layout_js) - .map(Self::new) - } + pub(super) pseudo: PseudoElementType, } -impl<'ln> ServoThreadSafeLayoutNode<'ln> { - /// Creates a new `ServoThreadSafeLayoutNode` from the given `ServoLayoutNode`. - pub fn new(node: ServoLayoutNode<'ln>) -> Self { - ServoThreadSafeLayoutNode { - node: node.clone(), - pseudo: PseudoElementType::Normal, - } - } - - /// Returns the interior of this node as a `LayoutDom`. This is highly unsafe for layout to - /// call and as such is marked `unsafe`. - unsafe fn get_jsmanaged(&self) -> LayoutDom<'ln, Node> { - self.node.get_jsmanaged() - } -} - -impl<'ln> NodeInfo for ServoThreadSafeLayoutNode<'ln> { - fn is_element(&self) -> bool { - self.node.is_element() - } - - fn is_text_node(&self) -> bool { - self.node.is_text_node() - } -} - -impl<'ln> ThreadSafeLayoutNode<'ln> for ServoThreadSafeLayoutNode<'ln> { - type ConcreteNode = ServoLayoutNode<'ln>; - type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'ln>; - type ConcreteElement = ServoLayoutElement<'ln>; - type ChildrenIterator = ThreadSafeLayoutNodeChildrenIterator; - - fn opaque(&self) -> OpaqueNode { - unsafe { self.get_jsmanaged().opaque() } - } - - fn type_id(&self) -> Option { - if self.pseudo == PseudoElementType::Normal { - Some(self.node.type_id()) - } else { - None - } - } - - fn parent_style(&self) -> Arc { - let parent = self.node.parent_node().unwrap().as_element().unwrap(); - let parent_data = parent.borrow_data().unwrap(); - parent_data.styles.primary().clone() - } - - fn debug_id(self) -> usize { - self.node.debug_id() - } - - fn children(&self) -> LayoutIterator { - if let Some(shadow) = self.node.as_element().and_then(|e| e.shadow_root()) { - return LayoutIterator(ThreadSafeLayoutNodeChildrenIterator::new( - shadow.as_node().to_threadsafe(), - )); - } - LayoutIterator(ThreadSafeLayoutNodeChildrenIterator::new(*self)) - } - - fn as_element(&self) -> Option> { - self.node - .as_element() - .map(|el| ServoThreadSafeLayoutElement { - element: el, - pseudo: self.pseudo, - }) - } - - fn get_style_and_opaque_layout_data(self) -> Option<&'ln StyleAndOpaqueLayoutData> { - self.node.get_style_and_opaque_layout_data() - } - - fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool { - unsafe { - let text: LayoutDom = match self.get_jsmanaged().downcast() { - Some(text) => text, - None => return false, - }; - - if !is_whitespace(text.upcast().data_for_layout()) { - return false; - } - - // NB: See the rules for `white-space` here: - // - // http://www.w3.org/TR/CSS21/text.html#propdef-white-space - // - // If you implement other values for this property, you will almost certainly - // want to update this check. - !self - .style(context) - .get_inherited_text() - .white_space - .preserve_newlines() - } - } - - unsafe fn unsafe_get(self) -> Self::ConcreteNode { - self.node - } - - fn node_text_content(self) -> Cow<'ln, str> { - unsafe { self.get_jsmanaged().text_content() } - } - - fn selection(&self) -> Option> { - let this = unsafe { self.get_jsmanaged() }; - - this.selection().map(|range| { - Range::new( - ByteIndex(range.start as isize), - ByteIndex(range.len() as isize), - ) - }) - } - - fn image_url(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.image_url() - } - - fn image_density(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.image_density() - } - - fn image_data(&self) -> Option<(Option>, Option)> { - let this = unsafe { self.get_jsmanaged() }; - this.image_data() - } - - fn canvas_data(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.canvas_data() - } - - fn media_data(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.media_data() - } - - fn svg_data(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.svg_data() - } - - // Can return None if the iframe has no nested browsing context - fn iframe_browsing_context_id(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.iframe_browsing_context_id() - } - - // Can return None if the iframe has no nested browsing context - fn iframe_pipeline_id(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.iframe_pipeline_id() - } - - fn get_colspan(&self) -> u32 { - unsafe { - self.get_jsmanaged() - .downcast::() - .unwrap() - .get_colspan() - } - } - - fn get_rowspan(&self) -> u32 { - unsafe { - self.get_jsmanaged() - .downcast::() - .unwrap() - .get_rowspan() - } +// These impls are required because `derive` has trouble with PhantomData. +// See https://github.com/rust-lang/rust/issues/52079 +impl<'dom, LayoutDataType: LayoutDataTrait> Clone + for ServoThreadSafeLayoutElement<'dom, LayoutDataType> +{ + fn clone(&self) -> Self { + *self } } - -pub struct ThreadSafeLayoutNodeChildrenIterator { - current_node: Option, - parent_node: ConcreteNode, -} - -impl<'dom, ConcreteNode> ThreadSafeLayoutNodeChildrenIterator -where - ConcreteNode: DangerousThreadSafeLayoutNode<'dom>, +impl<'dom, LayoutDataType: LayoutDataTrait> Copy + for ServoThreadSafeLayoutElement<'dom, LayoutDataType> { - pub fn new(parent: ConcreteNode) -> Self { - let first_child: Option = match parent.get_pseudo_element_type() { - PseudoElementType::Normal => parent - .get_before_pseudo() - .or_else(|| parent.get_details_summary_pseudo()) - .or_else(|| unsafe { parent.dangerous_first_child() }), - PseudoElementType::DetailsContent | PseudoElementType::DetailsSummary => unsafe { - parent.dangerous_first_child() - }, - _ => None, - }; - ThreadSafeLayoutNodeChildrenIterator { - current_node: first_child, - parent_node: parent, - } - } } - -impl<'dom, ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator -where - ConcreteNode: DangerousThreadSafeLayoutNode<'dom>, +impl<'dom, LayoutDataType: LayoutDataTrait> fmt::Debug + for ServoThreadSafeLayoutElement<'dom, LayoutDataType> { - type Item = ConcreteNode; - fn next(&mut self) -> Option { - use selectors::Element; - match self.parent_node.get_pseudo_element_type() { - PseudoElementType::Before | PseudoElementType::After => None, - - PseudoElementType::DetailsSummary => { - let mut current_node = self.current_node.clone(); - loop { - let next_node = if let Some(ref node) = current_node { - if let Some(element) = node.as_element() { - if element.has_local_name(&local_name!("summary")) && - element.has_namespace(&ns!(html)) - { - self.current_node = None; - return Some(node.clone()); - } - } - unsafe { node.dangerous_next_sibling() } - } else { - self.current_node = None; - return None; - }; - current_node = next_node; - } - }, - - PseudoElementType::DetailsContent => { - let node = self.current_node.clone(); - let node = node.and_then(|node| { - if node.is_element() && - node.as_element() - .unwrap() - .has_local_name(&local_name!("summary")) && - node.as_element().unwrap().has_namespace(&ns!(html)) - { - unsafe { node.dangerous_next_sibling() } - } else { - Some(node) - } - }); - self.current_node = node.and_then(|node| unsafe { node.dangerous_next_sibling() }); - node - }, - - PseudoElementType::Normal => { - let node = self.current_node.clone(); - if let Some(ref node) = node { - self.current_node = match node.get_pseudo_element_type() { - PseudoElementType::Before => self - .parent_node - .get_details_summary_pseudo() - .or_else(|| unsafe { self.parent_node.dangerous_first_child() }) - .or_else(|| self.parent_node.get_after_pseudo()), - PseudoElementType::Normal => unsafe { node.dangerous_next_sibling() } - .or_else(|| self.parent_node.get_after_pseudo()), - PseudoElementType::DetailsSummary => { - self.parent_node.get_details_content_pseudo() - }, - PseudoElementType::DetailsContent => self.parent_node.get_after_pseudo(), - PseudoElementType::After => None, - }; - } - node - }, - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.element.fmt(f) } } -/// A wrapper around elements that ensures layout can only -/// ever access safe properties and cannot race on elements. -#[derive(Clone, Copy, Debug)] -pub struct ServoThreadSafeLayoutElement<'le> { - element: ServoLayoutElement<'le>, - - /// The pseudo-element type, with (optionally) - /// a specified display value to override the stylesheet. - pseudo: PseudoElementType, -} - -impl<'le> ThreadSafeLayoutElement<'le> for ServoThreadSafeLayoutElement<'le> { - type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'le>; - type ConcreteElement = ServoLayoutElement<'le>; +impl<'dom, LayoutDataType: LayoutDataTrait> ThreadSafeLayoutElement<'dom> + for ServoThreadSafeLayoutElement<'dom, LayoutDataType> +{ + type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'dom, LayoutDataType>; + type ConcreteElement = ServoLayoutElement<'dom, LayoutDataType>; - fn as_node(&self) -> ServoThreadSafeLayoutNode<'le> { + fn as_node(&self) -> ServoThreadSafeLayoutNode<'dom, LayoutDataType> { ServoThreadSafeLayoutNode { node: self.element.as_node(), pseudo: self.pseudo.clone(), @@ -1347,7 +721,7 @@ impl<'le> ThreadSafeLayoutElement<'le> for ServoThreadSafeLayoutElement<'le> { self.as_node().type_id() } - unsafe fn unsafe_get(self) -> ServoLayoutElement<'le> { + unsafe fn unsafe_get(self) -> ServoLayoutElement<'dom, LayoutDataType> { self.element } @@ -1384,7 +758,9 @@ impl<'le> ThreadSafeLayoutElement<'le> for ServoThreadSafeLayoutElement<'le> { /// /// Note that the element implementation is needed only for selector matching, /// not for inheritance (styles are inherited appropriately). -impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { +impl<'dom, LayoutDataType: LayoutDataTrait> ::selectors::Element + for ServoThreadSafeLayoutElement<'dom, LayoutDataType> +{ type Impl = SelectorImpl; fn opaque(&self) -> ::selectors::OpaqueElement { @@ -1522,3 +898,11 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { false } } + +impl<'dom, LayoutDataType: LayoutDataTrait> GetStyleAndOpaqueLayoutData<'dom> + for ServoThreadSafeLayoutElement<'dom, LayoutDataType> +{ + fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> { + self.element.as_node().get_style_and_opaque_layout_data() + } +} diff --git a/components/script/layout_dom/mod.rs b/components/script/layout_dom/mod.rs new file mode 100644 index 0000000000000..0a7c951c7d23b --- /dev/null +++ b/components/script/layout_dom/mod.rs @@ -0,0 +1,32 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +//! A safe wrapper for DOM nodes that prevents layout from mutating the DOM, from letting DOM nodes +//! escape, and from generally doing anything that it isn't supposed to. This is accomplished via +//! a simple whitelist of allowed operations, along with some lifetime magic to prevent nodes from +//! escaping. +//! +//! As a security wrapper is only as good as its whitelist, be careful when adding operations to +//! this list. The cardinal rules are: +//! +//! 1. Layout is not allowed to mutate the DOM. +//! +//! 2. Layout is not allowed to see anything with `LayoutDom` in the name, because it could hang +//! onto these objects and cause use-after-free. +//! +//! When implementing wrapper functions, be careful that you do not touch the borrow flags, or you +//! will race and cause spurious thread failure. (Note that I do not believe these races are +//! exploitable, but they'll result in brokenness nonetheless.) + +#![allow(unsafe_code)] + +mod document; +mod element; +mod node; +mod shadow_root; + +pub use document::*; +pub use element::*; +pub use node::*; +pub use shadow_root::*; diff --git a/components/script/layout_dom/node.rs b/components/script/layout_dom/node.rs new file mode 100644 index 0000000000000..6d5eaecd5f176 --- /dev/null +++ b/components/script/layout_dom/node.rs @@ -0,0 +1,591 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +#![allow(unsafe_code)] + +use super::ServoLayoutDocument; +use super::ServoLayoutElement; +use super::ServoShadowRoot; +use super::ServoThreadSafeLayoutElement; +use crate::dom::bindings::inheritance::CharacterDataTypeId; +use crate::dom::bindings::inheritance::{NodeTypeId, TextTypeId}; +use crate::dom::bindings::root::LayoutDom; +use crate::dom::characterdata::LayoutCharacterDataHelpers; +use crate::dom::element::{Element, LayoutElementHelpers}; +use crate::dom::node::{LayoutNodeHelpers, Node, NodeFlags}; +use crate::dom::text::Text; +use atomic_refcell::AtomicRefCell; +use gfx_traits::ByteIndex; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; +use net_traits::image::base::{Image, ImageMetadata}; +use range::Range; +use script_layout_interface::wrapper_traits::{ + DangerousThreadSafeLayoutNode, GetStyleAndOpaqueLayoutData, LayoutDataTrait, LayoutNode, + PseudoElementType, ThreadSafeLayoutNode, +}; +use script_layout_interface::{ + HTMLCanvasData, HTMLMediaData, LayoutNodeType, SVGSVGData, StyleAndOpaqueLayoutData, StyleData, + TrustedNodeAddress, +}; +use servo_arc::Arc; +use servo_url::ServoUrl; +use std::borrow::Cow; +use std::fmt; +use std::marker::PhantomData; +use std::sync::Arc as StdArc; +use style; +use style::context::SharedStyleContext; +use style::dom::{NodeInfo, TElement, TNode, TShadowRoot}; +use style::properties::ComputedValues; +use style::str::is_whitespace; + +/// A wrapper around a `LayoutDom` which provides a safe interface that +/// can be used during layout. This implements the `LayoutNode` trait as well as +/// several style and selectors traits for use during layout. This version +/// should only be used on a single thread. If you need to use nodes across +/// threads use ServoThreadSafeLayoutNode. +pub struct ServoLayoutNode<'dom, LayoutDataType: LayoutDataTrait> { + /// The wrapped private DOM node. + pub(super) node: LayoutDom<'dom, Node>, + + /// A PhantomData that is used to track the type of the stored layout data. + pub(super) phantom: PhantomData, +} + +//// Those are supposed to be sound, but they aren't because the entire system +//// between script and layout so far has been designed to work around their +//// absence. Switching the entire thing to the inert crate infra will help. +/// +//// FIXME(mrobinson): These are required because Layout 2020 sends non-threadsafe +//// nodes to different threads. This should be adressed in a comprehensive way. +unsafe impl Send for ServoLayoutNode<'_, LayoutDataType> {} +unsafe impl Sync for ServoLayoutNode<'_, LayoutDataType> {} + +// These impls are required because `derive` has trouble with PhantomData. +// See https://github.com/rust-lang/rust/issues/52079 +impl<'dom, LayoutDataType: LayoutDataTrait> Clone for ServoLayoutNode<'dom, LayoutDataType> { + fn clone(&self) -> Self { + *self + } +} +impl<'dom, LayoutDataType: LayoutDataTrait> Copy for ServoLayoutNode<'dom, LayoutDataType> {} +impl<'a, LayoutDataType: LayoutDataTrait> PartialEq for ServoLayoutNode<'a, LayoutDataType> { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.node == other.node + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> fmt::Debug for ServoLayoutNode<'dom, LayoutDataType> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(el) = self.as_element() { + el.fmt(f) + } else { + if self.is_text_node() { + write!(f, " ({:#x})", self.opaque().0) + } else { + write!(f, " ({:#x})", self.opaque().0) + } + } + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> ServoLayoutNode<'dom, LayoutDataType> { + pub(super) fn from_layout_js(n: LayoutDom<'dom, Node>) -> Self { + ServoLayoutNode { + node: n, + phantom: PhantomData, + } + } + + pub unsafe fn new(address: &TrustedNodeAddress) -> Self { + ServoLayoutNode::from_layout_js(LayoutDom::from_trusted_node_address(*address)) + } + + pub(super) fn script_type_id(&self) -> NodeTypeId { + self.node.type_id_for_layout() + } + + /// Returns the interior of this node as a `LayoutDom`. + pub fn get_jsmanaged(self) -> LayoutDom<'dom, Node> { + self.node + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> style::dom::NodeInfo + for ServoLayoutNode<'dom, LayoutDataType> +{ + fn is_element(&self) -> bool { + self.node.is_element_for_layout() + } + + fn is_text_node(&self) -> bool { + self.script_type_id() == + NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> style::dom::TNode + for ServoLayoutNode<'dom, LayoutDataType> +{ + type ConcreteDocument = ServoLayoutDocument<'dom, LayoutDataType>; + type ConcreteElement = ServoLayoutElement<'dom, LayoutDataType>; + type ConcreteShadowRoot = ServoShadowRoot<'dom, LayoutDataType>; + + fn parent_node(&self) -> Option { + self.node + .composed_parent_node_ref() + .map(Self::from_layout_js) + } + + fn first_child(&self) -> Option { + self.node.first_child_ref().map(Self::from_layout_js) + } + + fn last_child(&self) -> Option { + self.node.last_child_ref().map(Self::from_layout_js) + } + + fn prev_sibling(&self) -> Option { + self.node.prev_sibling_ref().map(Self::from_layout_js) + } + + fn next_sibling(&self) -> Option { + self.node.next_sibling_ref().map(Self::from_layout_js) + } + + fn owner_doc(&self) -> Self::ConcreteDocument { + ServoLayoutDocument::from_layout_js(self.node.owner_doc_for_layout()) + } + + fn traversal_parent(&self) -> Option> { + let parent = self.parent_node()?; + if let Some(shadow) = parent.as_shadow_root() { + return Some(shadow.host()); + }; + parent.as_element() + } + + fn opaque(&self) -> style::dom::OpaqueNode { + self.get_jsmanaged().opaque() + } + + fn debug_id(self) -> usize { + self.opaque().0 + } + + fn as_element(&self) -> Option> { + self.node.downcast().map(ServoLayoutElement::from_layout_js) + } + + fn as_document(&self) -> Option> { + self.node + .downcast() + .map(ServoLayoutDocument::from_layout_js) + } + + fn as_shadow_root(&self) -> Option> { + self.node.downcast().map(ServoShadowRoot::from_layout_js) + } + + fn is_in_document(&self) -> bool { + unsafe { self.node.get_flag(NodeFlags::IS_IN_DOC) } + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> LayoutNode<'dom> + for ServoLayoutNode<'dom, LayoutDataType> +{ + type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'dom, LayoutDataType>; + + fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode { + ServoThreadSafeLayoutNode::new(*self) + } + + fn type_id(&self) -> LayoutNodeType { + self.script_type_id().into() + } + + unsafe fn initialize_data(&self) { + if self.get_style_and_opaque_layout_data().is_none() { + let opaque = StyleAndOpaqueLayoutData::new( + StyleData::new(), + AtomicRefCell::new(LayoutDataType::default()), + ); + self.init_style_and_opaque_layout_data(opaque); + }; + } + + unsafe fn init_style_and_opaque_layout_data(&self, data: Box) { + self.get_jsmanaged().init_style_and_opaque_layout_data(data); + } + + unsafe fn take_style_and_opaque_layout_data(&self) -> Box { + self.get_jsmanaged().take_style_and_opaque_layout_data() + } + + fn is_connected(&self) -> bool { + unsafe { self.node.get_flag(NodeFlags::IS_CONNECTED) } + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> GetStyleAndOpaqueLayoutData<'dom> + for ServoLayoutNode<'dom, LayoutDataType> +{ + fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> { + self.get_jsmanaged().get_style_and_opaque_layout_data() + } +} + +/// A wrapper around a `ServoLayoutNode` that can be used safely on different threads. +/// It's very important that this never mutate anything except this wrapped node and +/// never access any other node apart from its parent. +pub struct ServoThreadSafeLayoutNode<'dom, LayoutDataType: LayoutDataTrait> { + /// The wrapped `ServoLayoutNode`. + pub(super) node: ServoLayoutNode<'dom, LayoutDataType>, + + /// The pseudo-element type, with (optionally) + /// a specified display value to override the stylesheet. + pub(super) pseudo: PseudoElementType, +} + +// These impls are required because `derive` has trouble with PhantomData. +// See https://github.com/rust-lang/rust/issues/52079 +impl<'dom, LayoutDataType: LayoutDataTrait> Clone + for ServoThreadSafeLayoutNode<'dom, LayoutDataType> +{ + fn clone(&self) -> Self { + *self + } +} +impl<'dom, LayoutDataType: LayoutDataTrait> Copy + for ServoThreadSafeLayoutNode<'dom, LayoutDataType> +{ +} +impl<'a, LayoutDataType: LayoutDataTrait> PartialEq + for ServoThreadSafeLayoutNode<'a, LayoutDataType> +{ + #[inline] + fn eq(&self, other: &Self) -> bool { + self.node == other.node + } +} + +impl<'lr, LayoutDataType: LayoutDataTrait> fmt::Debug + for ServoThreadSafeLayoutNode<'lr, LayoutDataType> +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.node.fmt(f) + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> DangerousThreadSafeLayoutNode<'dom> + for ServoThreadSafeLayoutNode<'dom, LayoutDataType> +{ + unsafe fn dangerous_first_child(&self) -> Option { + self.get_jsmanaged() + .first_child_ref() + .map(ServoLayoutNode::from_layout_js) + .map(Self::new) + } + unsafe fn dangerous_next_sibling(&self) -> Option { + self.get_jsmanaged() + .next_sibling_ref() + .map(ServoLayoutNode::from_layout_js) + .map(Self::new) + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> ServoThreadSafeLayoutNode<'dom, LayoutDataType> { + /// Creates a new `ServoThreadSafeLayoutNode` from the given `ServoLayoutNode`. + pub fn new(node: ServoLayoutNode<'dom, LayoutDataType>) -> Self { + ServoThreadSafeLayoutNode { + node: node.clone(), + pseudo: PseudoElementType::Normal, + } + } + + /// Returns the interior of this node as a `LayoutDom`. This is highly unsafe for layout to + /// call and as such is marked `unsafe`. + unsafe fn get_jsmanaged(&self) -> LayoutDom<'dom, Node> { + self.node.get_jsmanaged() + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> style::dom::NodeInfo + for ServoThreadSafeLayoutNode<'dom, LayoutDataType> +{ + fn is_element(&self) -> bool { + self.node.is_element() + } + + fn is_text_node(&self) -> bool { + self.node.is_text_node() + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> ThreadSafeLayoutNode<'dom> + for ServoThreadSafeLayoutNode<'dom, LayoutDataType> +{ + type ConcreteNode = ServoLayoutNode<'dom, LayoutDataType>; + type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'dom, LayoutDataType>; + type ConcreteElement = ServoLayoutElement<'dom, LayoutDataType>; + type ChildrenIterator = ThreadSafeLayoutNodeChildrenIterator; + + fn opaque(&self) -> style::dom::OpaqueNode { + unsafe { self.get_jsmanaged().opaque() } + } + + fn type_id(&self) -> Option { + if self.pseudo == PseudoElementType::Normal { + Some(self.node.type_id()) + } else { + None + } + } + + fn parent_style(&self) -> Arc { + let parent = self.node.parent_node().unwrap().as_element().unwrap(); + let parent_data = parent.borrow_data().unwrap(); + parent_data.styles.primary().clone() + } + + fn debug_id(self) -> usize { + self.node.debug_id() + } + + fn children(&self) -> style::dom::LayoutIterator { + if let Some(shadow) = self.node.as_element().and_then(|e| e.shadow_root()) { + return style::dom::LayoutIterator(ThreadSafeLayoutNodeChildrenIterator::new( + shadow.as_node().to_threadsafe(), + )); + } + style::dom::LayoutIterator(ThreadSafeLayoutNodeChildrenIterator::new(*self)) + } + + fn as_element(&self) -> Option> { + self.node + .as_element() + .map(|el| ServoThreadSafeLayoutElement { + element: el, + pseudo: self.pseudo, + }) + } + + fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> { + self.node.get_style_and_opaque_layout_data() + } + + fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool { + unsafe { + let text: LayoutDom = match self.get_jsmanaged().downcast() { + Some(text) => text, + None => return false, + }; + + if !is_whitespace(text.upcast().data_for_layout()) { + return false; + } + + // NB: See the rules for `white-space` here: + // + // http://www.w3.org/TR/CSS21/text.html#propdef-white-space + // + // If you implement other values for this property, you will almost certainly + // want to update this check. + !self + .style(context) + .get_inherited_text() + .white_space + .preserve_newlines() + } + } + + unsafe fn unsafe_get(self) -> Self::ConcreteNode { + self.node + } + + fn node_text_content(self) -> Cow<'dom, str> { + unsafe { self.get_jsmanaged().text_content() } + } + + fn selection(&self) -> Option> { + let this = unsafe { self.get_jsmanaged() }; + + this.selection().map(|range| { + Range::new( + ByteIndex(range.start as isize), + ByteIndex(range.len() as isize), + ) + }) + } + + fn image_url(&self) -> Option { + let this = unsafe { self.get_jsmanaged() }; + this.image_url() + } + + fn image_density(&self) -> Option { + let this = unsafe { self.get_jsmanaged() }; + this.image_density() + } + + fn image_data(&self) -> Option<(Option>, Option)> { + let this = unsafe { self.get_jsmanaged() }; + this.image_data() + } + + fn canvas_data(&self) -> Option { + let this = unsafe { self.get_jsmanaged() }; + this.canvas_data() + } + + fn media_data(&self) -> Option { + let this = unsafe { self.get_jsmanaged() }; + this.media_data() + } + + fn svg_data(&self) -> Option { + let this = unsafe { self.get_jsmanaged() }; + this.svg_data() + } + + // Can return None if the iframe has no nested browsing context + fn iframe_browsing_context_id(&self) -> Option { + let this = unsafe { self.get_jsmanaged() }; + this.iframe_browsing_context_id() + } + + // Can return None if the iframe has no nested browsing context + fn iframe_pipeline_id(&self) -> Option { + let this = unsafe { self.get_jsmanaged() }; + this.iframe_pipeline_id() + } + + fn get_colspan(&self) -> u32 { + unsafe { + self.get_jsmanaged() + .downcast::() + .unwrap() + .get_colspan() + } + } + + fn get_rowspan(&self) -> u32 { + unsafe { + self.get_jsmanaged() + .downcast::() + .unwrap() + .get_rowspan() + } + } +} + +pub struct ThreadSafeLayoutNodeChildrenIterator { + current_node: Option, + parent_node: ConcreteNode, +} + +impl<'dom, ConcreteNode> ThreadSafeLayoutNodeChildrenIterator +where + ConcreteNode: DangerousThreadSafeLayoutNode<'dom>, +{ + pub fn new(parent: ConcreteNode) -> Self { + let first_child: Option = match parent.get_pseudo_element_type() { + PseudoElementType::Normal => parent + .get_before_pseudo() + .or_else(|| parent.get_details_summary_pseudo()) + .or_else(|| unsafe { parent.dangerous_first_child() }), + PseudoElementType::DetailsContent | PseudoElementType::DetailsSummary => unsafe { + parent.dangerous_first_child() + }, + _ => None, + }; + ThreadSafeLayoutNodeChildrenIterator { + current_node: first_child, + parent_node: parent, + } + } +} + +impl<'dom, ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator +where + ConcreteNode: DangerousThreadSafeLayoutNode<'dom>, +{ + type Item = ConcreteNode; + fn next(&mut self) -> Option { + use selectors::Element; + match self.parent_node.get_pseudo_element_type() { + PseudoElementType::Before | PseudoElementType::After => None, + + PseudoElementType::DetailsSummary => { + let mut current_node = self.current_node.clone(); + loop { + let next_node = if let Some(ref node) = current_node { + if let Some(element) = node.as_element() { + if element.has_local_name(&local_name!("summary")) && + element.has_namespace(&ns!(html)) + { + self.current_node = None; + return Some(node.clone()); + } + } + unsafe { node.dangerous_next_sibling() } + } else { + self.current_node = None; + return None; + }; + current_node = next_node; + } + }, + + PseudoElementType::DetailsContent => { + let node = self.current_node.clone(); + let node = node.and_then(|node| { + if node.is_element() && + node.as_element() + .unwrap() + .has_local_name(&local_name!("summary")) && + node.as_element().unwrap().has_namespace(&ns!(html)) + { + unsafe { node.dangerous_next_sibling() } + } else { + Some(node) + } + }); + self.current_node = node.and_then(|node| unsafe { node.dangerous_next_sibling() }); + node + }, + + PseudoElementType::Normal => { + let node = self.current_node.clone(); + if let Some(ref node) = node { + self.current_node = match node.get_pseudo_element_type() { + PseudoElementType::Before => self + .parent_node + .get_details_summary_pseudo() + .or_else(|| unsafe { self.parent_node.dangerous_first_child() }) + .or_else(|| self.parent_node.get_after_pseudo()), + PseudoElementType::Normal => unsafe { node.dangerous_next_sibling() } + .or_else(|| self.parent_node.get_after_pseudo()), + PseudoElementType::DetailsSummary => { + self.parent_node.get_details_content_pseudo() + }, + PseudoElementType::DetailsContent => self.parent_node.get_after_pseudo(), + PseudoElementType::After => None, + }; + } + node + }, + } + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> GetStyleAndOpaqueLayoutData<'dom> + for ServoThreadSafeLayoutNode<'dom, LayoutDataType> +{ + fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> { + self.node.get_style_and_opaque_layout_data() + } +} diff --git a/components/script/layout_dom/shadow_root.rs b/components/script/layout_dom/shadow_root.rs new file mode 100644 index 0000000000000..2fd1511ccfa0f --- /dev/null +++ b/components/script/layout_dom/shadow_root.rs @@ -0,0 +1,84 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 crate::dom::bindings::root::LayoutDom; +use crate::dom::shadowroot::{LayoutShadowRootHelpers, ShadowRoot}; +use crate::layout_dom::ServoLayoutElement; +use crate::layout_dom::ServoLayoutNode; +use script_layout_interface::wrapper_traits::LayoutDataTrait; +use selectors::matching::QuirksMode; +use std::fmt; +use std::marker::PhantomData; +use style::dom::TShadowRoot; +use style::media_queries::Device; +use style::shared_lock::SharedRwLockReadGuard as StyleSharedRwLockReadGuard; +use style::stylist::CascadeData; + +pub struct ServoShadowRoot<'dom, LayoutDataType: LayoutDataTrait> { + /// The wrapped private DOM ShadowRoot. + shadow_root: LayoutDom<'dom, ShadowRoot>, + + /// A PhantomData that is used to track the type of the stored layout data. + phantom: PhantomData, +} + +impl<'dom, LayoutDataType: LayoutDataTrait> Clone for ServoShadowRoot<'dom, LayoutDataType> { + fn clone(&self) -> Self { + *self + } +} +impl<'dom, LayoutDataType: LayoutDataTrait> Copy for ServoShadowRoot<'dom, LayoutDataType> {} + +impl<'a, LayoutDataType: LayoutDataTrait> PartialEq for ServoShadowRoot<'a, LayoutDataType> { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.shadow_root == other.shadow_root + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> fmt::Debug for ServoShadowRoot<'dom, LayoutDataType> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.as_node().fmt(f) + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> ::style::dom::TShadowRoot + for ServoShadowRoot<'dom, LayoutDataType> +{ + type ConcreteNode = ServoLayoutNode<'dom, LayoutDataType>; + + fn as_node(&self) -> Self::ConcreteNode { + ServoLayoutNode::from_layout_js(self.shadow_root.upcast()) + } + + fn host(&self) -> ServoLayoutElement<'dom, LayoutDataType> { + ServoLayoutElement::from_layout_js(self.shadow_root.get_host_for_layout()) + } + + fn style_data<'a>(&self) -> Option<&'a CascadeData> + where + Self: 'a, + { + Some(&self.shadow_root.get_style_data_for_layout()) + } +} + +impl<'dom, LayoutDataType: LayoutDataTrait> ServoShadowRoot<'dom, LayoutDataType> { + pub(super) fn from_layout_js(shadow_root: LayoutDom<'dom, ShadowRoot>) -> Self { + ServoShadowRoot { + shadow_root, + phantom: PhantomData, + } + } + + pub unsafe fn flush_stylesheets( + &self, + device: &Device, + quirks_mode: QuirksMode, + guard: &StyleSharedRwLockReadGuard, + ) { + self.shadow_root + .flush_stylesheets::>(device, quirks_mode, guard) + } +} diff --git a/components/script/lib.rs b/components/script/lib.rs index b34b1fb11ee30..66c878dbbdee6 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -73,6 +73,8 @@ mod image_listener; mod init; #[warn(deprecated)] mod layout_image; + +pub mod layout_dom; #[warn(deprecated)] mod mem; #[warn(deprecated)] @@ -115,23 +117,3 @@ mod window_named_properties; pub use init::init; pub use script_runtime::JSEngineSetup; - -/// A module with everything layout can use from script. -/// -/// Try to keep this small! -/// -/// TODO(emilio): A few of the FooHelpers can go away, presumably... -pub mod layout_exports { - pub use crate::dom::bindings::inheritance::{ - CharacterDataTypeId, DocumentFragmentTypeId, ElementTypeId, - }; - pub use crate::dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId, TextTypeId}; - pub use crate::dom::bindings::root::LayoutDom; - pub use crate::dom::characterdata::LayoutCharacterDataHelpers; - pub use crate::dom::document::{Document, LayoutDocumentHelpers}; - pub use crate::dom::element::{Element, LayoutElementHelpers}; - pub use crate::dom::node::NodeFlags; - pub use crate::dom::node::{LayoutNodeHelpers, Node}; - pub use crate::dom::shadowroot::{LayoutShadowRootHelpers, ShadowRoot}; - pub use crate::dom::text::Text; -} diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs index 10e6f8263153a..9091a394161cb 100644 --- a/components/script_layout_interface/wrapper_traits.rs +++ b/components/script_layout_interface/wrapper_traits.rs @@ -31,6 +31,8 @@ use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorIm use style::stylist::RuleInclusion; use webrender_api::ExternalScrollId; +pub trait LayoutDataTrait: Default + Send + Sync + 'static {} + #[derive(Clone, Copy, Debug, PartialEq)] pub enum PseudoElementType { Normal,