aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock3
-rw-r--r--components/layout/data.rs8
-rw-r--r--components/layout_2020/element_data.rs3
-rw-r--r--components/layout_thread/lib.rs40
-rw-r--r--components/layout_thread_2020/dom_wrapper.rs1537
-rw-r--r--components/layout_thread_2020/lib.rs40
-rw-r--r--components/script/Cargo.toml3
-rw-r--r--components/script/layout_dom/document.rs114
-rw-r--r--components/script/layout_dom/element.rs (renamed from components/layout_thread/dom_wrapper.rs)880
-rw-r--r--components/script/layout_dom/mod.rs32
-rw-r--r--components/script/layout_dom/node.rs591
-rw-r--r--components/script/layout_dom/shadow_root.rs84
-rw-r--r--components/script/lib.rs22
-rw-r--r--components/script_layout_interface/wrapper_traits.rs2
14 files changed, 1016 insertions, 2343 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3c99cb089ed..d945407f3ec 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 65d96bae244..7744b08a573 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 f0b1b198432..8963f191d6f 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<InlineLevelBox>),
FlexLevel(ArcRefCell<FlexLevelBox>),
}
+
+impl LayoutDataTrait for LayoutDataForElement {}
diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs
index d6fb814b758..bf4ff1b85bc 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<LayoutData>>,
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::<LayoutData>::new(&r.0)
+ .as_element()
+ .unwrap()
+ })
.collect();
for (el, restyle) in restyles {
- let el = unsafe { ServoLayoutNode::new(&el).as_element().unwrap() };
+ let el: ServoLayoutElement<LayoutData> =
+ 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 =
- <RecalcStyleAndConstructFlows as DomTraversal<ServoLayoutElement>>::shared_context(
- &traversal,
- );
+ let shared = <RecalcStyleAndConstructFlows as DomTraversal<
+ ServoLayoutElement<LayoutData>,
+ >>::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<LayoutData>,
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<LayoutData> = 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<LayoutData> = 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<LayoutData> = 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<LayoutData> = 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<LayoutData>>,
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<LayoutData>>,
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 2b5f30f3cd9..00000000000
--- 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, "<text node> ({:#x})", self.opaque().0)
- } else {
- write!(f, "<non-text node> ({:#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::<ServoLayoutElement>(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> {
- self.node
- .composed_parent_node_ref()
- .map(Self::from_layout_js)
- }
-
- fn first_child(&self) -> Option<Self> {
- self.node.first_child_ref().map(Self::from_layout_js)
- }
-
- fn last_child(&self) -> Option<Self> {
- self.node.last_child_ref().map(Self::from_layout_js)
- }
-
- fn prev_sibling(&self) -> Option<Self> {
- self.node.prev_sibling_ref().map(Self::from_layout_js)
- }
-
- fn next_sibling(&self) -> Option<Self> {
- 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<ServoLayoutElement<'ln>> {
- 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<ServoLayoutElement<'ln>> {
- as_element(self.node)
- }
-
- fn as_document(&self) -> Option<ServoLayoutDocument<'ln>> {
- self.node
- .downcast()
- .map(ServoLayoutDocument::from_layout_js)
- }
-
- fn as_shadow_root(&self) -> Option<ServoShadowRoot<'ln>> {
- 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<StyleAndOpaqueLayoutData>) {
- self.get_jsmanaged().init_style_and_opaque_layout_data(data);
- }
-
- unsafe fn take_style_and_opaque_layout_data(&self) -> Box<StyleAndOpaqueLayoutData> {
- 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<ServoLayoutElement<'ld>> {
- 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<ServoShadowRoot> {
- unsafe {
- self.document
- .shadow_roots()
- .iter()
- .map(|sr| {
- debug_assert!(sr.upcast::<Node>().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> {
- 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<Self::ConcreteNode>;
-
- type FontMetricsProvider = ServoMetricsProvider;
-
- fn as_node(&self) -> ServoLayoutNode<'le> {
- ServoLayoutNode::from_layout_js(self.element.upcast())
- }
-
- fn traversal_children(&self) -> LayoutIterator<Self::TraversalChildrenIterator> {
- 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<ArcBorrow<StyleLocked<PropertyDeclarationBlock>>> {
- 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<Arc<StyleLocked<PropertyDeclarationBlock>>> {
- 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<Arc<StyleLocked<PropertyDeclarationBlock>>> {
- 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<F>(&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<ElementData> {
- 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<AtomicRef<ElementData>> {
- self.get_style_data().map(|data| data.element_data.borrow())
- }
-
- /// Mutably borrows the ElementData.
- fn mutate_data(&self) -> Option<AtomicRefMut<ElementData>> {
- 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<PseudoElement>,
- ) -> 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<PseudoElement>,
- ) -> bool {
- let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
- context.animations.has_active_transitions(&key)
- }
-
- #[inline]
- fn lang_attr(&self) -> Option<SelectorAttrValue> {
- 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<Option<SelectorAttrValue>>,
- 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 <ident> or <string>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<V>(
- &self,
- _visited_handling: VisitedHandlingMode,
- hints: &mut V,
- ) where
- V: Push<ApplicableDeclarationBlock>,
- {
- self.element
- .synthesize_presentational_hints_for_legacy_attributes(hints);
- }
-
- /// The shadow root this element is a host of.
- fn shadow_root(&self) -> Option<ServoShadowRoot<'le>> {
- 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<ServoShadowRoot<'le>> {
- 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<H: Hasher>(&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<ServoLayoutElement<'dom>> {
- 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<ServoLayoutElement<'le>> {
- 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> {
- self.containing_shadow().map(|s| s.host())
- }
-
- fn prev_sibling_element(&self) -> Option<ServoLayoutElement<'le>> {
- 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<ServoLayoutElement<'le>> {
- 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<Self::Impl>,
- ) -> bool {
- false
- }
-
- fn match_non_ts_pseudo_class<F>(
- &self,
- pseudo_class: &NonTSPseudoClass,
- _: &mut MatchingContext<Self::Impl>,
- _: &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<AtomIdent> {
- 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> {
- self.get_jsmanaged()
- .first_child_ref()
- .map(ServoLayoutNode::from_layout_js)
- .map(Self::new)
- }
- unsafe fn dangerous_next_sibling(&self) -> Option<Self> {
- 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<Self>;
-
- fn opaque(&self) -> OpaqueNode {
- unsafe { self.get_jsmanaged().opaque() }
- }
-
- fn type_id(&self) -> Option<LayoutNodeType> {
- if self.pseudo == PseudoElementType::Normal {
- Some(self.node.type_id())
- } else {
- None
- }
- }
-
- fn parent_style(&self) -> Arc<ComputedValues> {
- 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<Self::ChildrenIterator> {
- 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<ServoThreadSafeLayoutElement<'ln>> {
- 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<Text> = 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<Range<ByteIndex>> {
- 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<ServoUrl> {
- let this = unsafe { self.get_jsmanaged() };
- this.image_url()
- }
-
- fn image_density(&self) -> Option<f64> {
- let this = unsafe { self.get_jsmanaged() };
- this.image_density()
- }
-
- fn image_data(&self) -> Option<(Option<StdArc<Image>>, Option<ImageMetadata>)> {
- let this = unsafe { self.get_jsmanaged() };
- this.image_data()
- }
-
- fn canvas_data(&self) -> Option<HTMLCanvasData> {
- let this = unsafe { self.get_jsmanaged() };
- this.canvas_data()
- }
-
- fn media_data(&self) -> Option<HTMLMediaData> {
- let this = unsafe { self.get_jsmanaged() };
- this.media_data()
- }
-
- fn svg_data(&self) -> Option<SVGSVGData> {
- 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<BrowsingContextId> {
- 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<PipelineId> {
- let this = unsafe { self.get_jsmanaged() };
- this.iframe_pipeline_id()
- }
-
- fn get_colspan(&self) -> u32 {
- unsafe {
- self.get_jsmanaged()
- .downcast::<Element>()
- .unwrap()
- .get_colspan()
- }
- }
-
- fn get_rowspan(&self) -> u32 {
- unsafe {
- self.get_jsmanaged()
- .downcast::<Element>()
- .unwrap()
- .get_rowspan()
- }
- }
-}
-
-pub struct ThreadSafeLayoutNodeChildrenIterator<ConcreteNode> {
- current_node: Option<ConcreteNode>,
- parent_node: ConcreteNode,
-}
-
-impl<'dom, ConcreteNode> ThreadSafeLayoutNodeChildrenIterator<ConcreteNode>
-where
- ConcreteNode: DangerousThreadSafeLayoutNode<'dom>,
-{
- pub fn new(parent: ConcreteNode) -> Self {
- let first_child: Option<ConcreteNode> = 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<ConcreteNode>
-where
- ConcreteNode: DangerousThreadSafeLayoutNode<'dom>,
-{
- type Item = ConcreteNode;
- fn next(&mut self) -> Option<ConcreteNode> {
- 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<LayoutNodeType> {
- 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<ElementData> {
- 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<Self> {
- warn!("ServoThreadSafeLayoutElement::parent_element called");
- None
- }
-
- fn parent_node_is_shadow_root(&self) -> bool {
- false
- }
-
- fn containing_shadow_host(&self) -> Option<Self> {
- None
- }
-
- // Skips non-element nodes
- fn prev_sibling_element(&self) -> Option<Self> {
- warn!("ServoThreadSafeLayoutElement::prev_sibling_element called");
- None
- }
-
- // Skips non-element nodes
- fn next_sibling_element(&self) -> Option<Self> {
- 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<Self::Impl>,
- ) -> 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<F>(
- &self,
- _: &NonTSPseudoClass,
- _: &mut MatchingContext<Self::Impl>,
- _: &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<AtomIdent> {
- 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 df7f2e90827..ddfbc69222f 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::<LayoutDataForElement>::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::<LayoutDataForElement>::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::<LayoutDataForElement>::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::<LayoutDataForElement>::new(&data.dirty_root.unwrap())
.as_element()
.unwrap()
};
let traversal = RecalcStyle::new(layout_context);
let token = {
- let shared = DomTraversal::<ServoLayoutElement>::shared_context(&traversal);
+ let shared = DomTraversal::<ServoLayoutElement<LayoutDataForElement>>::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<LayoutDataForElement> =
+ 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::<LayoutDataForElement>::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::<LayoutDataForElement>::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::<LayoutDataForElement>::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::<LayoutDataForElement>::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<FragmentTree>,
reflow_goal: &ReflowGoal,
- document: Option<&ServoLayoutDocument>,
+ document: Option<&ServoLayoutDocument<LayoutDataForElement>>,
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 94a8a3f33b4..bbe1f4f78bc 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 00000000000..e723f731cab
--- /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<LayoutDataType>,
+}
+
+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<ServoLayoutElement<'ld, LayoutDataType>> {
+ 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<ServoShadowRoot<LayoutDataType>> {
+ unsafe {
+ self.document
+ .shadow_roots()
+ .iter()
+ .map(|sr| {
+ debug_assert!(sr.upcast::<Node>().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
index 35e7fa3b8ec..9b9f8c4790b 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, "<text node> ({:#x})", self.opaque().0)
- } else {
- write!(f, "<non-text node> ({:#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::<ServoLayoutElement>(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> {
- self.node
- .composed_parent_node_ref()
- .map(Self::from_layout_js)
- }
-
- fn first_child(&self) -> Option<Self> {
- self.node.first_child_ref().map(Self::from_layout_js)
- }
-
- fn last_child(&self) -> Option<Self> {
- self.node.last_child_ref().map(Self::from_layout_js)
- }
-
- fn prev_sibling(&self) -> Option<Self> {
- self.node.prev_sibling_ref().map(Self::from_layout_js)
- }
-
- fn next_sibling(&self) -> Option<Self> {
- 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<ServoLayoutElement<'ln>> {
- 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<ServoLayoutElement<'ln>> {
- as_element(self.node)
- }
-
- fn as_document(&self) -> Option<ServoLayoutDocument<'ln>> {
- self.node
- .downcast()
- .map(ServoLayoutDocument::from_layout_js)
- }
-
- fn as_shadow_root(&self) -> Option<ServoShadowRoot<'ln>> {
- 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<StyleAndOpaqueLayoutData>) {
- self.get_jsmanaged().init_style_and_opaque_layout_data(data);
- }
-
- unsafe fn take_style_and_opaque_layout_data(&self) -> Box<StyleAndOpaqueLayoutData> {
- 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<LayoutDataType>,
}
-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<H: Hasher>(&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<ServoLayoutElement<'ld>> {
+ 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<ServoShadowRoot> {
- unsafe {
- self.document
- .shadow_roots()
- .iter()
- .map(|sr| {
- debug_assert!(sr.upcast::<Node>().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<Self::ConcreteNode>;
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<ServoShadowRoot<'le>> {
+ fn shadow_root(&self) -> Option<ServoShadowRoot<'dom, LayoutDataType>> {
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<ServoShadowRoot<'le>> {
+ fn containing_shadow(&self) -> Option<ServoShadowRoot<'dom, LayoutDataType>> {
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<H: Hasher>(&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<ServoLayoutElement<'dom>> {
- 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<ServoLayoutElement<'le>> {
+ fn parent_element(&self) -> Option<Self> {
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<ServoLayoutElement<'le>> {
+ fn prev_sibling_element(&self) -> Option<Self> {
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<ServoLayoutElement<'le>> {
+ fn next_sibling_element(&self) -> Option<ServoLayoutElement<'dom, LayoutDataType>> {
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> {
- self.get_jsmanaged()
- .first_child_ref()
- .map(ServoLayoutNode::from_layout_js)
- .map(Self::new)
- }
- unsafe fn dangerous_next_sibling(&self) -> Option<Self> {
- 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<Self>;
-
- fn opaque(&self) -> OpaqueNode {
- unsafe { self.get_jsmanaged().opaque() }
- }
-
- fn type_id(&self) -> Option<LayoutNodeType> {
- if self.pseudo == PseudoElementType::Normal {
- Some(self.node.type_id())
- } else {
- None
- }
- }
-
- fn parent_style(&self) -> Arc<ComputedValues> {
- 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<Self::ChildrenIterator> {
- 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<ServoThreadSafeLayoutElement<'ln>> {
- 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<Text> = 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<Range<ByteIndex>> {
- 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<ServoUrl> {
- let this = unsafe { self.get_jsmanaged() };
- this.image_url()
- }
-
- fn image_density(&self) -> Option<f64> {
- let this = unsafe { self.get_jsmanaged() };
- this.image_density()
- }
-
- fn image_data(&self) -> Option<(Option<StdArc<Image>>, Option<ImageMetadata>)> {
- let this = unsafe { self.get_jsmanaged() };
- this.image_data()
- }
-
- fn canvas_data(&self) -> Option<HTMLCanvasData> {
- let this = unsafe { self.get_jsmanaged() };
- this.canvas_data()
- }
-
- fn media_data(&self) -> Option<HTMLMediaData> {
- let this = unsafe { self.get_jsmanaged() };
- this.media_data()
- }
-
- fn svg_data(&self) -> Option<SVGSVGData> {
- 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<BrowsingContextId> {
- 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<PipelineId> {
- let this = unsafe { self.get_jsmanaged() };
- this.iframe_pipeline_id()
- }
-
- fn get_colspan(&self) -> u32 {
- unsafe {
- self.get_jsmanaged()
- .downcast::<Element>()
- .unwrap()
- .get_colspan()
- }
- }
-
- fn get_rowspan(&self) -> u32 {
- unsafe {
- self.get_jsmanaged()
- .downcast::<Element>()
- .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<ConcreteNode> {
- current_node: Option<ConcreteNode>,
- parent_node: ConcreteNode,
-}
-
-impl<'dom, ConcreteNode> ThreadSafeLayoutNodeChildrenIterator<ConcreteNode>
-where
- ConcreteNode: DangerousThreadSafeLayoutNode<'dom>,
+impl<'dom, LayoutDataType: LayoutDataTrait> Copy
+ for ServoThreadSafeLayoutElement<'dom, LayoutDataType>
{
- pub fn new(parent: ConcreteNode) -> Self {
- let first_child: Option<ConcreteNode> = 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<ConcreteNode>
-where
- ConcreteNode: DangerousThreadSafeLayoutNode<'dom>,
+impl<'dom, LayoutDataType: LayoutDataTrait> fmt::Debug
+ for ServoThreadSafeLayoutElement<'dom, LayoutDataType>
{
- type Item = ConcreteNode;
- fn next(&mut self) -> Option<ConcreteNode> {
- 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 00000000000..0a7c951c7d2
--- /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 00000000000..6d5eaecd5f1
--- /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<Node>` 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<LayoutDataType>,
+}
+
+//// 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<LayoutDataType: LayoutDataTrait> Send for ServoLayoutNode<'_, LayoutDataType> {}
+unsafe impl<LayoutDataType: LayoutDataTrait> 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, "<text node> ({:#x})", self.opaque().0)
+ } else {
+ write!(f, "<non-text node> ({:#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> {
+ self.node
+ .composed_parent_node_ref()
+ .map(Self::from_layout_js)
+ }
+
+ fn first_child(&self) -> Option<Self> {
+ self.node.first_child_ref().map(Self::from_layout_js)
+ }
+
+ fn last_child(&self) -> Option<Self> {
+ self.node.last_child_ref().map(Self::from_layout_js)
+ }
+
+ fn prev_sibling(&self) -> Option<Self> {
+ self.node.prev_sibling_ref().map(Self::from_layout_js)
+ }
+
+ fn next_sibling(&self) -> Option<Self> {
+ 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<ServoLayoutElement<'dom, LayoutDataType>> {
+ 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<ServoLayoutElement<'dom, LayoutDataType>> {
+ self.node.downcast().map(ServoLayoutElement::from_layout_js)
+ }
+
+ fn as_document(&self) -> Option<ServoLayoutDocument<'dom, LayoutDataType>> {
+ self.node
+ .downcast()
+ .map(ServoLayoutDocument::from_layout_js)
+ }
+
+ fn as_shadow_root(&self) -> Option<ServoShadowRoot<'dom, LayoutDataType>> {
+ 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<StyleAndOpaqueLayoutData>) {
+ self.get_jsmanaged().init_style_and_opaque_layout_data(data);
+ }
+
+ unsafe fn take_style_and_opaque_layout_data(&self) -> Box<StyleAndOpaqueLayoutData> {
+ 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> {
+ self.get_jsmanaged()
+ .first_child_ref()
+ .map(ServoLayoutNode::from_layout_js)
+ .map(Self::new)
+ }
+ unsafe fn dangerous_next_sibling(&self) -> Option<Self> {
+ 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<Self>;
+
+ fn opaque(&self) -> style::dom::OpaqueNode {
+ unsafe { self.get_jsmanaged().opaque() }
+ }
+
+ fn type_id(&self) -> Option<LayoutNodeType> {
+ if self.pseudo == PseudoElementType::Normal {
+ Some(self.node.type_id())
+ } else {
+ None
+ }
+ }
+
+ fn parent_style(&self) -> Arc<ComputedValues> {
+ 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<Self::ChildrenIterator> {
+ 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<ServoThreadSafeLayoutElement<'dom, LayoutDataType>> {
+ 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<Text> = 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<Range<ByteIndex>> {
+ 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<ServoUrl> {
+ let this = unsafe { self.get_jsmanaged() };
+ this.image_url()
+ }
+
+ fn image_density(&self) -> Option<f64> {
+ let this = unsafe { self.get_jsmanaged() };
+ this.image_density()
+ }
+
+ fn image_data(&self) -> Option<(Option<StdArc<Image>>, Option<ImageMetadata>)> {
+ let this = unsafe { self.get_jsmanaged() };
+ this.image_data()
+ }
+
+ fn canvas_data(&self) -> Option<HTMLCanvasData> {
+ let this = unsafe { self.get_jsmanaged() };
+ this.canvas_data()
+ }
+
+ fn media_data(&self) -> Option<HTMLMediaData> {
+ let this = unsafe { self.get_jsmanaged() };
+ this.media_data()
+ }
+
+ fn svg_data(&self) -> Option<SVGSVGData> {
+ 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<BrowsingContextId> {
+ 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<PipelineId> {
+ let this = unsafe { self.get_jsmanaged() };
+ this.iframe_pipeline_id()
+ }
+
+ fn get_colspan(&self) -> u32 {
+ unsafe {
+ self.get_jsmanaged()
+ .downcast::<Element>()
+ .unwrap()
+ .get_colspan()
+ }
+ }
+
+ fn get_rowspan(&self) -> u32 {
+ unsafe {
+ self.get_jsmanaged()
+ .downcast::<Element>()
+ .unwrap()
+ .get_rowspan()
+ }
+ }
+}
+
+pub struct ThreadSafeLayoutNodeChildrenIterator<ConcreteNode> {
+ current_node: Option<ConcreteNode>,
+ parent_node: ConcreteNode,
+}
+
+impl<'dom, ConcreteNode> ThreadSafeLayoutNodeChildrenIterator<ConcreteNode>
+where
+ ConcreteNode: DangerousThreadSafeLayoutNode<'dom>,
+{
+ pub fn new(parent: ConcreteNode) -> Self {
+ let first_child: Option<ConcreteNode> = 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<ConcreteNode>
+where
+ ConcreteNode: DangerousThreadSafeLayoutNode<'dom>,
+{
+ type Item = ConcreteNode;
+ fn next(&mut self) -> Option<ConcreteNode> {
+ 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 00000000000..2fd1511ccfa
--- /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<LayoutDataType>,
+}
+
+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::<ServoLayoutElement<LayoutDataType>>(device, quirks_mode, guard)
+ }
+}
diff --git a/components/script/lib.rs b/components/script/lib.rs
index b34b1fb11ee..66c878dbbde 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 10e6f826315..9091a394161 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,