aboutsummaryrefslogtreecommitdiffstats
path: root/components/shared/script_layout/wrapper_traits.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/shared/script_layout/wrapper_traits.rs')
-rw-r--r--components/shared/script_layout/wrapper_traits.rs497
1 files changed, 497 insertions, 0 deletions
diff --git a/components/shared/script_layout/wrapper_traits.rs b/components/shared/script_layout/wrapper_traits.rs
new file mode 100644
index 00000000000..1cc20c5f6ab
--- /dev/null
+++ b/components/shared/script_layout/wrapper_traits.rs
@@ -0,0 +1,497 @@
+/* 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 std::borrow::Cow;
+use std::fmt::Debug;
+use std::sync::Arc as StdArc;
+
+use atomic_refcell::AtomicRef;
+use gfx_traits::{combine_id_with_fragment_type, ByteIndex, FragmentType};
+use html5ever::{local_name, namespace_url, ns, LocalName, Namespace};
+use msg::constellation_msg::{BrowsingContextId, PipelineId};
+use net_traits::image::base::{Image, ImageMetadata};
+use range::Range;
+use servo_arc::Arc;
+use servo_url::ServoUrl;
+use style::attr::AttrValue;
+use style::context::SharedStyleContext;
+use style::data::ElementData;
+use style::dom::{LayoutIterator, NodeInfo, OpaqueNode, TElement, TNode};
+use style::properties::ComputedValues;
+use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl};
+use style::stylist::RuleInclusion;
+use webrender_api::ExternalScrollId;
+
+use crate::{HTMLCanvasData, HTMLMediaData, LayoutNodeType, SVGSVGData, StyleAndOpaqueLayoutData};
+
+pub trait LayoutDataTrait: Default + Send + Sync + 'static {}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum PseudoElementType {
+ Normal,
+ Before,
+ After,
+ DetailsSummary,
+ DetailsContent,
+}
+
+impl PseudoElementType {
+ pub fn fragment_type(&self) -> FragmentType {
+ match *self {
+ PseudoElementType::Normal => FragmentType::FragmentBody,
+ PseudoElementType::Before => FragmentType::BeforePseudoContent,
+ PseudoElementType::After => FragmentType::AfterPseudoContent,
+ PseudoElementType::DetailsSummary => FragmentType::FragmentBody,
+ PseudoElementType::DetailsContent => FragmentType::FragmentBody,
+ }
+ }
+
+ pub fn is_before(&self) -> bool {
+ match *self {
+ PseudoElementType::Before => true,
+ _ => false,
+ }
+ }
+
+ pub fn is_replaced_content(&self) -> bool {
+ match *self {
+ PseudoElementType::Before | PseudoElementType::After => true,
+ _ => false,
+ }
+ }
+
+ pub fn style_pseudo_element(&self) -> PseudoElement {
+ match *self {
+ PseudoElementType::Normal => {
+ unreachable!("style_pseudo_element called with PseudoElementType::Normal")
+ },
+ PseudoElementType::Before => PseudoElement::Before,
+ PseudoElementType::After => PseudoElement::After,
+ PseudoElementType::DetailsSummary => PseudoElement::DetailsSummary,
+ PseudoElementType::DetailsContent => PseudoElement::DetailsContent,
+ }
+ }
+}
+
+/// Trait to abstract access to layout data across various data structures.
+pub trait GetStyleAndOpaqueLayoutData<'dom> {
+ fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData>;
+}
+
+/// A wrapper so that layout can access only the methods that it should have access to. Layout must
+/// only ever see these and must never see instances of `LayoutDom`.
+/// FIXME(mrobinson): `Send + Sync` is required here for Layout 2020, but eventually it
+/// should stop sending LayoutNodes to other threads and rely on ThreadSafeLayoutNode
+/// or some other mechanism to ensure thread safety.
+pub trait LayoutNode<'dom>:
+ Copy + Debug + GetStyleAndOpaqueLayoutData<'dom> + TNode + Send + Sync
+{
+ type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'dom>;
+ fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode;
+
+ /// Returns the type ID of this node.
+ fn type_id(&self) -> LayoutNodeType;
+
+ unsafe fn initialize_data(&self);
+ unsafe fn init_style_and_opaque_layout_data(&self, data: Box<StyleAndOpaqueLayoutData>);
+ unsafe fn take_style_and_opaque_layout_data(&self) -> Box<StyleAndOpaqueLayoutData>;
+
+ fn rev_children(self) -> LayoutIterator<ReverseChildrenIterator<Self>> {
+ LayoutIterator(ReverseChildrenIterator {
+ current: self.last_child(),
+ })
+ }
+
+ fn traverse_preorder(self) -> TreeIterator<Self> {
+ TreeIterator::new(self)
+ }
+
+ /// Returns whether the node is connected.
+ fn is_connected(&self) -> bool;
+}
+
+pub struct ReverseChildrenIterator<ConcreteNode> {
+ current: Option<ConcreteNode>,
+}
+
+impl<'dom, ConcreteNode> Iterator for ReverseChildrenIterator<ConcreteNode>
+where
+ ConcreteNode: LayoutNode<'dom>,
+{
+ type Item = ConcreteNode;
+ fn next(&mut self) -> Option<ConcreteNode> {
+ let node = self.current;
+ self.current = node.and_then(|node| node.prev_sibling());
+ node
+ }
+}
+
+pub struct TreeIterator<ConcreteNode> {
+ stack: Vec<ConcreteNode>,
+}
+
+impl<'dom, ConcreteNode> TreeIterator<ConcreteNode>
+where
+ ConcreteNode: LayoutNode<'dom>,
+{
+ fn new(root: ConcreteNode) -> TreeIterator<ConcreteNode> {
+ let mut stack = vec![];
+ stack.push(root);
+ TreeIterator { stack: stack }
+ }
+
+ pub fn next_skipping_children(&mut self) -> Option<ConcreteNode> {
+ self.stack.pop()
+ }
+}
+
+impl<'dom, ConcreteNode> Iterator for TreeIterator<ConcreteNode>
+where
+ ConcreteNode: LayoutNode<'dom>,
+{
+ type Item = ConcreteNode;
+ fn next(&mut self) -> Option<ConcreteNode> {
+ let ret = self.stack.pop();
+ ret.map(|node| self.stack.extend(node.rev_children()));
+ ret
+ }
+}
+
+/// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout
+/// node does not allow any parents or siblings of nodes to be accessed, to avoid races.
+pub trait ThreadSafeLayoutNode<'dom>:
+ Clone + Copy + Debug + GetStyleAndOpaqueLayoutData<'dom> + NodeInfo + PartialEq + Sized
+{
+ type ConcreteNode: LayoutNode<'dom, ConcreteThreadSafeLayoutNode = Self>;
+ type ConcreteElement: TElement;
+
+ type ConcreteThreadSafeLayoutElement: ThreadSafeLayoutElement<'dom, ConcreteThreadSafeLayoutNode = Self>
+ + ::selectors::Element<Impl = SelectorImpl>;
+ type ChildrenIterator: Iterator<Item = Self> + Sized;
+
+ /// Converts self into an `OpaqueNode`.
+ fn opaque(&self) -> OpaqueNode;
+
+ /// Returns the type ID of this node.
+ /// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
+ fn type_id(&self) -> Option<LayoutNodeType>;
+
+ /// Returns the style for a text node. This is computed on the fly from the
+ /// parent style to avoid traversing text nodes in the style system.
+ ///
+ /// Note that this does require accessing the parent, which this interface
+ /// technically forbids. But accessing the parent is only unsafe insofar as
+ /// it can be used to reach siblings and cousins. A simple immutable borrow
+ /// of the parent data is fine, since the bottom-up traversal will not process
+ /// the parent until all the children have been processed.
+ fn parent_style(&self) -> Arc<ComputedValues>;
+
+ fn get_before_pseudo(&self) -> Option<Self> {
+ self.as_element()
+ .and_then(|el| el.get_before_pseudo())
+ .map(|el| el.as_node())
+ }
+
+ fn get_after_pseudo(&self) -> Option<Self> {
+ self.as_element()
+ .and_then(|el| el.get_after_pseudo())
+ .map(|el| el.as_node())
+ }
+
+ fn get_details_summary_pseudo(&self) -> Option<Self> {
+ self.as_element()
+ .and_then(|el| el.get_details_summary_pseudo())
+ .map(|el| el.as_node())
+ }
+
+ fn get_details_content_pseudo(&self) -> Option<Self> {
+ self.as_element()
+ .and_then(|el| el.get_details_content_pseudo())
+ .map(|el| el.as_node())
+ }
+
+ fn debug_id(self) -> usize;
+
+ /// Returns an iterator over this node's children.
+ fn children(&self) -> LayoutIterator<Self::ChildrenIterator>;
+
+ /// Returns a ThreadSafeLayoutElement if this is an element, None otherwise.
+ fn as_element(&self) -> Option<Self::ConcreteThreadSafeLayoutElement>;
+
+ #[inline]
+ fn get_pseudo_element_type(&self) -> PseudoElementType {
+ self.as_element()
+ .map_or(PseudoElementType::Normal, |el| el.get_pseudo_element_type())
+ }
+
+ fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData>;
+
+ fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
+ if let Some(el) = self.as_element() {
+ el.style(context)
+ } else {
+ // Text nodes are not styled during traversal,instead we simply
+ // return parent style here and do cascading during layout.
+ debug_assert!(self.is_text_node());
+ self.parent_style()
+ }
+ }
+
+ fn selected_style(&self) -> Arc<ComputedValues> {
+ if let Some(el) = self.as_element() {
+ el.selected_style()
+ } else {
+ debug_assert!(self.is_text_node());
+ // TODO(stshine): What should the selected style be for text?
+ self.parent_style()
+ }
+ }
+
+ fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool;
+
+ /// Returns true if this node contributes content. This is used in the implementation of
+ /// `empty_cells` per CSS 2.1 § 17.6.1.1.
+ fn is_content(&self) -> bool {
+ self.type_id().is_some()
+ }
+
+ /// Returns access to the underlying LayoutNode. This is breaks the abstraction
+ /// barrier of ThreadSafeLayout wrapper layer, and can lead to races if not used
+ /// carefully.
+ ///
+ /// We need this because the implementation of some methods need to access the layout
+ /// data flags, and we have this annoying trait separation between script and layout :-(
+ unsafe fn unsafe_get(self) -> Self::ConcreteNode;
+
+ fn node_text_content(self) -> Cow<'dom, str>;
+
+ /// If the insertion point is within this node, returns it. Otherwise, returns `None`.
+ fn selection(&self) -> Option<Range<ByteIndex>>;
+
+ /// If this is an image element, returns its URL. If this is not an image element, fails.
+ fn image_url(&self) -> Option<ServoUrl>;
+
+ /// If this is an image element, returns its current-pixel-density. If this is not an image element, fails.
+ fn image_density(&self) -> Option<f64>;
+
+ /// If this is an image element, returns its image data. Otherwise, returns `None`.
+ fn image_data(&self) -> Option<(Option<StdArc<Image>>, Option<ImageMetadata>)>;
+
+ fn canvas_data(&self) -> Option<HTMLCanvasData>;
+
+ fn svg_data(&self) -> Option<SVGSVGData>;
+
+ fn media_data(&self) -> Option<HTMLMediaData>;
+
+ /// If this node is an iframe element, returns its browsing context ID. If this node is
+ /// not an iframe element, fails. Returns None if there is no nested browsing context.
+ fn iframe_browsing_context_id(&self) -> Option<BrowsingContextId>;
+
+ /// If this node is an iframe element, returns its pipeline ID. If this node is
+ /// not an iframe element, fails. Returns None if there is no nested browsing context.
+ fn iframe_pipeline_id(&self) -> Option<PipelineId>;
+
+ fn get_colspan(&self) -> u32;
+
+ fn get_rowspan(&self) -> u32;
+
+ fn fragment_type(&self) -> FragmentType {
+ self.get_pseudo_element_type().fragment_type()
+ }
+
+ fn generate_scroll_id(&self, pipeline_id: PipelineId) -> ExternalScrollId {
+ let id = combine_id_with_fragment_type(self.opaque().id(), self.fragment_type());
+ ExternalScrollId(id as u64, pipeline_id.to_webrender())
+ }
+}
+
+pub trait ThreadSafeLayoutElement<'dom>:
+ Clone
+ + Copy
+ + Sized
+ + Debug
+ + ::selectors::Element<Impl = SelectorImpl>
+ + GetStyleAndOpaqueLayoutData<'dom>
+{
+ type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<
+ 'dom,
+ ConcreteThreadSafeLayoutElement = Self,
+ >;
+
+ /// This type alias is just a work-around to avoid writing
+ ///
+ /// <Self::ConcreteThreadSafeLayoutNode as ThreadSafeLayoutNode>::ConcreteElement
+ ///
+ type ConcreteElement: TElement;
+
+ fn as_node(&self) -> Self::ConcreteThreadSafeLayoutNode;
+
+ /// Creates a new `ThreadSafeLayoutElement` for the same `LayoutElement`
+ /// with a different pseudo-element type.
+ fn with_pseudo(&self, pseudo: PseudoElementType) -> Self;
+
+ /// Returns the type ID of this node.
+ /// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
+ fn type_id(&self) -> Option<LayoutNodeType>;
+
+ /// Returns access to the underlying TElement. This is breaks the abstraction
+ /// barrier of ThreadSafeLayout wrapper layer, and can lead to races if not used
+ /// carefully.
+ ///
+ /// We need this so that the functions defined on this trait can call
+ /// lazily_compute_pseudo_element_style, which operates on TElement.
+ unsafe fn unsafe_get(self) -> Self::ConcreteElement;
+
+ fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str>;
+
+ fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue>;
+
+ fn style_data(&self) -> AtomicRef<ElementData>;
+
+ fn get_pseudo_element_type(&self) -> PseudoElementType;
+
+ #[inline]
+ fn get_before_pseudo(&self) -> Option<Self> {
+ if self
+ .style_data()
+ .styles
+ .pseudos
+ .get(&PseudoElement::Before)
+ .is_some()
+ {
+ Some(self.with_pseudo(PseudoElementType::Before))
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn get_after_pseudo(&self) -> Option<Self> {
+ if self
+ .style_data()
+ .styles
+ .pseudos
+ .get(&PseudoElement::After)
+ .is_some()
+ {
+ Some(self.with_pseudo(PseudoElementType::After))
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn get_details_summary_pseudo(&self) -> Option<Self> {
+ if self.has_local_name(&local_name!("details")) && self.has_namespace(&ns!(html)) {
+ Some(self.with_pseudo(PseudoElementType::DetailsSummary))
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn get_details_content_pseudo(&self) -> Option<Self> {
+ if self.has_local_name(&local_name!("details")) &&
+ self.has_namespace(&ns!(html)) &&
+ self.get_attr(&ns!(), &local_name!("open")).is_some()
+ {
+ Some(self.with_pseudo(PseudoElementType::DetailsContent))
+ } else {
+ None
+ }
+ }
+
+ /// Returns the style results for the given node. If CSS selector matching
+ /// has not yet been performed, fails.
+ ///
+ /// Unlike the version on TNode, this handles pseudo-elements.
+ #[inline]
+ fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
+ let data = self.style_data();
+ match self.get_pseudo_element_type() {
+ PseudoElementType::Normal => data.styles.primary().clone(),
+ other => {
+ // Precompute non-eagerly-cascaded pseudo-element styles if not
+ // cached before.
+ let style_pseudo = other.style_pseudo_element();
+ match style_pseudo.cascade_type() {
+ // Already computed during the cascade.
+ PseudoElementCascadeType::Eager => self
+ .style_data()
+ .styles
+ .pseudos
+ .get(&style_pseudo)
+ .unwrap()
+ .clone(),
+ PseudoElementCascadeType::Precomputed => context
+ .stylist
+ .precomputed_values_for_pseudo::<Self::ConcreteElement>(
+ &context.guards,
+ &style_pseudo,
+ Some(data.styles.primary()),
+ ),
+ PseudoElementCascadeType::Lazy => {
+ context
+ .stylist
+ .lazily_compute_pseudo_element_style(
+ &context.guards,
+ unsafe { self.unsafe_get() },
+ &style_pseudo,
+ RuleInclusion::All,
+ data.styles.primary(),
+ /* is_probe = */ false,
+ /* matching_func = */ None,
+ )
+ .unwrap()
+ },
+ }
+ },
+ }
+ }
+
+ #[inline]
+ fn selected_style(&self) -> Arc<ComputedValues> {
+ let data = self.style_data();
+ data.styles
+ .pseudos
+ .get(&PseudoElement::Selection)
+ .unwrap_or(data.styles.primary())
+ .clone()
+ }
+
+ /// Returns the already resolved style of the node.
+ ///
+ /// This differs from `style(ctx)` in that if the pseudo-element has not yet
+ /// been computed it would panic.
+ ///
+ /// This should be used just for querying layout, or when we know the
+ /// element style is precomputed, not from general layout itself.
+ #[inline]
+ fn resolved_style(&self) -> Arc<ComputedValues> {
+ let data = self.style_data();
+ match self.get_pseudo_element_type() {
+ PseudoElementType::Normal => data.styles.primary().clone(),
+ other => data
+ .styles
+ .pseudos
+ .get(&other.style_pseudo_element())
+ .unwrap()
+ .clone(),
+ }
+ }
+
+ fn is_shadow_host(&self) -> bool;
+
+ /// Returns whether this node is a body element of an html element root
+ /// in an HTML element document.
+ ///
+ /// Note that this does require accessing the parent, which this interface
+ /// technically forbids. But accessing the parent is only unsafe insofar as
+ /// it can be used to reach siblings and cousins. A simple immutable borrow
+ /// of the parent data is fine, since the bottom-up traversal will not process
+ /// the parent until all the children have been processed.
+ fn is_body_element_of_html_element_root(&self) -> bool;
+}