diff --git a/components/gfx/text/glyph.rs b/components/gfx/text/glyph.rs index fe9dc415625..9e5e7cf732e 100644 --- a/components/gfx/text/glyph.rs +++ b/components/gfx/text/glyph.rs @@ -11,6 +11,8 @@ use std::cmp::{Ordering, PartialOrd}; use std::vec::Vec; use std::{fmt, mem, u16}; +pub use gfx_traits::ByteIndex; + /// GlyphEntry is a port of Gecko's CompressedGlyph scheme for storing glyph data compactly. /// /// In the common case (reasonable glyph advances, no offsets from the font em-box, and one glyph @@ -426,14 +428,6 @@ pub struct GlyphStore { is_rtl: bool, } -int_range_index! { - #[derive(Deserialize, Serialize, RustcEncodable)] - #[doc = "An index that refers to a byte offset in a text run. This could \ - point to the middle of a glyph."] - #[derive(HeapSizeOf)] - struct ByteIndex(isize) -} - impl<'a> GlyphStore { /// Initializes the glyph store, but doesn't actually shape anything. /// diff --git a/components/gfx_traits/Cargo.toml b/components/gfx_traits/Cargo.toml index 18b2d5abce8..133faf344b3 100644 --- a/components/gfx_traits/Cargo.toml +++ b/components/gfx_traits/Cargo.toml @@ -13,6 +13,8 @@ azure = {git = "https://github.com/servo/rust-azure", features = ["plugins"]} layers = {git = "https://github.com/servo/rust-layers", features = ["plugins"]} msg = {path = "../msg"} plugins = {path = "../plugins"} +range = {path = "../range"} +rustc-serialize = "0.3" euclid = {version = "0.6.5", features = ["plugins"]} heapsize = "0.3.0" heapsize_plugin = "0.1.2" diff --git a/components/gfx_traits/lib.rs b/components/gfx_traits/lib.rs index eb832c1c788..a131792331d 100644 --- a/components/gfx_traits/lib.rs +++ b/components/gfx_traits/lib.rs @@ -15,6 +15,9 @@ extern crate euclid; extern crate heapsize; extern crate layers; extern crate msg; +#[macro_use] +extern crate range; +extern crate rustc_serialize; extern crate serde; pub mod color; @@ -25,6 +28,7 @@ use azure::azure_hl::Color; use euclid::Matrix4D; use euclid::rect::Rect; use msg::constellation_msg::PipelineId; +use range::RangeIndex; use std::fmt::{self, Debug, Formatter}; use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering}; @@ -255,3 +259,10 @@ impl FragmentType { } } +int_range_index! { + #[derive(Deserialize, Serialize, RustcEncodable)] + #[doc = "An index that refers to a byte offset in a text run. This could \ + point to the middle of a glyph."] + #[derive(HeapSizeOf)] + struct ByteIndex(isize) +} diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml index f200aa86607..c6b1727d1d5 100644 --- a/components/layout/Cargo.toml +++ b/components/layout/Cargo.toml @@ -31,6 +31,7 @@ profile_traits = {path = "../profile_traits"} range = {path = "../range"} rustc-serialize = "0.3" script = {path = "../script"} +script_layout_interface = {path = "../script_layout_interface"} script_traits = {path = "../script_traits"} selectors = {version = "0.6", features = ["heap_size"]} serde_json = "0.7" diff --git a/components/layout/animation.rs b/components/layout/animation.rs index e9a530e7d90..cc87a2824a4 100644 --- a/components/layout/animation.rs +++ b/components/layout/animation.rs @@ -6,9 +6,9 @@ use flow::{self, Flow}; use gfx::display_list::OpaqueNode; -use incremental::RestyleDamage; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::PipelineId; +use script_layout_interface::restyle_damage::RestyleDamage; use script_traits::{AnimationState, LayoutMsg as ConstellationMsg}; use std::collections::HashMap; use std::collections::hash_map::Entry; diff --git a/components/layout/block.rs b/components/layout/block.rs index 6571d7d708a..9fc527d98d5 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -45,12 +45,12 @@ use fragment::SpecificFragmentInfo; use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, HAS_LAYER, Overflow}; use gfx::display_list::{ClippingRegion, StackingContext}; use gfx_traits::{LayerId, StackingContextId}; -use incremental::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT}; use layout_debug; use layout_thread::DISPLAY_PORT_SIZE_FACTOR; use model::{CollapsibleMargins, MaybeAuto, specified, specified_or_none}; use model::{self, IntrinsicISizes, MarginCollapseInfo}; use rustc_serialize::{Encodable, Encoder}; +use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT}; use std::cmp::{max, min}; use std::fmt; use std::sync::Arc; diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 49546fb2663..40ffdc42047 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -28,15 +28,14 @@ use fragment::{Fragment, GeneratedContentInfo, IframeFragmentInfo}; use fragment::{InlineAbsoluteHypotheticalFragmentInfo, TableColumnFragmentInfo}; use fragment::{InlineBlockFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo}; use gfx::display_list::OpaqueNode; -use incremental::{BUBBLE_ISIZES, RECONSTRUCT_FLOW, RestyleDamage}; use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, InlineFragmentNodeFlags}; use inline::{InlineFragmentNodeInfo, LAST_FRAGMENT_OF_ELEMENT}; use list_item::{ListItemFlow, ListStyleTypeContent}; use multicol::{MulticolFlow, MulticolColumnFlow}; use parallel; -use script::layout_interface::is_image_data; -use script::layout_interface::{CharacterDataTypeId, ElementTypeId}; -use script::layout_interface::{HTMLElementTypeId, NodeTypeId}; +use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, RECONSTRUCT_FLOW, RestyleDamage}; +use script_layout_interface::wrapper_traits::{ThreadSafeLayoutNode, PseudoElementType, ThreadSafeLayoutElement}; +use script_layout_interface::{LayoutNodeType, LayoutElementType, is_image_data}; use std::borrow::ToOwned; use std::collections::LinkedList; use std::marker::PhantomData; @@ -60,7 +59,7 @@ use traversal::PostorderNodeMutTraversal; use url::Url; use util::linked_list; use util::opts; -use wrapper::{PseudoElementType, TextContent, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; +use wrapper::{TextContent, ThreadSafeLayoutNodeHelpers}; /// The results of flow construction for a DOM node. #[derive(Clone)] @@ -303,44 +302,35 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> /// Builds the fragment for the given block or subclass thereof. fn build_fragment_for_block(&mut self, node: &ConcreteThreadSafeLayoutNode) -> Fragment { let specific_fragment_info = match node.type_id() { - Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLIFrameElement))) => { + Some(LayoutNodeType::Element(LayoutElementType::HTMLIFrameElement)) => { SpecificFragmentInfo::Iframe(IframeFragmentInfo::new(node)) } - Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLImageElement))) => { + Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) => { let image_info = box ImageFragmentInfo::new(node, node.image_url(), &self.layout_context); SpecificFragmentInfo::Image(image_info) } - Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLObjectElement))) => { + Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => { let image_info = box ImageFragmentInfo::new(node, node.object_data(), &self.layout_context); SpecificFragmentInfo::Image(image_info) } - Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLTableElement))) => { + Some(LayoutNodeType::Element(LayoutElementType::HTMLTableElement)) => { SpecificFragmentInfo::TableWrapper } - Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLTableColElement))) => { + Some(LayoutNodeType::Element(LayoutElementType::HTMLTableColElement)) => { SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node)) } - Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLTableCellElement(_)))) => { + Some(LayoutNodeType::Element(LayoutElementType::HTMLTableCellElement)) => { SpecificFragmentInfo::TableCell } - Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLTableRowElement))) | - Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLTableSectionElement))) => { + Some(LayoutNodeType::Element(LayoutElementType::HTMLTableRowElement)) | + Some(LayoutNodeType::Element(LayoutElementType::HTMLTableSectionElement)) => { SpecificFragmentInfo::TableRow } - Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLCanvasElement))) => { + Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) => { let data = node.canvas_data().unwrap(); SpecificFragmentInfo::Canvas(box CanvasFragmentInfo::new(node, data, self.layout_context)) } @@ -689,16 +679,13 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> -> ConstructionResult { let mut initial_fragments = IntermediateInlineFragments::new(); let node_is_input_or_text_area = - node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLInputElement))) || - node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLTextAreaElement))); + node.type_id() == Some(LayoutNodeType::Element(LayoutElementType::HTMLInputElement)) || + node.type_id() == Some(LayoutNodeType::Element(LayoutElementType::HTMLTextAreaElement)); if node.get_pseudo_element_type().is_replaced_content() || node_is_input_or_text_area { // A TextArea's text contents are displayed through the input text // box, so don't construct them. - if node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLTextAreaElement))) { + if node.type_id() == Some(LayoutNodeType::Element(LayoutElementType::HTMLTextAreaElement)) { for kid in node.children() { self.set_flow_construction_result(&kid, ConstructionResult::None) } @@ -970,7 +957,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> // fragment that needs to be generated for this inline node. let mut fragments = IntermediateInlineFragments::new(); match (node.get_pseudo_element_type(), node.type_id()) { - (_, Some(NodeTypeId::CharacterData(CharacterDataTypeId::Text))) => { + (_, Some(LayoutNodeType::Text)) => { self.create_fragments_for_node_text_content(&mut fragments, node, &style) } (PseudoElementType::Normal, _) => { @@ -1524,7 +1511,7 @@ impl<'a, ConcreteThreadSafeLayoutNode> PostorderNodeMutTraversal { + Some(LayoutNodeType::Element(_)) => { let style = node.style(self.style_context()); let original_display = style.get_box()._servo_display_for_hypothetical_box; let munged_display = match original_display { @@ -1533,13 +1520,13 @@ impl<'a, ConcreteThreadSafeLayoutNode> PostorderNodeMutTraversal + Some(LayoutNodeType::Text) => (display::T::inline, float::T::none, position::T::static_), - Some(NodeTypeId::CharacterData(CharacterDataTypeId::Comment)) | - Some(NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction)) | - Some(NodeTypeId::DocumentType) | - Some(NodeTypeId::DocumentFragment) | - Some(NodeTypeId::Document(_)) => { + Some(LayoutNodeType::Comment) | + Some(LayoutNodeType::ProcessingInstruction) | + Some(LayoutNodeType::DocumentType) | + Some(LayoutNodeType::DocumentFragment) | + Some(LayoutNodeType::Document) => { (display::T::none, float::T::none, position::T::static_) } }; @@ -1678,19 +1665,17 @@ impl NodeUtils for ConcreteThreadSafeLayoutNode where ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode { fn is_replaced_content(&self) -> bool { match self.type_id() { - Some(NodeTypeId::CharacterData(_)) | - Some(NodeTypeId::DocumentType) | - Some(NodeTypeId::DocumentFragment) | - Some(NodeTypeId::Document(_)) | - Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLImageElement))) | - Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLIFrameElement))) | - Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLCanvasElement))) => true, - Some(NodeTypeId::Element(ElementTypeId::HTMLElement( - HTMLElementTypeId::HTMLObjectElement))) => self.has_object_data(), - Some(NodeTypeId::Element(_)) => false, + Some(LayoutNodeType::Comment) | + Some(LayoutNodeType::ProcessingInstruction) | + Some(LayoutNodeType::Text) | + Some(LayoutNodeType::DocumentType) | + Some(LayoutNodeType::DocumentFragment) | + Some(LayoutNodeType::Document) | + Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) | + Some(LayoutNodeType::Element(LayoutElementType::HTMLIFrameElement)) | + Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) => true, + Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => self.has_object_data(), + Some(LayoutNodeType::Element(_)) => false, None => self.get_pseudo_element_type().is_replaced_content(), } } diff --git a/components/layout/data.rs b/components/layout/data.rs index 10c72667aae..d189814f788 100644 --- a/components/layout/data.rs +++ b/components/layout/data.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use construct::ConstructionResult; -use incremental::RestyleDamage; +use script_layout_interface::restyle_damage::RestyleDamage; use style::servo::PrivateStyleData; /// Data that layout associates with a node. diff --git a/components/layout/flex.rs b/components/layout/flex.rs index b977124bf80..dd279bbdfdf 100644 --- a/components/layout/flex.rs +++ b/components/layout/flex.rs @@ -19,9 +19,9 @@ use flow_ref::{self, FlowRef}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; use gfx::display_list::StackingContext; use gfx_traits::StackingContextId; -use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; use layout_debug; use model::{IntrinsicISizes, MaybeAuto, MinMaxConstraint}; +use script_layout_interface::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW}; use std::cmp::max; use std::sync::Arc; use style::computed_values::flex_direction; diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 350092acc90..3dc7fac29dc 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -36,12 +36,13 @@ use flow_ref::{self, FlowRef, WeakFlowRef}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow, SpecificFragmentInfo}; use gfx::display_list::{ClippingRegion, StackingContext}; use gfx_traits::{LayerId, LayerType, StackingContextId}; -use incremental::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage}; use inline::InlineFlow; use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo}; use multicol::MulticolFlow; use parallel::FlowParallelInfo; use rustc_serialize::{Encodable, Encoder}; +use script_layout_interface::restyle_damage::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage}; +use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutNode}; use std::iter::Zip; use std::slice::IterMut; use std::sync::Arc; @@ -60,7 +61,6 @@ use table_row::TableRowFlow; use table_rowgroup::TableRowGroupFlow; use table_wrapper::TableWrapperFlow; use util::print_tree::PrintTree; -use wrapper::{PseudoElementType, ThreadSafeLayoutNode}; /// Virtual methods that make up a float context. /// diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 80664d720c8..28d88e48da3 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -18,7 +18,6 @@ use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode}; use gfx::text::glyph::ByteIndex; use gfx::text::text_run::{TextRun, TextRunSlice}; use gfx_traits::{FragmentType, LayerId, LayerType, StackingContextId}; -use incremental::{RECONSTRUCT_FLOW, RestyleDamage}; use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFragmentContext, InlineFragmentNodeInfo}; use inline::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT}; use ipc_channel::ipc::IpcSender; @@ -30,7 +29,9 @@ use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder}; use range::*; use rustc_serialize::{Encodable, Encoder}; -use script::layout_interface::HTMLCanvasData; +use script_layout_interface::HTMLCanvasData; +use script_layout_interface::restyle_damage::{RECONSTRUCT_FLOW, RestyleDamage}; +use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use std::borrow::ToOwned; use std::cmp::{max, min}; use std::collections::LinkedList; @@ -49,7 +50,6 @@ use text; use text::TextRunScanner; use url::Url; use util; -use wrapper::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; /// Fragments (`struct Fragment`) are the leaves of the layout tree. They cannot position /// themselves. In general, fragments do not have a simple correspondence with CSS fragments in the diff --git a/components/layout/generated_content.rs b/components/layout/generated_content.rs index a7e9c8f463a..33a1d21f0d1 100644 --- a/components/layout/generated_content.rs +++ b/components/layout/generated_content.rs @@ -13,7 +13,8 @@ use flow::InorderFlowTraversal; use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, ImmutableFlowUtils}; use fragment::{Fragment, GeneratedContentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo}; use gfx::display_list::OpaqueNode; -use incremental::{RESOLVE_GENERATED_CONTENT, RestyleDamage}; +use script_layout_interface::restyle_damage::{RESOLVE_GENERATED_CONTENT, RestyleDamage}; +use script_layout_interface::wrapper_traits::PseudoElementType; use smallvec::SmallVec; use std::collections::{HashMap, LinkedList}; use std::sync::Arc; @@ -22,7 +23,6 @@ use style::computed_values::{display, list_style_type}; use style::dom::TRestyleDamage; use style::properties::{ComputedValues, ServoComputedValues}; use text::TextRunScanner; -use wrapper::PseudoElementType; // Decimal styles per CSS-COUNTER-STYLES § 6.1: static DECIMAL: [char; 10] = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ]; diff --git a/components/layout/incremental.rs b/components/layout/incremental.rs index a95a60e002a..14644be6ddc 100644 --- a/components/layout/incremental.rs +++ b/components/layout/incremental.rs @@ -3,46 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, IS_ABSOLUTELY_POSITIONED}; -use std::fmt; -use std::sync::Arc; -use style::computed_values::{display, float}; +use script_layout_interface::restyle_damage::{RestyleDamage, REFLOW, RECONSTRUCT_FLOW}; +use style::computed_values::float; use style::dom::TRestyleDamage; -use style::properties::{ComputedValues, ServoComputedValues}; - -bitflags! { - #[doc = "Individual layout actions that may be necessary after restyling."] - pub flags RestyleDamage: u8 { - #[doc = "Repaint the node itself."] - #[doc = "Currently unused; need to decide how this propagates."] - const REPAINT = 0x01, - - #[doc = "Recompute the overflow regions (bounding box of object and all descendants)."] - #[doc = "Propagates down the flow tree because the computation is bottom-up."] - const STORE_OVERFLOW = 0x02, - - #[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."] - #[doc = "Propagates down the flow tree because the computation is"] - #[doc = "bottom-up."] - const BUBBLE_ISIZES = 0x04, - - #[doc = "Recompute actual inline-sizes and block-sizes, only taking out-of-flow children \ - into account. \ - Propagates up the flow tree because the computation is top-down."] - const REFLOW_OUT_OF_FLOW = 0x08, - - #[doc = "Recompute actual inline_sizes and block_sizes."] - #[doc = "Propagates up the flow tree because the computation is"] - #[doc = "top-down."] - const REFLOW = 0x10, - - #[doc = "Re-resolve generated content. \ - Propagates up the flow tree because the computation is inorder."] - const RESOLVE_GENERATED_CONTENT = 0x20, - - #[doc = "The entire flow needs to be reconstructed."] - const RECONSTRUCT_FLOW = 0x40 - } -} bitflags! { pub flags SpecialRestyleDamage: u8 { @@ -52,221 +15,6 @@ bitflags! { } } -impl TRestyleDamage for RestyleDamage { - type ConcreteComputedValues = ServoComputedValues; - fn compute(old: Option<&Arc>, new: &ServoComputedValues) -> - RestyleDamage { compute_damage(old, new) } - - /// Returns a bitmask that represents a flow that needs to be rebuilt and reflowed. - /// - /// Use this instead of `RestyleDamage::all()` because `RestyleDamage::all()` will result in - /// unnecessary sequential resolution of generated content. - fn rebuild_and_reflow() -> RestyleDamage { - REPAINT | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW - } -} - - -impl RestyleDamage { - /// Supposing a flow has the given `position` property and this damage, returns the damage that - /// we should add to the *parent* of this flow. - pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage { - if child_is_absolutely_positioned { - self & (REPAINT | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT) - } else { - self & (REPAINT | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW | - RESOLVE_GENERATED_CONTENT) - } - } - - /// Supposing the *parent* of a flow with the given `position` property has this damage, - /// returns the damage that we should add to this flow. - pub fn damage_for_child(self, - parent_is_absolutely_positioned: bool, - child_is_absolutely_positioned: bool) - -> RestyleDamage { - match (parent_is_absolutely_positioned, child_is_absolutely_positioned) { - (false, true) => { - // Absolute children are out-of-flow and therefore insulated from changes. - // - // FIXME(pcwalton): Au contraire, if the containing block dimensions change! - self & REPAINT - } - (true, false) => { - // Changing the position of an absolutely-positioned block requires us to reflow - // its kids. - if self.contains(REFLOW_OUT_OF_FLOW) { - self | REFLOW - } else { - self - } - } - _ => { - // TODO(pcwalton): Take floatedness into account. - self & (REPAINT | REFLOW) - } - } - } -} - -impl fmt::Display for RestyleDamage { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - let mut first_elem = true; - - let to_iter = - [ (REPAINT, "Repaint") - , (STORE_OVERFLOW, "StoreOverflow") - , (BUBBLE_ISIZES, "BubbleISizes") - , (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow") - , (REFLOW, "Reflow") - , (RESOLVE_GENERATED_CONTENT, "ResolveGeneratedContent") - , (RECONSTRUCT_FLOW, "ReconstructFlow") - ]; - - for &(damage, damage_str) in &to_iter { - if self.contains(damage) { - if !first_elem { try!(write!(f, " | ")); } - try!(write!(f, "{}", damage_str)); - first_elem = false; - } - } - - if first_elem { - try!(write!(f, "NoDamage")); - } - - Ok(()) - } -} - -// NB: We need the braces inside the RHS due to Rust #8012. This particular -// version of this macro might be safe anyway, but we want to avoid silent -// breakage on modifications. -macro_rules! add_if_not_equal( - ($old:ident, $new:ident, $damage:ident, - [ $($effect:ident),* ], [ $($style_struct_getter:ident.$name:ident),* ]) => ({ - if $( ($old.$style_struct_getter().$name != $new.$style_struct_getter().$name) )||* { - $damage.insert($($effect)|*); - true - } else { - false - } - }) -); - -pub fn compute_damage(old: Option<&Arc>, new: &ServoComputedValues) -> RestyleDamage { - let old: &ServoComputedValues = match old { - None => return RestyleDamage::rebuild_and_reflow(), - Some(cv) => &**cv, - }; - - let mut damage = RestyleDamage::empty(); - - // This should check every CSS property, as enumerated in the fields of - // http://doc.servo.org/style/properties/struct.ServoComputedValues.html - - // FIXME: Test somehow that every property is included. - - add_if_not_equal!(old, new, damage, - [ - REPAINT, - STORE_OVERFLOW, - BUBBLE_ISIZES, - REFLOW_OUT_OF_FLOW, - REFLOW, - RECONSTRUCT_FLOW - ], [ - get_box.float, get_box.display, get_box.position, get_counters.content, - get_counters.counter_reset, get_counters.counter_increment, - get_inheritedbox._servo_under_display_none, - get_list.quotes, get_list.list_style_type, - - // If these text or font properties change, we need to reconstruct the flow so that - // text shaping is re-run. - get_inheritedtext.letter_spacing, get_inheritedtext.text_rendering, - get_inheritedtext.text_transform, get_inheritedtext.word_spacing, - get_inheritedtext.overflow_wrap, get_inheritedtext.text_justify, - get_inheritedtext.white_space, get_inheritedtext.word_break, get_text.text_overflow, - get_font.font_family, get_font.font_style, get_font.font_variant, get_font.font_weight, - get_font.font_size, get_font.font_stretch, - get_inheritedbox.direction, get_inheritedbox.writing_mode, - get_inheritedbox.text_orientation, - get_text.text_decoration, get_text.unicode_bidi, - get_inheritedtable.empty_cells, get_inheritedtable.caption_side, - get_column.column_width, get_column.column_count - ]) || (new.get_box().display == display::T::inline && - add_if_not_equal!(old, new, damage, - [REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW, - RECONSTRUCT_FLOW], [ - // For inline boxes only, border/padding styles are used in flow construction (to decide - // whether to create fragments for empty flows). - get_border.border_top_width, get_border.border_right_width, - get_border.border_bottom_width, get_border.border_left_width, - get_padding.padding_top, get_padding.padding_right, - get_padding.padding_bottom, get_padding.padding_left - ])) || add_if_not_equal!(old, new, damage, - [ REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW ], - [get_border.border_top_width, get_border.border_right_width, - get_border.border_bottom_width, get_border.border_left_width, - get_margin.margin_top, get_margin.margin_right, - get_margin.margin_bottom, get_margin.margin_left, - get_padding.padding_top, get_padding.padding_right, - get_padding.padding_bottom, get_padding.padding_left, - get_position.width, get_position.height, - get_inheritedtext.line_height, - get_inheritedtext.text_align, get_inheritedtext.text_indent, - get_table.table_layout, - get_inheritedtable.border_collapse, - get_inheritedtable.border_spacing, - get_column.column_gap, - get_position.flex_direction, - get_position.flex_wrap, - get_position.justify_content, - get_position.align_items, - get_position.align_content, - get_position.order, - get_position.flex_basis, - get_position.flex_grow, - get_position.flex_shrink, - get_position.align_self - ]) || add_if_not_equal!(old, new, damage, - [ REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW ], [ - get_position.top, get_position.left, - get_position.right, get_position.bottom - ]) || add_if_not_equal!(old, new, damage, - [ REPAINT ], [ - get_color.color, get_background.background_color, - get_background.background_image, get_background.background_position, - get_background.background_repeat, get_background.background_attachment, - get_background.background_clip, get_background.background_origin, - get_background.background_size, - get_border.border_top_color, get_border.border_right_color, - get_border.border_bottom_color, get_border.border_left_color, - get_border.border_top_style, get_border.border_right_style, - get_border.border_bottom_style, get_border.border_left_style, - get_border.border_top_left_radius, get_border.border_top_right_radius, - get_border.border_bottom_left_radius, get_border.border_bottom_right_radius, - get_position.z_index, get_box._servo_overflow_clip_box, - get_inheritedtext._servo_text_decorations_in_effect, - get_pointing.cursor, get_pointing.pointer_events, - get_effects.box_shadow, get_effects.clip, get_inheritedtext.text_shadow, get_effects.filter, - get_effects.transform, get_effects.backface_visibility, get_effects.transform_style, - get_effects.transform_origin, get_effects.perspective, get_effects.perspective_origin, - get_effects.mix_blend_mode, get_inheritedbox.image_rendering, - - // Note: May require REFLOW et al. if `visibility: collapse` is implemented. - get_inheritedbox.visibility - ]); - - // If the layer requirements of this flow have changed due to the value - // of the transform, then reflow is required to rebuild the layers. - if old.transform_requires_layer() != new.transform_requires_layer() { - damage.insert(RestyleDamage::rebuild_and_reflow()); - } - - damage -} - pub trait LayoutDamageComputation { fn compute_layout_damage(self) -> SpecialRestyleDamage; fn reflow_entire_document(self); diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 5387f943070..2b366acedac 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -21,10 +21,12 @@ use gfx::display_list::{OpaqueNode, StackingContext}; use gfx::font::FontMetrics; use gfx::font_context::FontContext; use gfx_traits::StackingContextId; -use incremental::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RESOLVE_GENERATED_CONTENT}; use layout_debug; use model::IntrinsicISizesContribution; use range::{Range, RangeIndex}; +use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW}; +use script_layout_interface::restyle_damage::{REPAINT, RESOLVE_GENERATED_CONTENT}; +use script_layout_interface::wrapper_traits::PseudoElementType; use std::cmp::max; use std::collections::VecDeque; use std::sync::Arc; @@ -38,7 +40,6 @@ use text; use unicode_bidi; use util; use util::print_tree::PrintTree; -use wrapper::PseudoElementType; // From gfxFontConstants.h in Firefox static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20; diff --git a/components/layout/layout_thread.rs b/components/layout/layout_thread.rs index bedc72791c6..a65b1452a09 100644 --- a/components/layout/layout_thread.rs +++ b/components/layout/layout_thread.rs @@ -29,8 +29,7 @@ use gfx::font_context; use gfx::paint_thread::LayoutToPaintMsg; use gfx_traits::{color, Epoch, FragmentType, LayerId, ScrollPolicy, StackingContextId}; use heapsize::HeapSizeOf; -use incremental::LayoutDamageComputation; -use incremental::{REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW, REFLOW, REFLOW_ENTIRE_DOCUMENT}; +use incremental::{LayoutDamageComputation, REFLOW_ENTIRE_DOCUMENT}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use layout_debug; @@ -47,15 +46,19 @@ use query::process_offset_parent_query; use query::{LayoutRPCImpl, process_content_box_request, process_content_boxes_request}; use query::{process_node_geometry_request, process_node_layer_id_request, process_node_scroll_area_request}; use query::{process_node_overflow_request, process_resolved_style_request, process_margin_style_query}; -use script::layout_interface::OpaqueStyleAndLayoutData; -use script::layout_interface::{LayoutRPC, OffsetParentResponse, NodeOverflowResponse, MarginStyleResponse}; -use script::layout_interface::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType, ScriptReflow}; -use script::reporter::CSSErrorReporter; +use script::layout_wrapper::ServoLayoutNode; +use script_layout_interface::message::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType, ScriptReflow}; +use script_layout_interface::reporter::CSSErrorReporter; +use script_layout_interface::restyle_damage::{REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW, REFLOW}; +use script_layout_interface::rpc::{LayoutRPC, MarginStyleResponse, NodeOverflowResponse, OffsetParentResponse}; +use script_layout_interface::wrapper_traits::LayoutNode; +use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData}; use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg}; use script_traits::{StackingContextScrollState, UntrustedNodeAddress}; use sequential; use serde_json; use std::borrow::ToOwned; +use std::cell::RefCell; use std::collections::HashMap; use std::hash::BuildHasherDefault; use std::ops::{Deref, DerefMut}; @@ -84,7 +87,7 @@ use util::thread_state; use util::workqueue::WorkQueue; use webrender_helpers::{WebRenderDisplayListConverter, WebRenderFrameBuilder}; use webrender_traits; -use wrapper::{LayoutNode, NonOpaqueStyleAndLayoutData, ServoLayoutNode}; +use wrapper::{LayoutNodeLayoutData, NonOpaqueStyleAndLayoutData}; /// The number of screens of data we're allowed to generate display lists for in each direction. pub const DISPLAY_PORT_SIZE_FACTOR: i32 = 8; @@ -1480,7 +1483,7 @@ impl LayoutThread { /// Handles a message to destroy layout data. Layout data must be destroyed on *this* thread /// because the struct type is transmuted to a different type on the script side. unsafe fn handle_reap_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData) { - let ptr: *mut () = *data.ptr; + let ptr: *mut RefCell = *data.ptr; let non_opaque: NonOpaqueStyleAndLayoutData = ptr as *mut _; let _ = Box::from_raw(non_opaque); } diff --git a/components/layout/lib.rs b/components/layout/lib.rs index 88f08610eba..d1eb3cf41ba 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -47,6 +47,7 @@ extern crate profile_traits; extern crate range; extern crate rustc_serialize; extern crate script; +extern crate script_layout_interface; extern crate script_traits; extern crate selectors; extern crate serde_json; @@ -102,4 +103,3 @@ mod wrapper; // For unit tests: pub use fragment::Fragment; -pub use wrapper::ServoThreadSafeLayoutNode; diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs index de079325e69..be0620b817d 100644 --- a/components/layout/list_item.rs +++ b/components/layout/list_item.rs @@ -19,8 +19,8 @@ use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, GeneratedC use generated_content; use gfx::display_list::StackingContext; use gfx_traits::StackingContextId; -use incremental::RESOLVE_GENERATED_CONTENT; use inline::InlineMetrics; +use script_layout_interface::restyle_damage::RESOLVE_GENERATED_CONTENT; use std::sync::Arc; use style::computed_values::{list_style_type, position}; use style::logical_geometry::LogicalSize; diff --git a/components/layout/opaque_node.rs b/components/layout/opaque_node.rs index a05a49d48e6..41a4ccd5617 100644 --- a/components/layout/opaque_node.rs +++ b/components/layout/opaque_node.rs @@ -2,41 +2,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#![allow(unsafe_code)] - use gfx::display_list::OpaqueNode; -use libc::{c_void, uintptr_t}; -use script::layout_interface::LayoutJS; -use script::layout_interface::Node; -use script::layout_interface::TrustedNodeAddress; +use libc::c_void; use script_traits::UntrustedNodeAddress; pub trait OpaqueNodeMethods { - /// Converts a DOM node (script view) to an `OpaqueNode`. - fn from_script_node(node: TrustedNodeAddress) -> Self; - - /// Converts a DOM node to an `OpaqueNode'. - fn from_jsmanaged(node: &LayoutJS) -> Self; - /// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type /// of node that script expects to receive in a hit test. fn to_untrusted_node_address(&self) -> UntrustedNodeAddress; } impl OpaqueNodeMethods for OpaqueNode { - fn from_script_node(node: TrustedNodeAddress) -> OpaqueNode { - unsafe { - OpaqueNodeMethods::from_jsmanaged(&LayoutJS::from_trusted_node_address(node)) - } - } - - fn from_jsmanaged(node: &LayoutJS) -> OpaqueNode { - unsafe { - let ptr: uintptr_t = node.get_jsobject() as uintptr_t; - OpaqueNode(ptr) - } - } - fn to_untrusted_node_address(&self) -> UntrustedNodeAddress { UntrustedNodeAddress(self.0 as *const c_void) } diff --git a/components/layout/query.rs b/components/layout/query.rs index 59da80802af..e558496f82d 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -16,9 +16,12 @@ use gfx::display_list::OpaqueNode; use gfx_traits::LayerId; use layout_thread::LayoutThreadData; use opaque_node::OpaqueNodeMethods; -use script::layout_interface::{ContentBoxResponse, NodeOverflowResponse, ContentBoxesResponse, NodeGeometryResponse}; -use script::layout_interface::{HitTestResponse, LayoutRPC, OffsetParentResponse, NodeLayerIdResponse}; -use script::layout_interface::{ResolvedStyleResponse, MarginStyleResponse}; +use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse}; +use script_layout_interface::rpc::{HitTestResponse, LayoutRPC}; +use script_layout_interface::rpc::{MarginStyleResponse, NodeGeometryResponse}; +use script_layout_interface::rpc::{NodeLayerIdResponse, NodeOverflowResponse}; +use script_layout_interface::rpc::{OffsetParentResponse, ResolvedStyleResponse}; +use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; use script_traits::LayoutMsg as ConstellationMsg; use script_traits::UntrustedNodeAddress; use sequential; @@ -34,7 +37,7 @@ use style::properties::style_structs; use style::selector_impl::PseudoElement; use style::values::AuExtensionMethods; use style_traits::cursor::Cursor; -use wrapper::{LayoutNode, ThreadSafeLayoutNode}; +use wrapper::ThreadSafeLayoutNodeHelpers; pub struct LayoutRPCImpl(pub Arc>); diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index 6663843f572..a761251b77f 100644 --- a/components/layout/sequential.rs +++ b/components/layout/sequential.rs @@ -16,7 +16,7 @@ use flow_ref::{self, FlowRef}; use fragment::FragmentBorderBoxIterator; use generated_content::ResolveGeneratedContent; use gfx::display_list::{DisplayItem, StackingContext}; -use incremental::{REFLOW, STORE_OVERFLOW}; +use script_layout_interface::restyle_damage::{REFLOW, STORE_OVERFLOW}; use traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList, ComputeAbsolutePositions}; use util::opts; diff --git a/components/layout/table.rs b/components/layout/table.rs index 5b7f26eb9d9..14845532ef0 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -18,9 +18,9 @@ use flow_list::MutFlowListIterator; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; use gfx::display_list::StackingContext; use gfx_traits::StackingContextId; -use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; use layout_debug; use model::{IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto}; +use script_layout_interface::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW}; use std::cmp; use std::fmt; use std::sync::Arc; diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index a579ecb6c12..4f043e351c4 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -16,9 +16,10 @@ use flow::{self, Flow, FlowClass, OpaqueFlow}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; use gfx::display_list::StackingContext; use gfx_traits::StackingContextId; -use incremental::REFLOW; use layout_debug; use model::MaybeAuto; +use script_layout_interface::restyle_damage::REFLOW; +use script_layout_interface::wrapper_traits::ThreadSafeLayoutNode; use std::fmt; use std::sync::Arc; use style::computed_values::{border_collapse, border_top_style, vertical_align}; @@ -27,7 +28,6 @@ use style::properties::{ComputedValues, ServoComputedValues}; use table::InternalTable; use table_row::{CollapsedBorder, CollapsedBorderProvenance}; use util::print_tree::PrintTree; -use wrapper::ThreadSafeLayoutNode; /// A table formatting context. #[derive(RustcEncodable)] diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index bd07e1f72bb..0571f903ea4 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -10,22 +10,30 @@ use display_list_builder::DisplayListBuildState; use flow::{CAN_BE_FRAGMENTED, Flow, ImmutableFlowUtils, PostorderFlowTraversal}; use flow::{PreorderFlowTraversal, self}; use gfx::display_list::OpaqueNode; -use incremental::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage}; +use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage}; +use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; use std::mem; use style::context::StyleContext; +use style::dom::TNode; use style::matching::MatchMethods; +use style::properties::ServoComputedValues; +use style::selector_impl::ServoSelectorImpl; use style::traversal::{DomTraversalContext, STYLE_BLOOM}; use style::traversal::{put_thread_local_bloom_filter, recalc_style_at}; use util::opts; use util::tid::tid; -use wrapper::{LayoutNode, ServoLayoutNode, ThreadSafeLayoutNode}; +use wrapper::{LayoutNodeLayoutData, ThreadSafeLayoutNodeHelpers}; pub struct RecalcStyleAndConstructFlows<'lc> { context: LayoutContext<'lc>, root: OpaqueNode, } -impl<'lc, 'ln> DomTraversalContext> for RecalcStyleAndConstructFlows<'lc> { +impl<'lc, N> DomTraversalContext for RecalcStyleAndConstructFlows<'lc> + where N: LayoutNode + TNode, + N::ConcreteElement: ::selectors::Element + +{ type SharedContext = SharedLayoutContext; #[allow(unsafe_code)] fn new<'a>(shared: &'a Self::SharedContext, root: OpaqueNode) -> Self { @@ -65,7 +73,7 @@ impl<'lc, 'ln> DomTraversalContext> for RecalcStyleAndConst } } - fn process_preorder(&self, node: ServoLayoutNode<'ln>) { + fn process_preorder(&self, node: N) { // FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML // parser. node.initialize_data(); @@ -73,7 +81,7 @@ impl<'lc, 'ln> DomTraversalContext> for RecalcStyleAndConst recalc_style_at(&self.context, self.root, node); } - fn process_postorder(&self, node: ServoLayoutNode<'ln>) { construct_flows_at(&self.context, self.root, node); } + fn process_postorder(&self, node: N) { construct_flows_at(&self.context, self.root, node); } } /// A bottom-up, parallelizable traversal. diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index 3c18e909bae..6db7f8b7cc2 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -32,267 +32,27 @@ use core::nonzero::NonZero; use data::{LayoutDataFlags, PrivateLayoutData}; -use gfx::display_list::OpaqueNode; -use gfx::text::glyph::ByteIndex; -use gfx_traits::{LayerId, LayerType}; -use incremental::RestyleDamage; -use msg::constellation_msg::PipelineId; -use opaque_node::OpaqueNodeMethods; -use range::Range; -use script::layout_interface::{CAN_BE_FRAGMENTED, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY}; -use script::layout_interface::{CharacterDataTypeId, Document, Element, ElementTypeId}; -use script::layout_interface::{HTMLCanvasData, HTMLElementTypeId, LayoutCharacterDataHelpers}; -use script::layout_interface::{LayoutDocumentHelpers, LayoutElementHelpers, LayoutJS}; -use script::layout_interface::{LayoutNodeHelpers, Node, NodeTypeId, OpaqueStyleAndLayoutData}; -use script::layout_interface::{RawLayoutElementHelpers, Text, TrustedNodeAddress}; -use selectors::matching::{DeclarationBlock, ElementFlags}; -use selectors::parser::{AttrSelector, NamespaceConstraint}; -use smallvec::VecLike; +use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; +use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData}; use std::cell::{Ref, RefCell, RefMut}; -use std::marker::PhantomData; -use std::mem::{transmute, transmute_copy}; -use std::sync::Arc; -use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace}; -use style::attr::AttrValue; -use style::computed_values::content::ContentItem; -use style::computed_values::{content, display}; -use style::dom::{PresentationalHintsSynthetizer, TDocument, TElement, TNode, UnsafeNode}; -use style::element_state::*; -use style::properties::{ComputedValues, ServoComputedValues}; -use style::properties::{PropertyDeclaration, PropertyDeclarationBlock}; -use style::restyle_hints::ElementSnapshot; -use style::selector_impl::{NonTSPseudoClass, PseudoElement, PseudoElementCascadeType, ServoSelectorImpl}; -use style::servo::{PrivateStyleData, SharedStyleContext}; -use url::Url; -use util::str::is_whitespace; +use style::computed_values::content::{self, ContentItem}; +use style::properties::ComputedValues; pub type NonOpaqueStyleAndLayoutData = *mut RefCell; -/// 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 `LayoutJS`. - -pub trait LayoutNode: TNode { - type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode; - fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode; - - /// Returns the type ID of this node. - fn type_id(&self) -> NodeTypeId; - +pub trait LayoutNodeLayoutData { /// Similar to borrow_data*, but returns the full PrivateLayoutData rather /// than only the PrivateStyleData. unsafe fn borrow_layout_data_unchecked(&self) -> Option<*const PrivateLayoutData>; fn borrow_layout_data(&self) -> Option>; fn mutate_layout_data(&self) -> Option>; + fn initialize_data(self); + fn flow_debug_id(self) -> usize; } -#[derive(Copy, Clone)] -pub struct ServoLayoutNode<'a> { - /// The wrapped node. - node: LayoutJS, - - /// Being chained to a PhantomData prevents `LayoutNode`s from escaping. - chain: PhantomData<&'a ()>, -} - -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: LayoutJS) -> ServoLayoutNode<'ln> { - ServoLayoutNode { - node: n, - chain: PhantomData, - } - } - - pub unsafe fn new(address: &TrustedNodeAddress) -> ServoLayoutNode { - ServoLayoutNode::from_layout_js(LayoutJS::from_trusted_node_address(*address)) - } - - /// Creates a new layout node with the same lifetime as this layout node. - pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS) -> ServoLayoutNode<'ln> { - ServoLayoutNode { - node: *node, - chain: self.chain, - } - } - - pub fn initialize_data(self) { - if unsafe { self.borrow_data_unchecked() }.is_none() { - let ptr: NonOpaqueStyleAndLayoutData = - Box::into_raw(box RefCell::new(PrivateLayoutData::new())); - let opaque = OpaqueStyleAndLayoutData { - ptr: unsafe { NonZero::new(ptr as *mut ()) } - }; - unsafe { - self.node.init_style_and_layout_data(opaque); - } - } - } -} - -impl<'ln> TNode for ServoLayoutNode<'ln> { - type ConcreteComputedValues = ServoComputedValues; - type ConcreteElement = ServoLayoutElement<'ln>; - type ConcreteDocument = ServoLayoutDocument<'ln>; - type ConcreteRestyleDamage = RestyleDamage; - - fn to_unsafe(&self) -> UnsafeNode { - unsafe { - let ptr: usize = transmute_copy(self); - (ptr, 0) - } - } - - unsafe fn from_unsafe(n: &UnsafeNode) -> Self { - let (node, _) = *n; - transmute(node) - } - - fn is_text_node(&self) -> bool { - self.type_id() == NodeTypeId::CharacterData(CharacterDataTypeId::Text) - } - - fn is_element(&self) -> bool { - unsafe { - self.node.is_element_for_layout() - } - } - - fn dump(self) { - self.dump_indent(0); - } - - fn opaque(&self) -> OpaqueNode { - OpaqueNodeMethods::from_jsmanaged(unsafe { self.get_jsmanaged() }) - } - - fn layout_parent_node(self, reflow_root: OpaqueNode) -> Option> { - if self.opaque() == reflow_root { - None - } else { - self.parent_node() - } - } - - fn debug_id(self) -> usize { - self.opaque().to_untrusted_node_address().0 as usize - } - - fn children_count(&self) -> u32 { - unsafe { self.node.children_count() } - } - - fn as_element(&self) -> Option> { - as_element(self.node) - } - - fn as_document(&self) -> Option> { - self.node.downcast().map(ServoLayoutDocument::from_layout_js) - } - - fn has_changed(&self) -> bool { - unsafe { self.node.get_flag(HAS_CHANGED) } - } - - unsafe fn set_changed(&self, value: bool) { - self.node.set_flag(HAS_CHANGED, value) - } - - fn is_dirty(&self) -> bool { - unsafe { self.node.get_flag(IS_DIRTY) } - } - - unsafe fn set_dirty(&self, value: bool) { - self.node.set_flag(IS_DIRTY, value) - } - - fn has_dirty_descendants(&self) -> bool { - unsafe { self.node.get_flag(HAS_DIRTY_DESCENDANTS) } - } - - unsafe fn set_dirty_descendants(&self, value: bool) { - self.node.set_flag(HAS_DIRTY_DESCENDANTS, value) - } - - fn can_be_fragmented(&self) -> bool { - unsafe { self.node.get_flag(CAN_BE_FRAGMENTED) } - } - - unsafe fn set_can_be_fragmented(&self, value: bool) { - self.node.set_flag(CAN_BE_FRAGMENTED, value) - } - - unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData> { - self.borrow_layout_data_unchecked().map(|d| &(*d).style_data as *const PrivateStyleData) - } - - fn borrow_data(&self) -> Option> { - self.borrow_layout_data().map(|d| Ref::map(d, |d| &d.style_data)) - } - - fn mutate_data(&self) -> Option> { - self.mutate_layout_data().map(|d| RefMut::map(d, |d| &mut d.style_data)) - } - - fn restyle_damage(self) -> RestyleDamage { - self.borrow_layout_data().unwrap().restyle_damage - } - - fn set_restyle_damage(self, damage: RestyleDamage) { - self.mutate_layout_data().unwrap().restyle_damage = damage; - } - - fn parent_node(&self) -> Option> { - unsafe { - self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node)) - } - } - - fn first_child(&self) -> Option> { - unsafe { - self.node.first_child_ref().map(|node| self.new_with_this_lifetime(&node)) - } - } - - fn last_child(&self) -> Option> { - unsafe { - self.node.last_child_ref().map(|node| self.new_with_this_lifetime(&node)) - } - } - - fn prev_sibling(&self) -> Option> { - unsafe { - self.node.prev_sibling_ref().map(|node| self.new_with_this_lifetime(&node)) - } - } - - fn next_sibling(&self) -> Option> { - unsafe { - self.node.next_sibling_ref().map(|node| self.new_with_this_lifetime(&node)) - } - } -} - -impl<'ln> LayoutNode for ServoLayoutNode<'ln> { - type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'ln>; - - fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode { - ServoThreadSafeLayoutNode::new(self) - } - - fn type_id(&self) -> NodeTypeId { - unsafe { - self.node.type_id_for_layout() - } - } - +impl LayoutNodeLayoutData for T { unsafe fn borrow_layout_data_unchecked(&self) -> Option<*const PrivateLayoutData> { - self.get_jsmanaged().get_style_and_layout_data().map(|opaque| { + self.get_style_and_layout_data().map(|opaque| { let container = *opaque.ptr as NonOpaqueStyleAndLayoutData; &(*(*container).as_unsafe_cell().get()) as *const PrivateLayoutData }) @@ -300,7 +60,7 @@ impl<'ln> LayoutNode for ServoLayoutNode<'ln> { fn borrow_layout_data(&self) -> Option> { unsafe { - self.get_jsmanaged().get_style_and_layout_data().map(|opaque| { + self.get_style_and_layout_data().map(|opaque| { let container = *opaque.ptr as NonOpaqueStyleAndLayoutData; (*container).borrow() }) @@ -309,448 +69,33 @@ impl<'ln> LayoutNode for ServoLayoutNode<'ln> { fn mutate_layout_data(&self) -> Option> { unsafe { - self.get_jsmanaged().get_style_and_layout_data().map(|opaque| { + self.get_style_and_layout_data().map(|opaque| { let container = *opaque.ptr as NonOpaqueStyleAndLayoutData; (*container).borrow_mut() }) } } -} - -impl<'ln> ServoLayoutNode<'ln> { - fn dump_indent(self, indent: u32) { - let mut s = String::new(); - for _ in 0..indent { - s.push_str(" "); - } - - s.push_str(&self.debug_str()); - println!("{}", s); - - for kid in self.children() { - kid.dump_indent(indent + 1); + fn initialize_data(self) { + if unsafe { self.borrow_data_unchecked() }.is_none() { + let ptr: NonOpaqueStyleAndLayoutData = + Box::into_raw(box RefCell::new(PrivateLayoutData::new())); + let opaque = OpaqueStyleAndLayoutData { + ptr: unsafe { NonZero::new(ptr as *mut RefCell) } + }; + self.init_style_and_layout_data(opaque); } } - fn debug_str(self) -> String { - format!("{:?}: changed={} dirty={} dirty_descendants={}", - self.type_id(), self.has_changed(), self.is_dirty(), self.has_dirty_descendants()) - } - - pub fn flow_debug_id(self) -> usize { + fn flow_debug_id(self) -> usize { self.borrow_layout_data().map_or(0, |d| d.flow_construction_result.debug_id()) } - - /// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to - /// call and as such is marked `unsafe`. - unsafe fn get_jsmanaged(&self) -> &LayoutJS { - &self.node - } } -// A wrapper around documents that ensures ayout can only ever access safe properties. -#[derive(Copy, Clone)] -pub struct ServoLayoutDocument<'ld> { - document: LayoutJS, - chain: PhantomData<&'ld ()>, -} - -impl<'ld> TDocument for ServoLayoutDocument<'ld> { - type ConcreteNode = ServoLayoutNode<'ld>; - type ConcreteElement = ServoLayoutElement<'ld>; - - fn as_node(&self) -> ServoLayoutNode<'ld> { - ServoLayoutNode::from_layout_js(self.document.upcast()) - } - - fn root_node(&self) -> Option> { - self.as_node().children().find(ServoLayoutNode::is_element) - } - - fn drain_modified_elements(&self) -> Vec<(ServoLayoutElement<'ld>, ElementSnapshot)> { - let elements = unsafe { self.document.drain_modified_elements() }; - elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect() - } -} - -impl<'ld> ServoLayoutDocument<'ld> { - fn from_layout_js(doc: LayoutJS) -> ServoLayoutDocument<'ld> { - ServoLayoutDocument { - document: doc, - chain: PhantomData, - } - } -} - -/// A wrapper around elements that ensures layout can only ever access safe properties. -#[derive(Copy, Clone)] -pub struct ServoLayoutElement<'le> { - element: LayoutJS, - chain: PhantomData<&'le ()>, -} - -impl<'le> PresentationalHintsSynthetizer for ServoLayoutElement<'le> { - fn synthesize_presentational_hints_for_legacy_attributes(&self, hints: &mut V) - where V: VecLike>> - { - unsafe { - self.element.synthesize_presentational_hints_for_legacy_attributes(hints); - } - } -} - -impl<'le> TElement for ServoLayoutElement<'le> { - type ConcreteNode = ServoLayoutNode<'le>; - type ConcreteDocument = ServoLayoutDocument<'le>; - - fn as_node(&self) -> ServoLayoutNode<'le> { - ServoLayoutNode::from_layout_js(self.element.upcast()) - } - - fn style_attribute(&self) -> &Option { - unsafe { - &*self.element.style_attribute() - } - } - - fn get_state(&self) -> ElementState { - self.element.get_state_for_layout() - } - - #[inline] - fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str> { - unsafe { - (*self.element.unsafe_get()).get_attr_val_for_layout(namespace, name) - } - } - - #[inline] - fn get_attrs(&self, name: &Atom) -> Vec<&str> { - unsafe { - (*self.element.unsafe_get()).get_attr_vals_for_layout(name) - } - } -} - - -impl<'le> ServoLayoutElement<'le> { - fn from_layout_js(el: LayoutJS) -> ServoLayoutElement<'le> { - ServoLayoutElement { - element: el, - chain: PhantomData, - } - } -} - -fn as_element<'le>(node: LayoutJS) -> Option> { - node.downcast().map(ServoLayoutElement::from_layout_js) -} - -impl<'le> ::selectors::Element for ServoLayoutElement<'le> { - type Impl = ServoSelectorImpl; - - fn parent_element(&self) -> Option> { - unsafe { - self.element.upcast().parent_node_ref().and_then(as_element) - } - } - - fn first_child_element(&self) -> Option> { - self.as_node().children().filter_map(|n| n.as_element()).next() - } - - fn last_child_element(&self) -> Option> { - self.as_node().rev_children().filter_map(|n| n.as_element()).next() - } - - fn prev_sibling_element(&self) -> Option> { - let mut node = self.as_node(); - while let Some(sibling) = node.prev_sibling() { - if let Some(element) = sibling.as_element() { - return Some(element) - } - node = sibling; - } - None - } - - fn next_sibling_element(&self) -> Option> { - let mut node = self.as_node(); - while let Some(sibling) = node.next_sibling() { - if let Some(element) = sibling.as_element() { - return Some(element) - } - node = sibling; - } - None - } - - fn is_root(&self) -> bool { - match self.as_node().parent_node() { - None => false, - Some(node) => { - match node.type_id() { - NodeTypeId::Document(_) => true, - _ => false - } - }, - } - } - - fn is_empty(&self) -> bool { - self.as_node().children().all(|node| match node.type_id() { - NodeTypeId::Element(..) => false, - NodeTypeId::CharacterData(CharacterDataTypeId::Text) => unsafe { - node.node.downcast().unwrap().data_for_layout().is_empty() - }, - _ => true - }) - } - - #[inline] - fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> { - BorrowedAtom(self.element.local_name()) - } - - #[inline] - fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> { - BorrowedNamespace(self.element.namespace()) - } - - fn match_non_ts_pseudo_class(&self, pseudo_class: NonTSPseudoClass) -> bool { - match pseudo_class { - // https://github.com/servo/servo/issues/8718 - NonTSPseudoClass::Link | - NonTSPseudoClass::AnyLink => unsafe { - match self.as_node().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.unsafe_get()).get_attr_val_for_layout(&ns!(), &atom!("href")).is_some(), - _ => false, - } - }, - NonTSPseudoClass::Visited => false, - - NonTSPseudoClass::ServoNonZeroBorder => unsafe { - match (*self.element.unsafe_get()).get_attr_for_layout(&ns!(), &atom!("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::Hover | - NonTSPseudoClass::Enabled | - NonTSPseudoClass::Disabled | - NonTSPseudoClass::Checked | - NonTSPseudoClass::Indeterminate | - NonTSPseudoClass::ReadWrite | - NonTSPseudoClass::PlaceholderShown => - self.element.get_state_for_layout().contains(pseudo_class.state_flag()) - } - } - - #[inline] - fn get_id(&self) -> Option { - unsafe { - (*self.element.id_attribute()).clone() - } - } - - #[inline] - fn has_class(&self, name: &Atom) -> bool { - unsafe { - self.element.has_class_for_layout(name) - } - } - - #[inline(always)] - fn each_class(&self, mut callback: F) where F: FnMut(&Atom) { - unsafe { - if let Some(ref classes) = self.element.get_classes_for_layout() { - for class in *classes { - callback(class) - } - } - } - } - - fn match_attr(&self, attr: &AttrSelector, test: F) -> bool where F: Fn(&str) -> bool { - let name = if self.is_html_element_in_html_document() { - &attr.lower_name - } else { - &attr.name - }; - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => { - self.get_attr(ns, name).map_or(false, |attr| test(attr)) - }, - NamespaceConstraint::Any => { - self.get_attrs(name).iter().any(|attr| test(*attr)) - } - } - } - - fn is_html_element_in_html_document(&self) -> bool { - unsafe { - self.element.html_element_in_html_document_for_layout() - } - } - - fn insert_flags(&self, flags: ElementFlags) { - self.element.insert_atomic_flags(flags); - } -} - -#[derive(Copy, PartialEq, Clone)] -pub enum PseudoElementType { - Normal, - Before(T), - After(T), - DetailsSummary(T), - DetailsContent(T), -} - -impl PseudoElementType { - 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 strip(&self) -> PseudoElementType<()> { - match *self { - PseudoElementType::Normal => PseudoElementType::Normal, - PseudoElementType::Before(_) => PseudoElementType::Before(()), - PseudoElementType::After(_) => PseudoElementType::After(()), - PseudoElementType::DetailsSummary(_) => PseudoElementType::DetailsSummary(()), - PseudoElementType::DetailsContent(_) => PseudoElementType::DetailsContent(()), - } - } - - 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, - } - } -} - -/// 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: Clone + Copy + Sized + PartialEq { - type ConcreteThreadSafeLayoutElement: - ThreadSafeLayoutElement - + ::selectors::Element; - type ChildrenIterator: Iterator + Sized; - - /// Creates a new `ThreadSafeLayoutNode` for the same `LayoutNode` - /// with a different pseudo-element type. - fn with_pseudo(&self, pseudo: PseudoElementType>) -> Self; - - /// 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; - - /// Returns the type ID of this node, without discarding pseudo-elements as - /// `type_id` does. - fn type_id_without_excluding_pseudo_elements(&self) -> NodeTypeId; - - #[inline] - fn is_element_or_elements_pseudo(&self) -> bool { - match self.type_id_without_excluding_pseudo_elements() { - NodeTypeId::Element(..) => true, - _ => false, - } - } - - fn debug_id(self) -> usize; - +pub trait ThreadSafeLayoutNodeHelpers { fn flow_debug_id(self) -> usize; - /// Returns an iterator over this node's children. - fn children(&self) -> Self::ChildrenIterator; - - #[inline] - fn is_element(&self) -> bool { if let Some(NodeTypeId::Element(_)) = self.type_id() { true } else { false } } - - /// If this is an element, accesses the element data. Fails if this is not an element node. - #[inline] - fn as_element(&self) -> Self::ConcreteThreadSafeLayoutElement; - - #[inline] - fn get_pseudo_element_type(&self) -> PseudoElementType>; - - #[inline] - fn get_before_pseudo(&self) -> Option { - if self.borrow_layout_data().unwrap() - .style_data.per_pseudo - .contains_key(&PseudoElement::Before) { - Some(self.with_pseudo(PseudoElementType::Before(None))) - } else { - None - } - } - - #[inline] - fn get_after_pseudo(&self) -> Option { - if self.borrow_layout_data().unwrap() - .style_data.per_pseudo - .contains_key(&PseudoElement::After) { - Some(self.with_pseudo(PseudoElementType::After(None))) - } else { - None - } - } - - #[inline] - fn get_details_summary_pseudo(&self) -> Option { - if self.is_element() && - self.as_element().get_local_name() == atom!("details") && - self.as_element().get_namespace() == ns!(html) { - Some(self.with_pseudo(PseudoElementType::DetailsSummary(None))) - } else { - None - } - } - - #[inline] - fn get_details_content_pseudo(&self) -> Option { - if self.is_element() && - self.as_element().get_local_name() == atom!("details") && - self.as_element().get_namespace() == ns!(html) { - let display = if self.as_element().get_attr(&ns!(), &atom!("open")).is_some() { - None // Specified by the stylesheet - } else { - Some(display::T::none) - }; - Some(self.with_pseudo(PseudoElementType::DetailsContent(display))) - } else { - None - } - } + unsafe fn borrow_layout_data_unchecked(&self) -> Option<*const PrivateLayoutData>; /// Borrows the layout data immutably. Fails on a conflicting borrow. /// @@ -764,357 +109,68 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + Sized + PartialEq { #[inline(always)] fn mutate_layout_data(&self) -> Option>; - /// 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) -> Ref> { - match self.get_pseudo_element_type() { - PseudoElementType::Normal => { - Ref::map(self.borrow_layout_data().unwrap(), |data| { - data.style_data.style.as_ref().unwrap() - }) - }, - 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 => {}, - PseudoElementCascadeType::Precomputed => { - if !self.borrow_layout_data() - .unwrap().style_data - .per_pseudo.contains_key(&style_pseudo) { - let mut data = self.mutate_layout_data().unwrap(); - let new_style = - context.stylist - .precomputed_values_for_pseudo(&style_pseudo, - data.style_data.style.as_ref()); - data.style_data.per_pseudo - .insert(style_pseudo.clone(), new_style.unwrap()); - } - } - PseudoElementCascadeType::Lazy => { - debug_assert!(self.is_element_or_elements_pseudo()); - if !self.borrow_layout_data() - .unwrap().style_data - .per_pseudo.contains_key(&style_pseudo) { - let mut data = self.mutate_layout_data().unwrap(); - let new_style = - context.stylist - .lazily_compute_pseudo_element_style( - &self.as_element(), - &style_pseudo, - data.style_data.style.as_ref().unwrap()); - data.style_data.per_pseudo - .insert(style_pseudo.clone(), new_style.unwrap()); - } - } - } - - Ref::map(self.borrow_layout_data().unwrap(), |data| { - data.style_data.per_pseudo.get(&style_pseudo).unwrap() - }) - } - } - } - - /// 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) -> Ref> { - Ref::map(self.borrow_layout_data().unwrap(), |data| { - match self.get_pseudo_element_type() { - PseudoElementType::Normal - => data.style_data.style.as_ref().unwrap(), - other - => data.style_data.per_pseudo.get(&other.style_pseudo_element()).unwrap(), - } - }) - } - - #[inline] - fn selected_style(&self, _context: &SharedStyleContext) -> Ref> { - Ref::map(self.borrow_layout_data().unwrap(), |data| { - data.style_data.per_pseudo - .get(&PseudoElement::Selection) - .unwrap_or(data.style_data.style.as_ref().unwrap()) - }) - } - - /// Removes the style from this node. - /// - /// Unlike the version on TNode, this handles pseudo-elements. - fn unstyle(self) { - let mut data = self.mutate_layout_data().unwrap(); - - match self.get_pseudo_element_type() { - PseudoElementType::Normal => { - data.style_data.style = None; - } - other => { - data.style_data.per_pseudo.remove(&other.style_pseudo_element()); - } - }; - } - - fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool; - - fn restyle_damage(self) -> RestyleDamage; - - fn set_restyle_damage(self, damage: RestyleDamage); - /// Returns the layout data flags for this node. fn flags(self) -> LayoutDataFlags; /// Adds the given flags to this node. - fn insert_flags(self, new_flags: LayoutDataFlags) { - self.mutate_layout_data().unwrap().flags.insert(new_flags); - } + fn insert_flags(self, new_flags: LayoutDataFlags); /// Removes the given flags from this node. - fn remove_flags(self, flags: LayoutDataFlags) { - self.mutate_layout_data().unwrap().flags.remove(flags); - } - - /// 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 { - match self.type_id() { - Some(NodeTypeId::Element(..)) | Some(NodeTypeId::CharacterData(CharacterDataTypeId::Text)) => true, - _ => false - } - } - - fn can_be_fragmented(&self) -> bool; + fn remove_flags(self, flags: LayoutDataFlags); /// If this is a text node, generated content, or a form element, copies out /// its content. Otherwise, panics. /// /// FIXME(pcwalton): This might have too much copying and/or allocation. Profile this. fn text_content(&self) -> TextContent; - - /// If the insertion point is within this node, returns it. Otherwise, returns `None`. - fn selection(&self) -> Option>; - - /// If this is an image element, returns its URL. If this is not an image element, fails. - /// - /// FIXME(pcwalton): Don't copy URLs. - fn image_url(&self) -> Option; - - fn canvas_data(&self) -> Option; - - /// If this node is an iframe element, returns its pipeline ID. If this node is - /// not an iframe element, fails. - fn iframe_pipeline_id(&self) -> PipelineId; - - fn get_colspan(&self) -> u32; - - fn layer_id(&self) -> LayerId { - let layer_type = match self.get_pseudo_element_type() { - PseudoElementType::Normal => LayerType::FragmentBody, - PseudoElementType::Before(_) => LayerType::BeforePseudoContent, - PseudoElementType::After(_) => LayerType::AfterPseudoContent, - PseudoElementType::DetailsSummary(_) => LayerType::FragmentBody, - PseudoElementType::DetailsContent(_) => LayerType::FragmentBody, - }; - LayerId::new_of_type(layer_type, self.opaque().id() as usize) - } - - fn layer_id_for_overflow_scroll(&self) -> LayerId { - LayerId::new_of_type(LayerType::OverflowScroll, self.opaque().id() as usize) - } } -// This trait is only public so that it can be implemented by the gecko wrapper. -// It can be used to violate thread-safety, so don't use it elsewhere in layout! -pub trait DangerousThreadSafeLayoutNode: ThreadSafeLayoutNode { - unsafe fn dangerous_first_child(&self) -> Option; - unsafe fn dangerous_next_sibling(&self) -> Option; -} - -pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + - ::selectors::Element + - PresentationalHintsSynthetizer { - type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode; - - #[inline] - fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str>; - - #[inline] - fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a>; - - #[inline] - fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a>; -} - -#[derive(Copy, Clone)] -pub struct ServoThreadSafeLayoutNode<'ln> { - /// The wrapped node. - node: ServoLayoutNode<'ln>, - - /// The pseudo-element type, with (optionally), - /// an 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 for ServoThreadSafeLayoutNode<'ln> { - unsafe fn dangerous_first_child(&self) -> Option { - self.get_jsmanaged().first_child_ref() - .map(|node| self.new_with_this_lifetime(&node)) - } - unsafe fn dangerous_next_sibling(&self) -> Option { - self.get_jsmanaged().next_sibling_ref() - .map(|node| self.new_with_this_lifetime(&node)) - } -} - -impl<'ln> ServoThreadSafeLayoutNode<'ln> { - /// Creates a new layout node with the same lifetime as this layout node. - pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS) -> ServoThreadSafeLayoutNode<'ln> { - ServoThreadSafeLayoutNode { - node: self.node.new_with_this_lifetime(node), - pseudo: PseudoElementType::Normal, - } - } - - /// Creates a new `ServoThreadSafeLayoutNode` from the given `ServoLayoutNode`. - pub fn new<'a>(node: &ServoLayoutNode<'a>) -> ServoThreadSafeLayoutNode<'a> { - ServoThreadSafeLayoutNode { - node: node.clone(), - pseudo: PseudoElementType::Normal, - } - } - - /// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to - /// call and as such is marked `unsafe`. - unsafe fn get_jsmanaged(&self) -> &LayoutJS { - self.node.get_jsmanaged() - } -} - -impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { - type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'ln>; - type ChildrenIterator = ThreadSafeLayoutNodeChildrenIterator; - - fn with_pseudo(&self, - pseudo: PseudoElementType>) -> ServoThreadSafeLayoutNode<'ln> { - ServoThreadSafeLayoutNode { - node: self.node.clone(), - pseudo: pseudo, - } - } - - fn opaque(&self) -> OpaqueNode { - OpaqueNodeMethods::from_jsmanaged(unsafe { self.get_jsmanaged() }) - } - - fn type_id(&self) -> Option { - if self.pseudo != PseudoElementType::Normal { - return None - } - - Some(self.node.type_id()) - } - - #[inline] - fn type_id_without_excluding_pseudo_elements(&self) -> NodeTypeId { - self.node.type_id() - } - - fn debug_id(self) -> usize { - self.node.debug_id() - } - +impl ThreadSafeLayoutNodeHelpers for T { fn flow_debug_id(self) -> usize { - self.node.flow_debug_id() + self.borrow_layout_data().map_or(0, |d| d.flow_construction_result.debug_id()) } - fn children(&self) -> Self::ChildrenIterator { - ThreadSafeLayoutNodeChildrenIterator::new(*self) - } - - fn as_element(&self) -> ServoThreadSafeLayoutElement<'ln> { - unsafe { - let element = match self.get_jsmanaged().downcast() { - Some(e) => e.unsafe_get(), - None => panic!("not an element") - }; - // FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on - // implementations. - ServoThreadSafeLayoutElement { - element: &*element, - } - } - } - - fn get_pseudo_element_type(&self) -> PseudoElementType> { - self.pseudo + unsafe fn borrow_layout_data_unchecked(&self) -> Option<*const PrivateLayoutData> { + self.get_style_and_layout_data().map(|opaque| { + let container = *opaque.ptr as NonOpaqueStyleAndLayoutData; + &(*(*container).as_unsafe_cell().get()) as *const PrivateLayoutData + }) } fn borrow_layout_data(&self) -> Option> { - self.node.borrow_layout_data() - } - - fn mutate_layout_data(&self) -> Option> { - self.node.mutate_layout_data() - } - - fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool { unsafe { - let text: LayoutJS = 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_inheritedtext().white_space.preserve_newlines() + self.get_style_and_layout_data().map(|opaque| { + let container = *opaque.ptr as NonOpaqueStyleAndLayoutData; + (*container).borrow() + }) } } - fn restyle_damage(self) -> RestyleDamage { - self.node.restyle_damage() - } - - fn set_restyle_damage(self, damage: RestyleDamage) { - self.node.set_restyle_damage(damage) + fn mutate_layout_data(&self) -> Option> { + unsafe { + self.get_style_and_layout_data().map(|opaque| { + let container = *opaque.ptr as NonOpaqueStyleAndLayoutData; + (*container).borrow_mut() + }) + } } fn flags(self) -> LayoutDataFlags { unsafe { - (*self.node.borrow_layout_data_unchecked().unwrap()).flags + (*self.borrow_layout_data_unchecked().unwrap()).flags } } - fn can_be_fragmented(&self) -> bool { - self.node.can_be_fragmented() + fn insert_flags(self, new_flags: LayoutDataFlags) { + self.mutate_layout_data().unwrap().flags.insert(new_flags); + } + + fn remove_flags(self, flags: LayoutDataFlags) { + self.mutate_layout_data().unwrap().flags.remove(flags); } fn text_content(&self) -> TextContent { - if self.pseudo.is_replaced_content() { + if self.get_pseudo_element_type().is_replaced_content() { let style = self.resolved_style(); return match style.as_ref().get_counters().content { @@ -1125,165 +181,7 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { }; } - let this = unsafe { self.get_jsmanaged() }; - return TextContent::Text(this.text_content()); - } - - fn selection(&self) -> Option> { - let this = unsafe { self.get_jsmanaged() }; - - this.selection().map(|range| { - Range::new(ByteIndex(range.start as isize), - ByteIndex(range.len() as isize)) - }) - } - - fn image_url(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.image_url() - } - - fn canvas_data(&self) -> Option { - let this = unsafe { self.get_jsmanaged() }; - this.canvas_data() - } - - fn iframe_pipeline_id(&self) -> PipelineId { - let this = unsafe { self.get_jsmanaged() }; - this.iframe_pipeline_id() - } - - fn get_colspan(&self) -> u32 { - unsafe { - self.get_jsmanaged().downcast::().unwrap().get_colspan() - } - } -} - -pub struct ThreadSafeLayoutNodeChildrenIterator { - current_node: Option, - parent_node: ConcreteNode, -} - -impl ThreadSafeLayoutNodeChildrenIterator - where ConcreteNode: DangerousThreadSafeLayoutNode { - pub fn new(parent: ConcreteNode) -> Self { - let first_child: Option = match parent.get_pseudo_element_type() { - PseudoElementType::Normal => { - parent.get_before_pseudo().or_else(|| parent.get_details_summary_pseudo()).or_else(|| { - unsafe { parent.dangerous_first_child() } - }) - }, - PseudoElementType::DetailsContent(_) | PseudoElementType::DetailsSummary(_) => { - unsafe { parent.dangerous_first_child() } - }, - _ => None, - }; - ThreadSafeLayoutNodeChildrenIterator { - current_node: first_child, - parent_node: parent, - } - } -} - -impl Iterator for ThreadSafeLayoutNodeChildrenIterator - where ConcreteNode: DangerousThreadSafeLayoutNode { - type Item = ConcreteNode; - fn next(&mut self) -> Option { - 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 node.is_element() && - node.as_element().get_local_name() == atom!("summary") && - node.as_element().get_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().get_local_name() == atom!("summary") && - node.as_element().get_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(_) => { - let first = self.parent_node.get_details_summary_pseudo().or_else(|| unsafe { - self.parent_node.dangerous_first_child() - }); - match first { - Some(first) => Some(first), - None => self.parent_node.get_after_pseudo(), - } - }, - PseudoElementType::Normal => { - match unsafe { node.dangerous_next_sibling() } { - Some(next) => Some(next), - None => 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(Copy, Clone)] -pub struct ServoThreadSafeLayoutElement<'le> { - element: &'le Element, -} - -impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> { - type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'le>; - - fn get_attr<'a>(&'a self, namespace: &Namespace, name: &Atom) -> Option<&'a str> { - unsafe { - self.element.get_attr_val_for_layout(namespace, name) - } - } - - #[inline] - fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> { - BorrowedAtom(self.element.local_name()) - } - - #[inline] - fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> { - BorrowedNamespace(self.element.namespace()) + return TextContent::Text(self.node_text_content()); } } @@ -1300,113 +198,3 @@ impl TextContent { } } } - -/// 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`, -/// `each_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 = ServoSelectorImpl; - - fn parent_element(&self) -> Option { - warn!("ServoThreadSafeLayoutElement::parent_element called"); - None - } - - fn first_child_element(&self) -> Option { - warn!("ServoThreadSafeLayoutElement::first_child_element called"); - None - } - - // Skips non-element nodes - fn last_child_element(&self) -> Option { - warn!("ServoThreadSafeLayoutElement::last_child_element called"); - None - } - - // Skips non-element nodes - fn prev_sibling_element(&self) -> Option { - warn!("ServoThreadSafeLayoutElement::prev_sibling_element called"); - None - } - - // Skips non-element nodes - fn next_sibling_element(&self) -> Option { - warn!("ServoThreadSafeLayoutElement::next_sibling_element called"); - None - } - - fn is_html_element_in_html_document(&self) -> bool { - debug!("ServoThreadSafeLayoutElement::is_html_element_in_html_document called"); - true - } - - #[inline] - fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> { - ThreadSafeLayoutElement::get_local_name(self) - } - - #[inline] - fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> { - ThreadSafeLayoutElement::get_namespace(self) - } - - fn match_non_ts_pseudo_class(&self, _: NonTSPseudoClass) -> bool { - // NB: This could maybe be implemented - warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called"); - false - } - - fn get_id(&self) -> Option { - debug!("ServoThreadSafeLayoutElement::get_id called"); - None - } - - fn has_class(&self, _name: &Atom) -> bool { - debug!("ServoThreadSafeLayoutElement::has_class called"); - false - } - - fn match_attr(&self, attr: &AttrSelector, test: F) -> bool - where F: Fn(&str) -> bool { - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => { - self.get_attr(ns, &attr.name).map_or(false, |attr| test(attr)) - }, - NamespaceConstraint::Any => { - unsafe { - self.element.get_attr_vals_for_layout(&attr.name).iter() - .any(|attr| test(*attr)) - } - } - } - } - - fn is_empty(&self) -> bool { - warn!("ServoThreadSafeLayoutElement::is_empty called"); - false - } - - fn is_root(&self) -> bool { - warn!("ServoThreadSafeLayoutElement::is_root called"); - false - } - - fn each_class(&self, _callback: F) - where F: FnMut(&Atom) { - warn!("ServoThreadSafeLayoutElement::each_class called"); - } -} - -impl<'le> PresentationalHintsSynthetizer for ServoThreadSafeLayoutElement<'le> { - fn synthesize_presentational_hints_for_legacy_attributes(&self, _hints: &mut V) - where V: VecLike>> {} -} diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index a9daf1a2adf..fe446adebd6 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -49,10 +49,12 @@ phf_macros = "0.7.13" plugins = {path = "../plugins"} profile_traits = {path = "../profile_traits"} rand = "0.3" +range = {path = "../range"} ref_filter_map = "1.0" ref_slice = "1.0" regex = "0.1.43" rustc-serialize = "0.3" +script_layout_interface = {path = "../script_layout_interface"} script_traits = {path = "../script_traits"} selectors = {version = "0.6", features = ["heap_size"]} serde = "0.7" diff --git a/components/script/dom/bindings/js.rs b/components/script/dom/bindings/js.rs index f8ddb80e44b..12ff0aa8823 100644 --- a/components/script/dom/bindings/js.rs +++ b/components/script/dom/bindings/js.rs @@ -33,7 +33,7 @@ use dom::node::Node; use heapsize::HeapSizeOf; use js::jsapi::{Heap, JSObject, JSTracer}; use js::jsval::JSVal; -use layout_interface::TrustedNodeAddress; +use script_layout_interface::TrustedNodeAddress; use script_thread::STACK_ROOTS; use std::cell::UnsafeCell; use std::default::Default; diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 34ec7d19e14..cd4a0f5d3ca 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -55,7 +55,6 @@ use js::glue::{CallObjectTracer, CallUnbarrieredObjectTracer, CallValueTracer}; use js::jsapi::{GCTraceKindToAscii, Heap, TraceKind, JSObject, JSTracer}; use js::jsval::JSVal; use js::rust::Runtime; -use layout_interface::LayoutRPC; use libc; use msg::constellation_msg::{FrameType, PipelineId, SubpageId, WindowSizeData, WindowSizeType, ReferrerPolicy}; use net_traits::filemanager_thread::SelectedFileId; @@ -67,6 +66,9 @@ use net_traits::{Metadata, NetworkError, ResourceThreads}; use offscreen_gl_context::GLLimits; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; +use script_layout_interface::OpaqueStyleAndLayoutData; +use script_layout_interface::reporter::CSSErrorReporter; +use script_layout_interface::rpc::LayoutRPC; use script_runtime::ScriptChan; use script_traits::{TimerEventId, TimerSource, TouchpadPressurePhase, UntrustedNodeAddress}; use serde::{Deserialize, Serialize}; @@ -328,6 +330,8 @@ no_jsmanaged_fields!(ReferrerPolicy); no_jsmanaged_fields!(ResourceThreads); no_jsmanaged_fields!(SystemTime); no_jsmanaged_fields!(SelectedFileId); +no_jsmanaged_fields!(OpaqueStyleAndLayoutData); +no_jsmanaged_fields!(CSSErrorReporter); impl JSTraceable for Box { #[inline] diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 4dfb484a301..a69537d3c1c 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -92,7 +92,6 @@ use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks, QuirksMode}; use ipc_channel::ipc::{self, IpcSender}; use js::jsapi::JS_GetRuntime; use js::jsapi::{JSContext, JSObject, JSRuntime}; -use layout_interface::{Msg, ReflowQueryType}; use msg::constellation_msg::{ALT, CONTROL, SHIFT, SUPER}; use msg::constellation_msg::{Key, KeyModifiers, KeyState}; use msg::constellation_msg::{PipelineId, ReferrerPolicy, SubpageId}; @@ -103,6 +102,7 @@ use net_traits::{AsyncResponseTarget, PendingAsyncLoad, IpcSend}; use num_traits::ToPrimitive; use origin::Origin; use parse::{ParserRoot, ParserRef, MutNullableParserField}; +use script_layout_interface::message::{Msg, ReflowQueryType}; use script_thread::{MainThreadScriptMsg, Runnable}; use script_traits::UntrustedNodeAddress; use script_traits::{AnimationState, MouseButton, MouseEventType, MozBrowserEvent}; diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index 95073d49cd3..add54917e6d 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -30,6 +30,7 @@ use ipc_channel::ipc::{self, IpcSender}; use js::jsapi::{HandleValue, JSContext}; use offscreen_gl_context::GLContextAttributes; use rustc_serialize::base64::{STANDARD, ToBase64}; +use script_layout_interface::HTMLCanvasData; use std::iter::repeat; use string_cache::Atom; use style::attr::AttrValue; @@ -92,12 +93,6 @@ impl HTMLCanvasElement { } } -pub struct HTMLCanvasData { - pub ipc_renderer: Option>, - pub width: u32, - pub height: u32, -} - pub trait LayoutHTMLCanvasElementHelpers { fn data(&self) -> HTMLCanvasData; } diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 86146b8e915..22e67d162d9 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -38,9 +38,9 @@ use dom::window::{ReflowReason, Window}; use ipc_channel::ipc; use js::jsapi::{JSAutoCompartment, RootedValue, JSContext, MutableHandleValue}; use js::jsval::{UndefinedValue, NullValue}; -use layout_interface::ReflowQueryType; use msg::constellation_msg::{FrameType, LoadData, NavigationDirection, PipelineId, SubpageId}; use net_traits::response::HttpsState; +use script_layout_interface::message::ReflowQueryType; use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed}; use script_traits::{IFrameLoadInfo, MozBrowserEvent, ScriptMsg as ConstellationMsg}; use std::cell::Cell; diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 79b6ed32f0b..ab2c2a99887 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -25,9 +25,9 @@ use hyper::header::ContentType; use hyper::mime::{Mime, TopLevel, SubLevel}; use ipc_channel::ipc; use ipc_channel::router::ROUTER; -use layout_interface::Msg; use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError}; use network_listener::{NetworkListener, PreInvoke}; +use script_layout_interface::message::Msg; use script_traits::{MozBrowserEvent, ScriptMsg as ConstellationMsg}; use std::ascii::AsciiExt; use std::borrow::ToOwned; diff --git a/components/script/dom/htmlobjectelement.rs b/components/script/dom/htmlobjectelement.rs index 268ff17e9cf..13798632e17 100644 --- a/components/script/dom/htmlobjectelement.rs +++ b/components/script/dom/htmlobjectelement.rs @@ -68,11 +68,6 @@ impl<'a> ProcessDataURL for &'a HTMLObjectElement { } } -pub fn is_image_data(uri: &str) -> bool { - static TYPES: &'static [&'static str] = &["data:image/png", "data:image/gif", "data:image/jpeg"]; - TYPES.iter().any(|&type_| uri.starts_with(type_)) -} - impl HTMLObjectElementMethods for HTMLObjectElement { // https://html.spec.whatwg.org/multipage/#dom-cva-validity fn Validity(&self) -> Root { diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs index c972cb77bb5..47409402855 100644 --- a/components/script/dom/htmlstyleelement.rs +++ b/components/script/dom/htmlstyleelement.rs @@ -14,7 +14,7 @@ use dom::element::Element; use dom::htmlelement::HTMLElement; use dom::node::{ChildrenMutation, Node, document_from_node, window_from_node}; use dom::virtualmethods::VirtualMethods; -use layout_interface::Msg; +use script_layout_interface::message::Msg; use std::sync::Arc; use string_cache::Atom; use style::media_queries::parse_media_query_list; diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index a3289cca56e..53a3f355b9d 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -5,7 +5,6 @@ //! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements. use app_units::Au; -use core::nonzero::NonZero; use devtools_traits::NodeInfo; use document_loader::DocumentLoader; use dom::attr::Attr; @@ -22,8 +21,8 @@ use dom::bindings::codegen::UnionTypes::NodeOrString; use dom::bindings::conversions::{self, DerivedFrom}; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::GlobalRef; -use dom::bindings::inheritance::{Castable, CharacterDataTypeId}; -use dom::bindings::inheritance::{EventTargetTypeId, NodeTypeId}; +use dom::bindings::inheritance::{Castable, CharacterDataTypeId, ElementTypeId}; +use dom::bindings::inheritance::{EventTargetTypeId, HTMLElementTypeId, NodeTypeId}; use dom::bindings::js::Root; use dom::bindings::js::RootedReference; use dom::bindings::js::{JS, LayoutJS, MutNullableHeap}; @@ -38,7 +37,7 @@ use dom::documenttype::DocumentType; use dom::element::{Element, ElementCreator}; use dom::eventtarget::EventTarget; use dom::htmlbodyelement::HTMLBodyElement; -use dom::htmlcanvaselement::{LayoutHTMLCanvasElementHelpers, HTMLCanvasData}; +use dom::htmlcanvaselement::LayoutHTMLCanvasElementHelpers; use dom::htmlcollection::HTMLCollection; use dom::htmlelement::HTMLElement; use dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods}; @@ -57,11 +56,13 @@ use euclid::size::Size2D; use heapsize::{HeapSizeOf, heap_size_of}; use html5ever::tree_builder::QuirksMode; use js::jsapi::{JSContext, JSObject, JSRuntime}; -use layout_interface::Msg; use libc::{self, c_void, uintptr_t}; use msg::constellation_msg::PipelineId; use parse::html::parse_html_fragment; use ref_slice::ref_slice; +use script_layout_interface::message::Msg; +use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData}; +use script_layout_interface::{LayoutNodeType, LayoutElementType, TrustedNodeAddress}; use script_traits::UntrustedNodeAddress; use selectors::matching::matches; use selectors::parser::Selector; @@ -74,6 +75,7 @@ use std::iter::{self, FilterMap, Peekable}; use std::mem; use std::ops::Range; use string_cache::{Atom, Namespace, QualName}; +use style::dom::OpaqueNode; use style::selector_impl::ServoSelectorImpl; use url::Url; use util::thread_state; @@ -171,7 +173,7 @@ impl NodeFlags { impl Drop for Node { #[allow(unsafe_code)] fn drop(&mut self) { - self.style_and_layout_data.get().map(|d| d.dispose(self)); + self.style_and_layout_data.get().map(|d| self.dispose(d)); } } @@ -184,29 +186,15 @@ enum SuppressObserver { Unsuppressed } -#[derive(Copy, Clone, HeapSizeOf)] -pub struct OpaqueStyleAndLayoutData { - #[ignore_heap_size_of = "TODO(#6910) Box value that should be counted but \ - the type lives in layout"] - pub ptr: NonZero<*mut ()> -} - -#[allow(unsafe_code)] -unsafe impl Send for OpaqueStyleAndLayoutData {} - -no_jsmanaged_fields!(OpaqueStyleAndLayoutData); - -impl OpaqueStyleAndLayoutData { - /// Sends the style and layout data, if any, back to the layout thread to be destroyed. - pub fn dispose(self, node: &Node) { - debug_assert!(thread_state::get().is_script()); - let win = window_from_node(node); - node.style_and_layout_data.set(None); - win.layout_chan().send(Msg::ReapStyleAndLayoutData(self)).unwrap(); - } -} - impl Node { + /// Sends the style and layout data, if any, back to the layout thread to be destroyed. + pub fn dispose(&self, data: OpaqueStyleAndLayoutData) { + debug_assert!(thread_state::get().is_script()); + let win = window_from_node(self); + self.style_and_layout_data.set(None); + win.layout_chan().send(Msg::ReapStyleAndLayoutData(data)).unwrap(); + } + /// Adds a new child to the end of this node's list of children. /// /// Fails unless `new_child` is disconnected from the tree. @@ -292,7 +280,7 @@ impl Node { for node in child.traverse_preorder() { node.set_flag(IS_IN_DOC, false); vtable_for(&&*node).unbind_from_tree(&context); - node.style_and_layout_data.get().map(|d| d.dispose(&node)); + node.style_and_layout_data.get().map(|d| node.dispose(d)); } self.owner_doc().content_and_heritage_changed(self, NodeDamage::OtherNodeDamage); @@ -340,7 +328,7 @@ impl<'a> Iterator for QuerySelectorIterator { impl Node { pub fn teardown(&self) { - self.style_and_layout_data.get().map(|d| d.dispose(self)); + self.style_and_layout_data.get().map(|d| self.dispose(d)); for kid in self.children() { kid.teardown(); } @@ -974,6 +962,7 @@ pub trait LayoutNodeHelpers { fn image_url(&self) -> Option; fn canvas_data(&self) -> Option; fn iframe_pipeline_id(&self) -> PipelineId; + fn opaque(&self) -> OpaqueNode; } impl LayoutNodeHelpers for LayoutJS { @@ -1114,6 +1103,13 @@ impl LayoutNodeHelpers for LayoutJS { .expect("not an iframe element!"); iframe_element.pipeline_id().unwrap() } + + #[allow(unsafe_code)] + fn opaque(&self) -> OpaqueNode { + unsafe { + OpaqueNode(self.get_jsobject() as usize) + } + } } @@ -2413,17 +2409,6 @@ impl NodeMethods for Node { } } - - -/// The address of a node known to be valid. These are sent from script to layout, -/// and are also used in the HTML parser interface. - -#[derive(Clone, PartialEq, Eq, Copy)] -pub struct TrustedNodeAddress(pub *const c_void); - -#[allow(unsafe_code)] -unsafe impl Send for TrustedNodeAddress {} - pub fn document_from_node + Reflectable>(derived: &T) -> Root { derived.upcast().owner_doc() } @@ -2648,3 +2633,56 @@ impl UniqueId { } } } + +impl Into for NodeTypeId { + #[inline(always)] + fn into(self) -> LayoutNodeType { + match self { + NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => + LayoutNodeType::Comment, + NodeTypeId::Document(..) => + LayoutNodeType::Document, + NodeTypeId::DocumentFragment => + LayoutNodeType::DocumentFragment, + NodeTypeId::DocumentType => + LayoutNodeType::DocumentType, + NodeTypeId::Element(e) => + LayoutNodeType::Element(e.into()), + NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => + LayoutNodeType::ProcessingInstruction, + NodeTypeId::CharacterData(CharacterDataTypeId::Text) => + LayoutNodeType::Text, + } + } +} + +impl Into for ElementTypeId { + #[inline(always)] + fn into(self) -> LayoutElementType { + match self { + ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement) => + LayoutElementType::HTMLCanvasElement, + ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement) => + LayoutElementType::HTMLIFrameElement, + ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement) => + LayoutElementType::HTMLImageElement, + ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement) => + LayoutElementType::HTMLInputElement, + ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement) => + LayoutElementType::HTMLObjectElement, + ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableCellElement(_)) => + LayoutElementType::HTMLTableCellElement, + ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableColElement) => + LayoutElementType::HTMLTableColElement, + ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement) => + LayoutElementType::HTMLTableElement, + ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement) => + LayoutElementType::HTMLTableRowElement, + ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableSectionElement) => + LayoutElementType::HTMLTableSectionElement, + ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement) => + LayoutElementType::HTMLTextAreaElement, + _ => LayoutElementType::Element, + } + } +} diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 03fbe2b2a2b..58fe8b6ad40 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -31,7 +31,7 @@ use dom::element::Element; use dom::eventtarget::EventTarget; use dom::location::Location; use dom::navigator::Navigator; -use dom::node::{Node, TrustedNodeAddress, from_untrusted_node_address, window_from_node}; +use dom::node::{Node, from_untrusted_node_address, window_from_node}; use dom::performance::Performance; use dom::screen::Screen; use dom::storage::Storage; @@ -42,8 +42,6 @@ use js::jsapi::{Evaluate2, HandleObject, HandleValue, JSAutoCompartment, JSConte use js::jsapi::{JS_GetRuntime, JS_GC, MutableHandleValue, SetWindowProxy}; use js::rust::CompileOptionsWrapper; use js::rust::Runtime; -use layout_interface::{ContentBoxResponse, ContentBoxesResponse, ResolvedStyleResponse, ScriptReflow}; -use layout_interface::{LayoutRPC, Msg, Reflow, ReflowQueryType, MarginStyleResponse}; use libc; use msg::constellation_msg::{FrameType, LoadData, PanicMsg, PipelineId, SubpageId}; use msg::constellation_msg::{WindowSizeData, WindowSizeType}; @@ -57,8 +55,12 @@ use open; use profile_traits::mem; use profile_traits::time::{ProfilerCategory, TimerMetadata, TimerMetadataFrameType}; use profile_traits::time::{ProfilerChan, TimerMetadataReflowType, profile}; -use reporter::CSSErrorReporter; use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64}; +use script_layout_interface::TrustedNodeAddress; +use script_layout_interface::message::{Msg, Reflow, ReflowQueryType, ScriptReflow}; +use script_layout_interface::reporter::CSSErrorReporter; +use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC}; +use script_layout_interface::rpc::{MarginStyleResponse, ResolvedStyleResponse}; use script_runtime::{ScriptChan, ScriptPort}; use script_thread::SendableMainThreadScriptChan; use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, RunnableWrapper}; diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs new file mode 100644 index 00000000000..2fc6991310e --- /dev/null +++ b/components/script/layout_wrapper.rs @@ -0,0 +1,999 @@ +/* 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 http://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 `LayoutJS` 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 dom::bindings::inheritance::{CharacterDataTypeId, ElementTypeId}; +use dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId}; +use dom::bindings::js::LayoutJS; +use dom::characterdata::LayoutCharacterDataHelpers; +use dom::document::{Document, LayoutDocumentHelpers}; +use dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers}; +use dom::node::{CAN_BE_FRAGMENTED, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY}; +use dom::node::{Node, LayoutNodeHelpers}; +use dom::text::Text; +use gfx_traits::ByteIndex; +use msg::constellation_msg::PipelineId; +use range::Range; +use script_layout_interface::restyle_damage::RestyleDamage; +use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, LayoutNode, PseudoElementType}; +use script_layout_interface::wrapper_traits::{ThreadSafeLayoutNode, ThreadSafeLayoutElement}; +use script_layout_interface::{HTMLCanvasData, LayoutNodeType, TrustedNodeAddress}; +use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData}; +use selectors::matching::{DeclarationBlock, ElementFlags}; +use selectors::parser::{AttrSelector, NamespaceConstraint}; +use smallvec::VecLike; +use std::cell::{Ref, RefCell, RefMut}; +use std::marker::PhantomData; +use std::mem::{transmute, transmute_copy}; +use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace}; +use style::attr::AttrValue; +use style::computed_values::display; +use style::dom::{PresentationalHintsSynthetizer, OpaqueNode, TDocument, TElement, TNode, UnsafeNode}; +use style::element_state::*; +use style::properties::{ComputedValues, ServoComputedValues}; +use style::properties::{PropertyDeclaration, PropertyDeclarationBlock}; +use style::restyle_hints::ElementSnapshot; +use style::selector_impl::{NonTSPseudoClass, ServoSelectorImpl}; +use style::servo::{PrivateStyleData, SharedStyleContext}; +use url::Url; +use util::str::is_whitespace; + +#[derive(Copy, Clone)] +pub struct ServoLayoutNode<'a> { + /// The wrapped node. + node: LayoutJS, + + /// Being chained to a PhantomData prevents `LayoutNode`s from escaping. + chain: PhantomData<&'a ()>, +} + +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: LayoutJS) -> ServoLayoutNode<'ln> { + ServoLayoutNode { + node: n, + chain: PhantomData, + } + } + + pub unsafe fn new(address: &TrustedNodeAddress) -> ServoLayoutNode { + ServoLayoutNode::from_layout_js(LayoutJS::from_trusted_node_address(*address)) + } + + /// Creates a new layout node with the same lifetime as this layout node. + pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS) -> ServoLayoutNode<'ln> { + ServoLayoutNode { + node: *node, + chain: self.chain, + } + } + + fn script_type_id(&self) -> NodeTypeId { + unsafe { + self.node.type_id_for_layout() + } + } +} + +impl<'ln> TNode for ServoLayoutNode<'ln> { + type ConcreteComputedValues = ServoComputedValues; + type ConcreteElement = ServoLayoutElement<'ln>; + type ConcreteDocument = ServoLayoutDocument<'ln>; + type ConcreteRestyleDamage = RestyleDamage; + + fn to_unsafe(&self) -> UnsafeNode { + unsafe { + let ptr: usize = transmute_copy(self); + (ptr, 0) + } + } + + unsafe fn from_unsafe(n: &UnsafeNode) -> Self { + let (node, _) = *n; + transmute(node) + } + + fn is_text_node(&self) -> bool { + self.script_type_id() == NodeTypeId::CharacterData(CharacterDataTypeId::Text) + } + + fn is_element(&self) -> bool { + unsafe { + self.node.is_element_for_layout() + } + } + + fn dump(self) { + self.dump_indent(0); + } + + fn opaque(&self) -> OpaqueNode { + unsafe { self.get_jsmanaged().opaque() } + } + + fn layout_parent_node(self, reflow_root: OpaqueNode) -> Option> { + if self.opaque() == reflow_root { + None + } else { + self.parent_node() + } + } + + fn debug_id(self) -> usize { + self.opaque().0 + } + + fn children_count(&self) -> u32 { + unsafe { self.node.children_count() } + } + + fn as_element(&self) -> Option> { + as_element(self.node) + } + + fn as_document(&self) -> Option> { + self.node.downcast().map(ServoLayoutDocument::from_layout_js) + } + + fn has_changed(&self) -> bool { + unsafe { self.node.get_flag(HAS_CHANGED) } + } + + unsafe fn set_changed(&self, value: bool) { + self.node.set_flag(HAS_CHANGED, value) + } + + fn is_dirty(&self) -> bool { + unsafe { self.node.get_flag(IS_DIRTY) } + } + + unsafe fn set_dirty(&self, value: bool) { + self.node.set_flag(IS_DIRTY, value) + } + + fn has_dirty_descendants(&self) -> bool { + unsafe { self.node.get_flag(HAS_DIRTY_DESCENDANTS) } + } + + unsafe fn set_dirty_descendants(&self, value: bool) { + self.node.set_flag(HAS_DIRTY_DESCENDANTS, value) + } + + fn can_be_fragmented(&self) -> bool { + unsafe { self.node.get_flag(CAN_BE_FRAGMENTED) } + } + + unsafe fn set_can_be_fragmented(&self, value: bool) { + self.node.set_flag(CAN_BE_FRAGMENTED, value) + } + + unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData> { + self.get_style_data().map(|d| { + &(*d.as_unsafe_cell().get()).style_data as *const _ + }) + } + + fn borrow_data(&self) -> Option> { + self.get_style_data().map(|d| { + Ref::map(d.borrow(), |d| &d.style_data) + }) + } + + fn mutate_data(&self) -> Option> { + self.get_style_data().map(|d| { + RefMut::map(d.borrow_mut(), |d| &mut d.style_data) + }) + } + + fn restyle_damage(self) -> RestyleDamage { + self.get_style_data().unwrap().borrow().restyle_damage + } + + fn set_restyle_damage(self, damage: RestyleDamage) { + self.get_style_data().unwrap().borrow_mut().restyle_damage = damage; + } + + fn parent_node(&self) -> Option> { + unsafe { + self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node)) + } + } + + fn first_child(&self) -> Option> { + unsafe { + self.node.first_child_ref().map(|node| self.new_with_this_lifetime(&node)) + } + } + + fn last_child(&self) -> Option> { + unsafe { + self.node.last_child_ref().map(|node| self.new_with_this_lifetime(&node)) + } + } + + fn prev_sibling(&self) -> Option> { + unsafe { + self.node.prev_sibling_ref().map(|node| self.new_with_this_lifetime(&node)) + } + } + + fn next_sibling(&self) -> Option> { + unsafe { + self.node.next_sibling_ref().map(|node| self.new_with_this_lifetime(&node)) + } + } +} + +impl<'ln> LayoutNode 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() + } + + fn get_style_data(&self) -> Option<&RefCell> { + unsafe { + self.get_jsmanaged().get_style_and_layout_data().map(|d| { + &**d.ptr + }) + } + } + + fn init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData) { + unsafe { + self.get_jsmanaged().init_style_and_layout_data(data); + } + } + + fn get_style_and_layout_data(&self) -> Option { + unsafe { + self.get_jsmanaged().get_style_and_layout_data() + } + } +} + +impl<'ln> ServoLayoutNode<'ln> { + fn dump_indent(self, indent: u32) { + let mut s = String::new(); + for _ in 0..indent { + s.push_str(" "); + } + + s.push_str(&self.debug_str()); + println!("{}", s); + + for kid in self.children() { + kid.dump_indent(indent + 1); + } + } + + fn debug_str(self) -> String { + format!("{:?}: changed={} dirty={} dirty_descendants={}", + self.script_type_id(), self.has_changed(), self.is_dirty(), self.has_dirty_descendants()) + } + + /// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to + /// call and as such is marked `unsafe`. + unsafe fn get_jsmanaged(&self) -> &LayoutJS { + &self.node + } +} + +// A wrapper around documents that ensures ayout can only ever access safe properties. +#[derive(Copy, Clone)] +pub struct ServoLayoutDocument<'ld> { + document: LayoutJS, + chain: PhantomData<&'ld ()>, +} + +impl<'ld> TDocument for ServoLayoutDocument<'ld> { + type ConcreteNode = ServoLayoutNode<'ld>; + type ConcreteElement = ServoLayoutElement<'ld>; + + fn as_node(&self) -> ServoLayoutNode<'ld> { + ServoLayoutNode::from_layout_js(self.document.upcast()) + } + + fn root_node(&self) -> Option> { + self.as_node().children().find(ServoLayoutNode::is_element) + } + + fn drain_modified_elements(&self) -> Vec<(ServoLayoutElement<'ld>, ElementSnapshot)> { + let elements = unsafe { self.document.drain_modified_elements() }; + elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect() + } +} + +impl<'ld> ServoLayoutDocument<'ld> { + fn from_layout_js(doc: LayoutJS) -> ServoLayoutDocument<'ld> { + ServoLayoutDocument { + document: doc, + chain: PhantomData, + } + } +} + +/// A wrapper around elements that ensures layout can only ever access safe properties. +#[derive(Copy, Clone)] +pub struct ServoLayoutElement<'le> { + element: LayoutJS, + chain: PhantomData<&'le ()>, +} + +impl<'le> PresentationalHintsSynthetizer for ServoLayoutElement<'le> { + fn synthesize_presentational_hints_for_legacy_attributes(&self, hints: &mut V) + where V: VecLike>> + { + unsafe { + self.element.synthesize_presentational_hints_for_legacy_attributes(hints); + } + } +} + +impl<'le> TElement for ServoLayoutElement<'le> { + type ConcreteNode = ServoLayoutNode<'le>; + type ConcreteDocument = ServoLayoutDocument<'le>; + + fn as_node(&self) -> ServoLayoutNode<'le> { + ServoLayoutNode::from_layout_js(self.element.upcast()) + } + + fn style_attribute(&self) -> &Option { + unsafe { + &*self.element.style_attribute() + } + } + + fn get_state(&self) -> ElementState { + self.element.get_state_for_layout() + } + + #[inline] + fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str> { + unsafe { + (*self.element.unsafe_get()).get_attr_val_for_layout(namespace, name) + } + } + + #[inline] + fn get_attrs(&self, name: &Atom) -> Vec<&str> { + unsafe { + (*self.element.unsafe_get()).get_attr_vals_for_layout(name) + } + } +} + + +impl<'le> ServoLayoutElement<'le> { + fn from_layout_js(el: LayoutJS) -> ServoLayoutElement<'le> { + ServoLayoutElement { + element: el, + chain: PhantomData, + } + } +} + +fn as_element<'le>(node: LayoutJS) -> Option> { + node.downcast().map(ServoLayoutElement::from_layout_js) +} + +impl<'le> ::selectors::Element for ServoLayoutElement<'le> { + type Impl = ServoSelectorImpl; + + fn parent_element(&self) -> Option> { + unsafe { + self.element.upcast().parent_node_ref().and_then(as_element) + } + } + + fn first_child_element(&self) -> Option> { + self.as_node().children().filter_map(|n| n.as_element()).next() + } + + fn last_child_element(&self) -> Option> { + self.as_node().rev_children().filter_map(|n| n.as_element()).next() + } + + fn prev_sibling_element(&self) -> Option> { + let mut node = self.as_node(); + while let Some(sibling) = node.prev_sibling() { + if let Some(element) = sibling.as_element() { + return Some(element) + } + node = sibling; + } + None + } + + fn next_sibling_element(&self) -> Option> { + let mut node = self.as_node(); + while let Some(sibling) = node.next_sibling() { + if let Some(element) = sibling.as_element() { + return Some(element) + } + node = sibling; + } + None + } + + fn is_root(&self) -> bool { + match self.as_node().parent_node() { + None => false, + Some(node) => { + match node.script_type_id() { + NodeTypeId::Document(_) => true, + _ => false + } + }, + } + } + + fn is_empty(&self) -> bool { + self.as_node().children().all(|node| match node.script_type_id() { + NodeTypeId::Element(..) => false, + NodeTypeId::CharacterData(CharacterDataTypeId::Text) => unsafe { + node.node.downcast().unwrap().data_for_layout().is_empty() + }, + _ => true + }) + } + + #[inline] + fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> { + BorrowedAtom(self.element.local_name()) + } + + #[inline] + fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> { + BorrowedNamespace(self.element.namespace()) + } + + fn match_non_ts_pseudo_class(&self, pseudo_class: NonTSPseudoClass) -> bool { + match pseudo_class { + // https://github.com/servo/servo/issues/8718 + NonTSPseudoClass::Link | + NonTSPseudoClass::AnyLink => unsafe { + 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.unsafe_get()).get_attr_val_for_layout(&ns!(), &atom!("href")).is_some(), + _ => false, + } + }, + NonTSPseudoClass::Visited => false, + + NonTSPseudoClass::ServoNonZeroBorder => unsafe { + match (*self.element.unsafe_get()).get_attr_for_layout(&ns!(), &atom!("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::Hover | + NonTSPseudoClass::Enabled | + NonTSPseudoClass::Disabled | + NonTSPseudoClass::Checked | + NonTSPseudoClass::Indeterminate | + NonTSPseudoClass::ReadWrite | + NonTSPseudoClass::PlaceholderShown => + self.element.get_state_for_layout().contains(pseudo_class.state_flag()) + } + } + + #[inline] + fn get_id(&self) -> Option { + unsafe { + (*self.element.id_attribute()).clone() + } + } + + #[inline] + fn has_class(&self, name: &Atom) -> bool { + unsafe { + self.element.has_class_for_layout(name) + } + } + + #[inline(always)] + fn each_class(&self, mut callback: F) where F: FnMut(&Atom) { + unsafe { + if let Some(ref classes) = self.element.get_classes_for_layout() { + for class in *classes { + callback(class) + } + } + } + } + + fn match_attr(&self, attr: &AttrSelector, test: F) -> bool where F: Fn(&str) -> bool { + let name = if self.is_html_element_in_html_document() { + &attr.lower_name + } else { + &attr.name + }; + match attr.namespace { + NamespaceConstraint::Specific(ref ns) => { + self.get_attr(ns, name).map_or(false, |attr| test(attr)) + }, + NamespaceConstraint::Any => { + self.get_attrs(name).iter().any(|attr| test(*attr)) + } + } + } + + fn is_html_element_in_html_document(&self) -> bool { + unsafe { + self.element.html_element_in_html_document_for_layout() + } + } + + fn insert_flags(&self, flags: ElementFlags) { + self.element.insert_atomic_flags(flags); + } +} + +#[derive(Copy, Clone)] +pub struct ServoThreadSafeLayoutNode<'ln> { + /// The wrapped node. + node: ServoLayoutNode<'ln>, + + /// The pseudo-element type, with (optionally), + /// an 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 for ServoThreadSafeLayoutNode<'ln> { + unsafe fn dangerous_first_child(&self) -> Option { + self.get_jsmanaged().first_child_ref() + .map(|node| self.new_with_this_lifetime(&node)) + } + unsafe fn dangerous_next_sibling(&self) -> Option { + self.get_jsmanaged().next_sibling_ref() + .map(|node| self.new_with_this_lifetime(&node)) + } +} + +impl<'ln> ServoThreadSafeLayoutNode<'ln> { + /// Creates a new layout node with the same lifetime as this layout node. + pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS) -> ServoThreadSafeLayoutNode<'ln> { + ServoThreadSafeLayoutNode { + node: self.node.new_with_this_lifetime(node), + pseudo: PseudoElementType::Normal, + } + } + + /// Creates a new `ServoThreadSafeLayoutNode` from the given `ServoLayoutNode`. + pub fn new<'a>(node: &ServoLayoutNode<'a>) -> ServoThreadSafeLayoutNode<'a> { + ServoThreadSafeLayoutNode { + node: node.clone(), + pseudo: PseudoElementType::Normal, + } + } + + /// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to + /// call and as such is marked `unsafe`. + unsafe fn get_jsmanaged(&self) -> &LayoutJS { + self.node.get_jsmanaged() + } +} + +impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { + type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'ln>; + type ChildrenIterator = ThreadSafeLayoutNodeChildrenIterator; + + fn with_pseudo(&self, + pseudo: PseudoElementType>) -> ServoThreadSafeLayoutNode<'ln> { + ServoThreadSafeLayoutNode { + node: self.node.clone(), + pseudo: pseudo, + } + } + + fn opaque(&self) -> OpaqueNode { + unsafe { self.get_jsmanaged().opaque() } + } + + fn type_id(&self) -> Option { + if self.pseudo != PseudoElementType::Normal { + return None + } + + Some(self.node.type_id()) + } + + #[inline] + fn type_id_without_excluding_pseudo_elements(&self) -> LayoutNodeType { + self.node.type_id() + } + + fn debug_id(self) -> usize { + self.node.debug_id() + } + + fn children(&self) -> Self::ChildrenIterator { + ThreadSafeLayoutNodeChildrenIterator::new(*self) + } + + fn as_element(&self) -> ServoThreadSafeLayoutElement<'ln> { + unsafe { + let element = match self.get_jsmanaged().downcast() { + Some(e) => e.unsafe_get(), + None => panic!("not an element") + }; + // FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on + // implementations. + ServoThreadSafeLayoutElement { + element: &*element, + } + } + } + + fn get_pseudo_element_type(&self) -> PseudoElementType> { + self.pseudo + } + + fn get_style_and_layout_data(&self) -> Option { + self.node.get_style_and_layout_data() + } + + fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool { + unsafe { + let text: LayoutJS = 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_inheritedtext().white_space.preserve_newlines() + } + } + + fn restyle_damage(self) -> RestyleDamage { + self.node.restyle_damage() + } + + fn set_restyle_damage(self, damage: RestyleDamage) { + self.node.set_restyle_damage(damage) + } + + fn can_be_fragmented(&self) -> bool { + self.node.can_be_fragmented() + } + + fn node_text_content(&self) -> String { + let this = unsafe { self.get_jsmanaged() }; + return this.text_content(); + } + + fn selection(&self) -> Option> { + let this = unsafe { self.get_jsmanaged() }; + + this.selection().map(|range| { + Range::new(ByteIndex(range.start as isize), + ByteIndex(range.len() as isize)) + }) + } + + fn image_url(&self) -> Option { + let this = unsafe { self.get_jsmanaged() }; + this.image_url() + } + + fn canvas_data(&self) -> Option { + let this = unsafe { self.get_jsmanaged() }; + this.canvas_data() + } + + fn iframe_pipeline_id(&self) -> PipelineId { + let this = unsafe { self.get_jsmanaged() }; + this.iframe_pipeline_id() + } + + fn get_colspan(&self) -> u32 { + unsafe { + self.get_jsmanaged().downcast::().unwrap().get_colspan() + } + } + + fn get_style_data(&self) -> Option<&RefCell> { + self.node.get_style_data() + } +} + +pub struct ThreadSafeLayoutNodeChildrenIterator { + current_node: Option, + parent_node: ConcreteNode, +} + +impl ThreadSafeLayoutNodeChildrenIterator + where ConcreteNode: DangerousThreadSafeLayoutNode { + pub fn new(parent: ConcreteNode) -> Self { + let first_child: Option = match parent.get_pseudo_element_type() { + PseudoElementType::Normal => { + parent.get_before_pseudo().or_else(|| parent.get_details_summary_pseudo()).or_else(|| { + unsafe { parent.dangerous_first_child() } + }) + }, + PseudoElementType::DetailsContent(_) | PseudoElementType::DetailsSummary(_) => { + unsafe { parent.dangerous_first_child() } + }, + _ => None, + }; + ThreadSafeLayoutNodeChildrenIterator { + current_node: first_child, + parent_node: parent, + } + } +} + +impl Iterator for ThreadSafeLayoutNodeChildrenIterator + where ConcreteNode: DangerousThreadSafeLayoutNode { + type Item = ConcreteNode; + fn next(&mut self) -> Option { + 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 node.is_element() && + node.as_element().get_local_name() == atom!("summary") && + node.as_element().get_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().get_local_name() == atom!("summary") && + node.as_element().get_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(_) => { + let first = self.parent_node.get_details_summary_pseudo().or_else(|| unsafe { + self.parent_node.dangerous_first_child() + }); + match first { + Some(first) => Some(first), + None => self.parent_node.get_after_pseudo(), + } + }, + PseudoElementType::Normal => { + match unsafe { node.dangerous_next_sibling() } { + Some(next) => Some(next), + None => 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(Copy, Clone)] +pub struct ServoThreadSafeLayoutElement<'le> { + element: &'le Element, +} + +impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> { + type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'le>; + + fn get_attr<'a>(&'a self, namespace: &Namespace, name: &Atom) -> Option<&'a str> { + unsafe { + self.element.get_attr_val_for_layout(namespace, name) + } + } + + #[inline] + fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> { + BorrowedAtom(self.element.local_name()) + } + + #[inline] + fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> { + BorrowedNamespace(self.element.namespace()) + } +} + +/// 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`, +/// `each_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 = ServoSelectorImpl; + + fn parent_element(&self) -> Option { + warn!("ServoThreadSafeLayoutElement::parent_element called"); + None + } + + fn first_child_element(&self) -> Option { + warn!("ServoThreadSafeLayoutElement::first_child_element called"); + None + } + + // Skips non-element nodes + fn last_child_element(&self) -> Option { + warn!("ServoThreadSafeLayoutElement::last_child_element called"); + None + } + + // Skips non-element nodes + fn prev_sibling_element(&self) -> Option { + warn!("ServoThreadSafeLayoutElement::prev_sibling_element called"); + None + } + + // Skips non-element nodes + fn next_sibling_element(&self) -> Option { + warn!("ServoThreadSafeLayoutElement::next_sibling_element called"); + None + } + + fn is_html_element_in_html_document(&self) -> bool { + debug!("ServoThreadSafeLayoutElement::is_html_element_in_html_document called"); + true + } + + #[inline] + fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> { + ThreadSafeLayoutElement::get_local_name(self) + } + + #[inline] + fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> { + ThreadSafeLayoutElement::get_namespace(self) + } + + fn match_non_ts_pseudo_class(&self, _: NonTSPseudoClass) -> bool { + // NB: This could maybe be implemented + warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called"); + false + } + + fn get_id(&self) -> Option { + debug!("ServoThreadSafeLayoutElement::get_id called"); + None + } + + fn has_class(&self, _name: &Atom) -> bool { + debug!("ServoThreadSafeLayoutElement::has_class called"); + false + } + + fn match_attr(&self, attr: &AttrSelector, test: F) -> bool + where F: Fn(&str) -> bool { + match attr.namespace { + NamespaceConstraint::Specific(ref ns) => { + self.get_attr(ns, &attr.name).map_or(false, |attr| test(attr)) + }, + NamespaceConstraint::Any => { + unsafe { + self.element.get_attr_vals_for_layout(&attr.name).iter() + .any(|attr| test(*attr)) + } + } + } + } + + fn is_empty(&self) -> bool { + warn!("ServoThreadSafeLayoutElement::is_empty called"); + false + } + + fn is_root(&self) -> bool { + warn!("ServoThreadSafeLayoutElement::is_root called"); + false + } + + fn each_class(&self, _callback: F) + where F: FnMut(&Atom) { + warn!("ServoThreadSafeLayoutElement::each_class called"); + } +} + +impl<'le> PresentationalHintsSynthetizer for ServoThreadSafeLayoutElement<'le> { + fn synthesize_presentational_hints_for_legacy_attributes(&self, _hints: &mut V) + where V: VecLike>> {} +} diff --git a/components/script/lib.rs b/components/script/lib.rs index 208e48811f6..757c8a28678 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -65,10 +65,12 @@ extern crate phf; #[macro_use] extern crate profile_traits; extern crate rand; +extern crate range; extern crate ref_filter_map; extern crate ref_slice; extern crate regex; extern crate rustc_serialize; +extern crate script_layout_interface; extern crate script_traits; extern crate selectors; extern crate serde; @@ -93,12 +95,11 @@ mod devtools; pub mod document_loader; #[macro_use] pub mod dom; -pub mod layout_interface; +pub mod layout_wrapper; mod mem; mod network_listener; pub mod origin; pub mod parse; -pub mod reporter; pub mod script_runtime; #[allow(unsafe_code)] pub mod script_thread; diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 3459e5ddcfa..8418071e908 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -61,7 +61,6 @@ use js::jsapi::{JSAutoCompartment, JSContext, JS_SetWrapObjectCallbacks}; use js::jsapi::{JSTracer, SetWindowProxyClass}; use js::jsval::UndefinedValue; use js::rust::Runtime; -use layout_interface::{self, NewLayoutThreadInfo, ReflowQueryType}; use mem::heap_size_of_self_and_children; use msg::constellation_msg::{FrameType, LoadData, PanicMsg, PipelineId, PipelineNamespace}; use msg::constellation_msg::{SubpageId, WindowSizeData, WindowSizeType}; @@ -77,6 +76,7 @@ use parse::html::{ParseContext, parse_html}; use parse::xml::{self, parse_xml}; use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan}; use profile_traits::time::{self, ProfilerCategory, profile}; +use script_layout_interface::message::{self, NewLayoutThreadInfo, ReflowQueryType}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory}; use script_runtime::{ScriptPort, StackRootTLS, new_rt_and_cx, get_reports}; use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent}; @@ -135,7 +135,7 @@ struct InProgressLoad { /// The current window size associated with this pipeline. window_size: Option, /// Channel to the layout thread associated with this pipeline. - layout_chan: Sender, + layout_chan: Sender, /// The current viewport clipping rectangle applying to this pipeline, if any. clip_rect: Option>, /// Window is frozen (navigated away while loading for example). @@ -150,7 +150,7 @@ impl InProgressLoad { /// Create a new InProgressLoad object. fn new(id: PipelineId, parent_info: Option<(PipelineId, SubpageId, FrameType)>, - layout_chan: Sender, + layout_chan: Sender, window_size: Option, url: Url) -> InProgressLoad { InProgressLoad { @@ -438,11 +438,11 @@ impl<'a> Drop for ScriptMemoryFailsafe<'a> { } impl ScriptThreadFactory for ScriptThread { - type Message = layout_interface::Msg; + type Message = message::Msg; fn create(state: InitialScriptState, load_data: LoadData) - -> (Sender, Receiver) { + -> (Sender, Receiver) { let panic_chan = state.panic_chan.clone(); let (script_chan, script_port) = channel(); @@ -1184,7 +1184,7 @@ impl ScriptThread { // Tell layout to actually spawn the thread. parent_window.layout_chan() - .send(layout_interface::Msg::CreateLayoutThread(layout_creation_info)) + .send(message::Msg::CreateLayoutThread(layout_creation_info)) .unwrap(); // Kick off the fetch for the new resource. @@ -1462,10 +1462,10 @@ impl ScriptThread { // processed this message. let (response_chan, response_port) = channel(); let chan = &load.layout_chan; - if chan.send(layout_interface::Msg::PrepareToExit(response_chan)).is_ok() { + if chan.send(message::Msg::PrepareToExit(response_chan)).is_ok() { debug!("shutting down layout for page {:?}", id); response_port.recv().unwrap(); - chan.send(layout_interface::Msg::ExitNow).ok(); + chan.send(message::Msg::ExitNow).ok(); } let has_pending_loads = self.incomplete_loads.borrow().len() > 0; @@ -1523,7 +1523,7 @@ impl ScriptThread { { // send the final url to the layout thread. incomplete.layout_chan - .send(layout_interface::Msg::SetFinalUrl(final_url.clone())) + .send(message::Msg::SetFinalUrl(final_url.clone())) .unwrap(); // update the pipeline url @@ -2126,7 +2126,7 @@ fn shut_down_layout(context_tree: &BrowsingContext) { let (response_chan, response_port) = channel(); let window = context.active_window(); let chan = window.layout_chan().clone(); - if chan.send(layout_interface::Msg::PrepareToExit(response_chan)).is_ok() { + if chan.send(message::Msg::PrepareToExit(response_chan)).is_ok() { channels.push(chan); response_port.recv().unwrap(); } @@ -2143,7 +2143,7 @@ fn shut_down_layout(context_tree: &BrowsingContext) { // Destroy the layout thread. If there were node leaks, layout will now crash safely. for chan in channels { - chan.send(layout_interface::Msg::ExitNow).ok(); + chan.send(message::Msg::ExitNow).ok(); } } diff --git a/components/script_layout_interface/Cargo.toml b/components/script_layout_interface/Cargo.toml new file mode 100644 index 00000000000..287c72ee2c0 --- /dev/null +++ b/components/script_layout_interface/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "script_layout_interface" +version = "0.0.1" +authors = ["The Servo Project Developers"] +publish = false + +[lib] +name = "script_layout_interface" +path = "lib.rs" + +[dependencies] +app_units = {version = "0.2.3", features = ["plugins"]} +bitflags = "0.7" +canvas_traits = {path = "../canvas_traits"} +cssparser = {version = "0.5.4", features = ["heap_size", "serde-serialization"]} +euclid = {version = "0.6.4", features = ["plugins"]} +gfx_traits = {path = "../gfx_traits"} +heapsize = "0.3.0" +heapsize_plugin = "0.1.2" +ipc-channel = {git = "https://github.com/servo/ipc-channel"} +libc = "0.2" +log = "0.3.5" +msg = {path = "../msg"} +net_traits = {path = "../net_traits"} +profile_traits = {path = "../profile_traits"} +plugins = {path = "../plugins"} +range = {path = "../range"} +script_traits = {path = "../script_traits"} +selectors = {version = "0.6", features = ["heap_size"]} +string_cache = {version = "0.2.20", features = ["heap_size"]} +style = {path = "../style"} +url = {version = "1.0.0", features = ["heap_size"]} +util = {path = "../util"} diff --git a/components/script_layout_interface/lib.rs b/components/script_layout_interface/lib.rs new file mode 100644 index 00000000000..c2e06e3dcf7 --- /dev/null +++ b/components/script_layout_interface/lib.rs @@ -0,0 +1,116 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +//! This module contains traits in script used generically in the rest of Servo. +//! The traits are here instead of in script so that these modules won't have +//! to depend on script. + +#![deny(unsafe_code)] +#![feature(box_syntax)] +#![feature(custom_attribute)] +#![feature(custom_derive)] +#![feature(nonzero)] +#![feature(plugin)] +#![plugin(heapsize_plugin)] +#![plugin(plugins)] + +extern crate app_units; +#[allow(unused_extern_crates)] +#[macro_use] +extern crate bitflags; +extern crate canvas_traits; +extern crate core; +extern crate cssparser; +extern crate euclid; +extern crate gfx_traits; +extern crate heapsize; +extern crate ipc_channel; +extern crate libc; +#[macro_use] +extern crate log; +extern crate msg; +extern crate net_traits; +extern crate profile_traits; +extern crate range; +extern crate script_traits; +extern crate selectors; +#[macro_use(atom, ns)] +extern crate string_cache; +extern crate style; +extern crate url; +extern crate util; + +pub mod message; +pub mod reporter; +pub mod restyle_damage; +pub mod rpc; +pub mod wrapper_traits; + +use canvas_traits::CanvasMsg; +use core::nonzero::NonZero; +use ipc_channel::ipc::IpcSender; +use libc::c_void; +use restyle_damage::RestyleDamage; +use std::cell::RefCell; +use style::servo::PrivateStyleData; + +pub struct PartialStyleAndLayoutData { + pub style_data: PrivateStyleData, + pub restyle_damage: RestyleDamage, +} + +#[derive(Copy, Clone, HeapSizeOf)] +pub struct OpaqueStyleAndLayoutData { + #[ignore_heap_size_of = "TODO(#6910) Box value that should be counted but \ + the type lives in layout"] + pub ptr: NonZero<*mut RefCell> +} + +#[allow(unsafe_code)] +unsafe impl Send for OpaqueStyleAndLayoutData {} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum LayoutNodeType { + Comment, + Document, + DocumentFragment, + DocumentType, + Element(LayoutElementType), + ProcessingInstruction, + Text, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum LayoutElementType { + Element, + HTMLCanvasElement, + HTMLIFrameElement, + HTMLImageElement, + HTMLInputElement, + HTMLObjectElement, + HTMLTableCellElement, + HTMLTableColElement, + HTMLTableElement, + HTMLTableRowElement, + HTMLTableSectionElement, + HTMLTextAreaElement, +} + +pub struct HTMLCanvasData { + pub ipc_renderer: Option>, + pub width: u32, + pub height: u32, +} + +/// The address of a node known to be valid. These are sent from script to layout. +#[derive(Clone, PartialEq, Eq, Copy)] +pub struct TrustedNodeAddress(pub *const c_void); + +#[allow(unsafe_code)] +unsafe impl Send for TrustedNodeAddress {} + +pub fn is_image_data(uri: &str) -> bool { + static TYPES: &'static [&'static str] = &["data:image/png", "data:image/gif", "data:image/jpeg"]; + TYPES.iter().any(|&type_| uri.starts_with(type_)) +} diff --git a/components/script/layout_interface.rs b/components/script_layout_interface/message.rs similarity index 54% rename from components/script/layout_interface.rs rename to components/script_layout_interface/message.rs index 37b6d169481..e3c8a14e4c3 100644 --- a/components/script/layout_interface.rs +++ b/components/script_layout_interface/message.rs @@ -2,10 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -//! The high-level interface from script to layout. Using this abstract -//! interface helps reduce coupling between these two components, and enables -//! the DOM to be placed in a separate crate from layout. - use app_units::Au; use euclid::point::Point2D; use euclid::rect::Rect; @@ -14,33 +10,18 @@ use ipc_channel::ipc::{IpcReceiver, IpcSender}; use msg::constellation_msg::{PanicMsg, PipelineId, WindowSizeData}; use net_traits::image_cache_thread::ImageCacheThread; use profile_traits::mem::ReportsChan; -use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg}; -use script_traits::{StackingContextScrollState, UntrustedNodeAddress}; +use rpc::LayoutRPC; +use script_traits::{ConstellationControlMsg, LayoutControlMsg}; +use script_traits::{LayoutMsg as ConstellationMsg, StackingContextScrollState}; use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender}; use string_cache::Atom; use style::context::ReflowGoal; -use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left, overflow_x}; use style::selector_impl::PseudoElement; use style::servo::Stylesheet; use url::Url; use util::ipc::OptionalOpaqueIpcSender; - -pub use dom::bindings::inheritance::{CharacterDataTypeId, ElementTypeId}; -pub use dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId}; -pub use dom::bindings::js::LayoutJS; -pub use dom::characterdata::LayoutCharacterDataHelpers; -pub use dom::document::{Document, LayoutDocumentHelpers}; -pub use dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers}; -pub use dom::htmlcanvaselement::HTMLCanvasData; -pub use dom::htmlobjectelement::is_image_data; -pub use dom::node::{CAN_BE_FRAGMENTED, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY}; -pub use dom::node::LayoutNodeHelpers; -pub use dom::node::Node; -pub use dom::node::OpaqueStyleAndLayoutData; -pub use dom::node::TrustedNodeAddress; -pub use dom::text::Text; - +use {OpaqueStyleAndLayoutData, TrustedNodeAddress}; /// Asynchronous messages that script can send to layout. pub enum Msg { @@ -103,88 +84,6 @@ pub enum Msg { SetStackingContextScrollStates(Vec), } -/// Synchronous messages that script can send to layout. -/// -/// In general, you should use messages to talk to Layout. Use the RPC interface -/// if and only if the work is -/// -/// 1) read-only with respect to LayoutThreadData, -/// 2) small, -/// 3) and really needs to be fast. -pub trait LayoutRPC { - /// Requests the dimensions of the content box, as in the `getBoundingClientRect()` call. - fn content_box(&self) -> ContentBoxResponse; - /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call. - fn content_boxes(&self) -> ContentBoxesResponse; - /// Requests the geometry of this node. Used by APIs such as `clientTop`. - fn node_geometry(&self) -> NodeGeometryResponse; - /// Requests the overflow-x and overflow-y of this node. Used by `scrollTop` etc. - fn node_overflow(&self) -> NodeOverflowResponse; - /// Requests the scroll geometry of this node. Used by APIs such as `scrollTop`. - fn node_scroll_area(&self) -> NodeGeometryResponse; - /// Requests the layer id of this node. Used by APIs such as `scrollTop` - fn node_layer_id(&self) -> NodeLayerIdResponse; - /// Requests the node containing the point of interest - fn hit_test(&self) -> HitTestResponse; - /// Query layout for the resolved value of a given CSS property - fn resolved_style(&self) -> ResolvedStyleResponse; - fn offset_parent(&self) -> OffsetParentResponse; - /// Query layout for the resolve values of the margin properties for an element. - fn margin_style(&self) -> MarginStyleResponse; - - fn nodes_from_point(&self, point: Point2D) -> Vec; -} - -#[derive(Clone)] -pub struct MarginStyleResponse { - pub top: margin_top::computed_value::T, - pub right: margin_right::computed_value::T, - pub bottom: margin_bottom::computed_value::T, - pub left: margin_left::computed_value::T, -} - -impl MarginStyleResponse { - pub fn empty() -> MarginStyleResponse { - MarginStyleResponse { - top: margin_top::computed_value::T::Auto, - right: margin_right::computed_value::T::Auto, - bottom: margin_bottom::computed_value::T::Auto, - left: margin_left::computed_value::T::Auto, - } - } -} - -pub struct NodeOverflowResponse(pub Option>); - -pub struct ContentBoxResponse(pub Rect); -pub struct ContentBoxesResponse(pub Vec>); -pub struct HitTestResponse { - pub node_address: Option, -} -pub struct NodeGeometryResponse { - pub client_rect: Rect, -} - -pub struct NodeLayerIdResponse { - pub layer_id: LayerId, -} - -pub struct ResolvedStyleResponse(pub Option); - -#[derive(Clone)] -pub struct OffsetParentResponse { - pub node_address: Option, - pub rect: Rect, -} - -impl OffsetParentResponse { - pub fn empty() -> OffsetParentResponse { - OffsetParentResponse { - node_address: None, - rect: Rect::zero(), - } - } -} /// Any query to perform with this reflow. #[derive(PartialEq)] diff --git a/components/script/reporter.rs b/components/script_layout_interface/reporter.rs similarity index 98% rename from components/script/reporter.rs rename to components/script_layout_interface/reporter.rs index 39b9e81d333..4561f2d63ca 100644 --- a/components/script/reporter.rs +++ b/components/script_layout_interface/reporter.rs @@ -10,7 +10,7 @@ use script_traits::ConstellationControlMsg; use std::sync::{Mutex, Arc}; use style::error_reporting::ParseErrorReporter; -#[derive(JSTraceable, HeapSizeOf)] +#[derive(HeapSizeOf)] pub struct CSSErrorReporter { pub pipelineid: PipelineId, // Arc+Mutex combo is necessary to make this struct Sync, diff --git a/components/script_layout_interface/restyle_damage.rs b/components/script_layout_interface/restyle_damage.rs new file mode 100644 index 00000000000..56a068a2237 --- /dev/null +++ b/components/script_layout_interface/restyle_damage.rs @@ -0,0 +1,258 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use std::fmt; +use std::sync::Arc; +use style::computed_values::display; +use style::dom::TRestyleDamage; +use style::properties::{ComputedValues, ServoComputedValues}; + +bitflags! { + #[doc = "Individual layout actions that may be necessary after restyling."] + pub flags RestyleDamage: u8 { + #[doc = "Repaint the node itself."] + #[doc = "Currently unused; need to decide how this propagates."] + const REPAINT = 0x01, + + #[doc = "Recompute the overflow regions (bounding box of object and all descendants)."] + #[doc = "Propagates down the flow tree because the computation is bottom-up."] + const STORE_OVERFLOW = 0x02, + + #[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."] + #[doc = "Propagates down the flow tree because the computation is"] + #[doc = "bottom-up."] + const BUBBLE_ISIZES = 0x04, + + #[doc = "Recompute actual inline-sizes and block-sizes, only taking out-of-flow children \ + into account. \ + Propagates up the flow tree because the computation is top-down."] + const REFLOW_OUT_OF_FLOW = 0x08, + + #[doc = "Recompute actual inline_sizes and block_sizes."] + #[doc = "Propagates up the flow tree because the computation is"] + #[doc = "top-down."] + const REFLOW = 0x10, + + #[doc = "Re-resolve generated content. \ + Propagates up the flow tree because the computation is inorder."] + const RESOLVE_GENERATED_CONTENT = 0x20, + + #[doc = "The entire flow needs to be reconstructed."] + const RECONSTRUCT_FLOW = 0x40 + } +} + +impl TRestyleDamage for RestyleDamage { + type ConcreteComputedValues = ServoComputedValues; + fn compute(old: Option<&Arc>, new: &ServoComputedValues) -> + RestyleDamage { compute_damage(old, new) } + + /// Returns a bitmask that represents a flow that needs to be rebuilt and reflowed. + /// + /// Use this instead of `RestyleDamage::all()` because `RestyleDamage::all()` will result in + /// unnecessary sequential resolution of generated content. + fn rebuild_and_reflow() -> RestyleDamage { + REPAINT | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW + } +} + +impl RestyleDamage { + /// Supposing a flow has the given `position` property and this damage, returns the damage that + /// we should add to the *parent* of this flow. + pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage { + if child_is_absolutely_positioned { + self & (REPAINT | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT) + } else { + self & (REPAINT | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW | + RESOLVE_GENERATED_CONTENT) + } + } + + /// Supposing the *parent* of a flow with the given `position` property has this damage, + /// returns the damage that we should add to this flow. + pub fn damage_for_child(self, + parent_is_absolutely_positioned: bool, + child_is_absolutely_positioned: bool) + -> RestyleDamage { + match (parent_is_absolutely_positioned, child_is_absolutely_positioned) { + (false, true) => { + // Absolute children are out-of-flow and therefore insulated from changes. + // + // FIXME(pcwalton): Au contraire, if the containing block dimensions change! + self & REPAINT + } + (true, false) => { + // Changing the position of an absolutely-positioned block requires us to reflow + // its kids. + if self.contains(REFLOW_OUT_OF_FLOW) { + self | REFLOW + } else { + self + } + } + _ => { + // TODO(pcwalton): Take floatedness into account. + self & (REPAINT | REFLOW) + } + } + } +} + +impl fmt::Display for RestyleDamage { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let mut first_elem = true; + + let to_iter = + [ (REPAINT, "Repaint") + , (STORE_OVERFLOW, "StoreOverflow") + , (BUBBLE_ISIZES, "BubbleISizes") + , (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow") + , (REFLOW, "Reflow") + , (RESOLVE_GENERATED_CONTENT, "ResolveGeneratedContent") + , (RECONSTRUCT_FLOW, "ReconstructFlow") + ]; + + for &(damage, damage_str) in &to_iter { + if self.contains(damage) { + if !first_elem { try!(write!(f, " | ")); } + try!(write!(f, "{}", damage_str)); + first_elem = false; + } + } + + if first_elem { + try!(write!(f, "NoDamage")); + } + + Ok(()) + } +} + +// NB: We need the braces inside the RHS due to Rust #8012. This particular +// version of this macro might be safe anyway, but we want to avoid silent +// breakage on modifications. +macro_rules! add_if_not_equal( + ($old:ident, $new:ident, $damage:ident, + [ $($effect:ident),* ], [ $($style_struct_getter:ident.$name:ident),* ]) => ({ + if $( ($old.$style_struct_getter().$name != $new.$style_struct_getter().$name) )||* { + $damage.insert($($effect)|*); + true + } else { + false + } + }) +); + +fn compute_damage(old: Option<&Arc>, new: &ServoComputedValues) -> RestyleDamage { + let old: &ServoComputedValues = match old { + None => return RestyleDamage::rebuild_and_reflow(), + Some(cv) => &**cv, + }; + + let mut damage = RestyleDamage::empty(); + + // This should check every CSS property, as enumerated in the fields of + // http://doc.servo.org/style/properties/struct.ServoComputedValues.html + + // FIXME: Test somehow that every property is included. + + add_if_not_equal!(old, new, damage, + [ + REPAINT, + STORE_OVERFLOW, + BUBBLE_ISIZES, + REFLOW_OUT_OF_FLOW, + REFLOW, + RECONSTRUCT_FLOW + ], [ + get_box.float, get_box.display, get_box.position, get_counters.content, + get_counters.counter_reset, get_counters.counter_increment, + get_inheritedbox._servo_under_display_none, + get_list.quotes, get_list.list_style_type, + + // If these text or font properties change, we need to reconstruct the flow so that + // text shaping is re-run. + get_inheritedtext.letter_spacing, get_inheritedtext.text_rendering, + get_inheritedtext.text_transform, get_inheritedtext.word_spacing, + get_inheritedtext.overflow_wrap, get_inheritedtext.text_justify, + get_inheritedtext.white_space, get_inheritedtext.word_break, get_text.text_overflow, + get_font.font_family, get_font.font_style, get_font.font_variant, get_font.font_weight, + get_font.font_size, get_font.font_stretch, + get_inheritedbox.direction, get_inheritedbox.writing_mode, + get_inheritedbox.text_orientation, + get_text.text_decoration, get_text.unicode_bidi, + get_inheritedtable.empty_cells, get_inheritedtable.caption_side, + get_column.column_width, get_column.column_count + ]) || (new.get_box().display == display::T::inline && + add_if_not_equal!(old, new, damage, + [REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW, + RECONSTRUCT_FLOW], [ + // For inline boxes only, border/padding styles are used in flow construction (to decide + // whether to create fragments for empty flows). + get_border.border_top_width, get_border.border_right_width, + get_border.border_bottom_width, get_border.border_left_width, + get_padding.padding_top, get_padding.padding_right, + get_padding.padding_bottom, get_padding.padding_left + ])) || add_if_not_equal!(old, new, damage, + [ REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW ], + [get_border.border_top_width, get_border.border_right_width, + get_border.border_bottom_width, get_border.border_left_width, + get_margin.margin_top, get_margin.margin_right, + get_margin.margin_bottom, get_margin.margin_left, + get_padding.padding_top, get_padding.padding_right, + get_padding.padding_bottom, get_padding.padding_left, + get_position.width, get_position.height, + get_inheritedtext.line_height, + get_inheritedtext.text_align, get_inheritedtext.text_indent, + get_table.table_layout, + get_inheritedtable.border_collapse, + get_inheritedtable.border_spacing, + get_column.column_gap, + get_position.flex_direction, + get_position.flex_wrap, + get_position.justify_content, + get_position.align_items, + get_position.align_content, + get_position.order, + get_position.flex_basis, + get_position.flex_grow, + get_position.flex_shrink, + get_position.align_self + ]) || add_if_not_equal!(old, new, damage, + [ REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW ], [ + get_position.top, get_position.left, + get_position.right, get_position.bottom + ]) || add_if_not_equal!(old, new, damage, + [ REPAINT ], [ + get_color.color, get_background.background_color, + get_background.background_image, get_background.background_position, + get_background.background_repeat, get_background.background_attachment, + get_background.background_clip, get_background.background_origin, + get_background.background_size, + get_border.border_top_color, get_border.border_right_color, + get_border.border_bottom_color, get_border.border_left_color, + get_border.border_top_style, get_border.border_right_style, + get_border.border_bottom_style, get_border.border_left_style, + get_border.border_top_left_radius, get_border.border_top_right_radius, + get_border.border_bottom_left_radius, get_border.border_bottom_right_radius, + get_position.z_index, get_box._servo_overflow_clip_box, + get_inheritedtext._servo_text_decorations_in_effect, + get_pointing.cursor, get_pointing.pointer_events, + get_effects.box_shadow, get_effects.clip, get_inheritedtext.text_shadow, get_effects.filter, + get_effects.transform, get_effects.backface_visibility, get_effects.transform_style, + get_effects.transform_origin, get_effects.perspective, get_effects.perspective_origin, + get_effects.mix_blend_mode, get_inheritedbox.image_rendering, + + // Note: May require REFLOW et al. if `visibility: collapse` is implemented. + get_inheritedbox.visibility + ]); + + // If the layer requirements of this flow have changed due to the value + // of the transform, then reflow is required to rebuild the layers. + if old.transform_requires_layer() != new.transform_requires_layer() { + damage.insert(RestyleDamage::rebuild_and_reflow()); + } + + damage +} diff --git a/components/script_layout_interface/rpc.rs b/components/script_layout_interface/rpc.rs new file mode 100644 index 00000000000..21cac2a19c2 --- /dev/null +++ b/components/script_layout_interface/rpc.rs @@ -0,0 +1,96 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use app_units::Au; +use euclid::point::Point2D; +use euclid::rect::Rect; +use gfx_traits::LayerId; +use script_traits::UntrustedNodeAddress; +use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left, overflow_x}; + +/// Synchronous messages that script can send to layout. +/// +/// In general, you should use messages to talk to Layout. Use the RPC interface +/// if and only if the work is +/// +/// 1) read-only with respect to LayoutThreadData, +/// 2) small, +/// 3) and really needs to be fast. +pub trait LayoutRPC { + /// Requests the dimensions of the content box, as in the `getBoundingClientRect()` call. + fn content_box(&self) -> ContentBoxResponse; + /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call. + fn content_boxes(&self) -> ContentBoxesResponse; + /// Requests the geometry of this node. Used by APIs such as `clientTop`. + fn node_geometry(&self) -> NodeGeometryResponse; + /// Requests the overflow-x and overflow-y of this node. Used by `scrollTop` etc. + fn node_overflow(&self) -> NodeOverflowResponse; + /// Requests the scroll geometry of this node. Used by APIs such as `scrollTop`. + fn node_scroll_area(&self) -> NodeGeometryResponse; + /// Requests the layer id of this node. Used by APIs such as `scrollTop` + fn node_layer_id(&self) -> NodeLayerIdResponse; + /// Requests the node containing the point of interest + fn hit_test(&self) -> HitTestResponse; + /// Query layout for the resolved value of a given CSS property + fn resolved_style(&self) -> ResolvedStyleResponse; + fn offset_parent(&self) -> OffsetParentResponse; + /// Query layout for the resolve values of the margin properties for an element. + fn margin_style(&self) -> MarginStyleResponse; + + fn nodes_from_point(&self, point: Point2D) -> Vec; +} + +pub struct ContentBoxResponse(pub Rect); + +pub struct ContentBoxesResponse(pub Vec>); + +pub struct NodeGeometryResponse { + pub client_rect: Rect, +} + +pub struct NodeOverflowResponse(pub Option>); + +pub struct NodeLayerIdResponse { + pub layer_id: LayerId, +} + +pub struct HitTestResponse { + pub node_address: Option, +} + +pub struct ResolvedStyleResponse(pub Option); + +#[derive(Clone)] +pub struct OffsetParentResponse { + pub node_address: Option, + pub rect: Rect, +} + +impl OffsetParentResponse { + pub fn empty() -> OffsetParentResponse { + OffsetParentResponse { + node_address: None, + rect: Rect::zero(), + } + } +} + +#[derive(Clone)] +pub struct MarginStyleResponse { + pub top: margin_top::computed_value::T, + pub right: margin_right::computed_value::T, + pub bottom: margin_bottom::computed_value::T, + pub left: margin_left::computed_value::T, +} + +impl MarginStyleResponse { + pub fn empty() -> MarginStyleResponse { + MarginStyleResponse { + top: margin_top::computed_value::T::Auto, + right: margin_right::computed_value::T::Auto, + bottom: margin_bottom::computed_value::T::Auto, + left: margin_left::computed_value::T::Auto, + } + } +} diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs new file mode 100644 index 00000000000..145f9dd067b --- /dev/null +++ b/components/script_layout_interface/wrapper_traits.rs @@ -0,0 +1,366 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use HTMLCanvasData; +use LayoutNodeType; +use OpaqueStyleAndLayoutData; +use PartialStyleAndLayoutData; +use gfx_traits::{ByteIndex, LayerId, LayerType}; +use msg::constellation_msg::PipelineId; +use range::Range; +use restyle_damage::RestyleDamage; +use std::cell::{Ref, RefCell}; +use std::sync::Arc; +use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace}; +use style::computed_values::display; +use style::dom::OpaqueNode; +use style::dom::{PresentationalHintsSynthetizer, TNode}; +use style::properties::ServoComputedValues; +use style::selector_impl::{PseudoElement, PseudoElementCascadeType, ServoSelectorImpl}; +use style::servo::SharedStyleContext; +use url::Url; + +#[derive(Copy, PartialEq, Clone)] +pub enum PseudoElementType { + Normal, + Before(T), + After(T), + DetailsSummary(T), + DetailsContent(T), +} + +impl PseudoElementType { + 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 strip(&self) -> PseudoElementType<()> { + match *self { + PseudoElementType::Normal => PseudoElementType::Normal, + PseudoElementType::Before(_) => PseudoElementType::Before(()), + PseudoElementType::After(_) => PseudoElementType::After(()), + PseudoElementType::DetailsSummary(_) => PseudoElementType::DetailsSummary(()), + PseudoElementType::DetailsContent(_) => PseudoElementType::DetailsContent(()), + } + } + + 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, + } + } +} + +/// 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 `LayoutJS`. +pub trait LayoutNode: TNode { + type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode; + fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode; + + /// Returns the type ID of this node. + fn type_id(&self) -> LayoutNodeType; + + fn get_style_data(&self) -> Option<&RefCell>; + + fn init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData); + fn get_style_and_layout_data(&self) -> Option; +} + +/// 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: Clone + Copy + Sized + PartialEq { + type ConcreteThreadSafeLayoutElement: + ThreadSafeLayoutElement + + ::selectors::Element; + type ChildrenIterator: Iterator + Sized; + + /// Creates a new `ThreadSafeLayoutNode` for the same `LayoutNode` + /// with a different pseudo-element type. + fn with_pseudo(&self, pseudo: PseudoElementType>) -> Self; + + /// 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; + + /// Returns the type ID of this node, without discarding pseudo-elements as + /// `type_id` does. + fn type_id_without_excluding_pseudo_elements(&self) -> LayoutNodeType; + + #[inline] + fn is_element_or_elements_pseudo(&self) -> bool { + match self.type_id_without_excluding_pseudo_elements() { + LayoutNodeType::Element(..) => true, + _ => false, + } + } + + fn debug_id(self) -> usize; + + /// Returns an iterator over this node's children. + fn children(&self) -> Self::ChildrenIterator; + + #[inline] + fn is_element(&self) -> bool { if let Some(LayoutNodeType::Element(_)) = self.type_id() { true } else { false } } + + /// If this is an element, accesses the element data. Fails if this is not an element node. + #[inline] + fn as_element(&self) -> Self::ConcreteThreadSafeLayoutElement; + + #[inline] + fn get_pseudo_element_type(&self) -> PseudoElementType>; + + #[inline] + fn get_before_pseudo(&self) -> Option { + if self.get_style_data() + .unwrap() + .borrow() + .style_data + .per_pseudo + .contains_key(&PseudoElement::Before) { + Some(self.with_pseudo(PseudoElementType::Before(None))) + } else { + None + } + } + + #[inline] + fn get_after_pseudo(&self) -> Option { + if self.get_style_data() + .unwrap() + .borrow() + .style_data + .per_pseudo + .contains_key(&PseudoElement::After) { + Some(self.with_pseudo(PseudoElementType::After(None))) + } else { + None + } + } + + #[inline] + fn get_details_summary_pseudo(&self) -> Option { + if self.is_element() && + self.as_element().get_local_name() == atom!("details") && + self.as_element().get_namespace() == ns!(html) { + Some(self.with_pseudo(PseudoElementType::DetailsSummary(None))) + } else { + None + } + } + + #[inline] + fn get_details_content_pseudo(&self) -> Option { + if self.is_element() && + self.as_element().get_local_name() == atom!("details") && + self.as_element().get_namespace() == ns!(html) { + let display = if self.as_element().get_attr(&ns!(), &atom!("open")).is_some() { + None // Specified by the stylesheet + } else { + Some(display::T::none) + }; + Some(self.with_pseudo(PseudoElementType::DetailsContent(display))) + } else { + None + } + } + + fn get_style_and_layout_data(&self) -> Option; + + /// 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) -> Ref> { + match self.get_pseudo_element_type() { + PseudoElementType::Normal => { + Ref::map(self.get_style_data().unwrap().borrow(), |data| { + data.style_data.style.as_ref().unwrap() + }) + }, + 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 => {}, + PseudoElementCascadeType::Precomputed => { + if !self.get_style_data() + .unwrap() + .borrow() + .style_data + .per_pseudo.contains_key(&style_pseudo) { + let mut data = self.get_style_data().unwrap().borrow_mut(); + let new_style = + context.stylist + .precomputed_values_for_pseudo(&style_pseudo, + data.style_data.style.as_ref()); + data.style_data.per_pseudo + .insert(style_pseudo.clone(), new_style.unwrap()); + } + } + PseudoElementCascadeType::Lazy => { + debug_assert!(self.is_element_or_elements_pseudo()); + if !self.get_style_data() + .unwrap() + .borrow() + .style_data + .per_pseudo.contains_key(&style_pseudo) { + let mut data = self.get_style_data().unwrap().borrow_mut(); + let new_style = + context.stylist + .lazily_compute_pseudo_element_style( + &self.as_element(), + &style_pseudo, + data.style_data.style.as_ref().unwrap()); + data.style_data.per_pseudo + .insert(style_pseudo.clone(), new_style.unwrap()); + } + } + } + + Ref::map(self.get_style_data().unwrap().borrow(), |data| { + data.style_data.per_pseudo.get(&style_pseudo).unwrap() + }) + } + } + } + + /// 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) -> Ref> { + Ref::map(self.get_style_data().unwrap().borrow(), |data| { + match self.get_pseudo_element_type() { + PseudoElementType::Normal + => data.style_data.style.as_ref().unwrap(), + other + => data.style_data.per_pseudo.get(&other.style_pseudo_element()).unwrap(), + } + }) + } + + #[inline] + fn selected_style(&self, _context: &SharedStyleContext) -> Ref> { + Ref::map(self.get_style_data().unwrap().borrow(), |data| { + data.style_data.per_pseudo + .get(&PseudoElement::Selection) + .unwrap_or(data.style_data.style.as_ref().unwrap()) + }) + } + + /// Removes the style from this node. + /// + /// Unlike the version on TNode, this handles pseudo-elements. + fn unstyle(self) { + let mut data = self.get_style_data().unwrap().borrow_mut(); + + match self.get_pseudo_element_type() { + PseudoElementType::Normal => { + data.style_data.style = None; + } + other => { + data.style_data.per_pseudo.remove(&other.style_pseudo_element()); + } + }; + } + + fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool; + + fn restyle_damage(self) -> RestyleDamage; + + fn set_restyle_damage(self, damage: RestyleDamage); + + /// 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 { + match self.type_id() { + Some(LayoutNodeType::Element(..)) | Some(LayoutNodeType::Text) => true, + _ => false + } + } + + fn can_be_fragmented(&self) -> bool; + + fn node_text_content(&self) -> String; + + /// If the insertion point is within this node, returns it. Otherwise, returns `None`. + fn selection(&self) -> Option>; + + /// If this is an image element, returns its URL. If this is not an image element, fails. + /// + /// FIXME(pcwalton): Don't copy URLs. + fn image_url(&self) -> Option; + + fn canvas_data(&self) -> Option; + + /// If this node is an iframe element, returns its pipeline ID. If this node is + /// not an iframe element, fails. + fn iframe_pipeline_id(&self) -> PipelineId; + + fn get_colspan(&self) -> u32; + + fn layer_id(&self) -> LayerId { + let layer_type = match self.get_pseudo_element_type() { + PseudoElementType::Normal => LayerType::FragmentBody, + PseudoElementType::Before(_) => LayerType::BeforePseudoContent, + PseudoElementType::After(_) => LayerType::AfterPseudoContent, + PseudoElementType::DetailsSummary(_) => LayerType::FragmentBody, + PseudoElementType::DetailsContent(_) => LayerType::FragmentBody, + }; + LayerId::new_of_type(layer_type, self.opaque().id() as usize) + } + + fn layer_id_for_overflow_scroll(&self) -> LayerId { + LayerId::new_of_type(LayerType::OverflowScroll, self.opaque().id() as usize) + } + + fn get_style_data(&self) -> Option<&RefCell>; +} + +// This trait is only public so that it can be implemented by the gecko wrapper. +// It can be used to violate thread-safety, so don't use it elsewhere in layout! +#[allow(unsafe_code)] +pub trait DangerousThreadSafeLayoutNode: ThreadSafeLayoutNode { + unsafe fn dangerous_first_child(&self) -> Option; + unsafe fn dangerous_next_sibling(&self) -> Option; +} + +pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + + ::selectors::Element + + PresentationalHintsSynthetizer { + type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode; + + #[inline] + fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str>; + + #[inline] + fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a>; + + #[inline] + fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a>; +} diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 3139d73870e..0b3c9855b79 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -33,6 +33,7 @@ dependencies = [ "profile_tests 0.0.1", "profile_traits 0.0.1", "script 0.0.1", + "script_layout_interface 0.0.1", "script_tests 0.0.1", "script_traits 0.0.1", "style 0.0.1", @@ -814,6 +815,8 @@ dependencies = [ "layers 0.2.5 (git+https://github.com/servo/rust-layers)", "msg 0.0.1", "plugins 0.0.1", + "range 0.0.1", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", "serde_macros 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1155,6 +1158,7 @@ dependencies = [ "range 0.0.1", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "script 0.0.1", + "script_layout_interface 0.0.1", "script_traits 0.0.1", "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1903,10 +1907,12 @@ dependencies = [ "plugins 0.0.1", "profile_traits 0.0.1", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "range 0.0.1", "ref_filter_map 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "ref_slice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.71 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", + "script_layout_interface 0.0.1", "script_traits 0.0.1", "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1923,6 +1929,34 @@ dependencies = [ "xml5ever 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "script_layout_interface" +version = "0.0.1" +dependencies = [ + "app_units 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "canvas_traits 0.0.1", + "cssparser 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx_traits 0.0.1", + "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "heapsize_plugin 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ipc-channel 0.2.3 (git+https://github.com/servo/ipc-channel)", + "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "msg 0.0.1", + "net_traits 0.0.1", + "plugins 0.0.1", + "profile_traits 0.0.1", + "range 0.0.1", + "script_traits 0.0.1", + "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "style 0.0.1", + "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "util 0.0.1", +] + [[package]] name = "script_tests" version = "0.0.1" diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index b7956a6b198..dc1dc080e73 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -43,6 +43,7 @@ compiletest_helper = {path = "../../tests/compiletest/helper"} plugin_compiletest = {path = "../../tests/compiletest/plugin"} [dependencies] +script_layout_interface = {path = "../script_layout_interface"} webrender_traits = {git = "https://github.com/servo/webrender_traits"} webrender = {git = "https://github.com/servo/webrender"} compositing = {path = "../compositing"} diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 363cd3bec4f..5f98a1894b6 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -39,6 +39,7 @@ pub extern crate profile; pub extern crate profile_traits; pub extern crate script; pub extern crate script_traits; +pub extern crate script_layout_interface; pub extern crate style; pub extern crate url; pub extern crate util; @@ -229,7 +230,7 @@ fn create_constellation(opts: opts::Opts, webrender_api_sender: webrender_api_sender, }; let constellation_chan = - Constellation::::start(initial_state); @@ -263,7 +264,7 @@ pub fn run_content_process(token: String) { script::init(); - unprivileged_content.start_all::(true); } diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index b92f6724a90..a5fc590683d 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -727,6 +727,8 @@ dependencies = [ "layers 0.2.5 (git+https://github.com/servo/rust-layers)", "msg 0.0.1", "plugins 0.0.1", + "range 0.0.1", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", "serde_macros 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1068,6 +1070,7 @@ dependencies = [ "range 0.0.1", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "script 0.0.1", + "script_layout_interface 0.0.1", "script_traits 0.0.1", "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1761,10 +1764,12 @@ dependencies = [ "plugins 0.0.1", "profile_traits 0.0.1", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "range 0.0.1", "ref_filter_map 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "ref_slice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.71 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", + "script_layout_interface 0.0.1", "script_traits 0.0.1", "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1781,6 +1786,34 @@ dependencies = [ "xml5ever 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "script_layout_interface" +version = "0.0.1" +dependencies = [ + "app_units 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "canvas_traits 0.0.1", + "cssparser 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx_traits 0.0.1", + "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "heapsize_plugin 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ipc-channel 0.2.3 (git+https://github.com/servo/ipc-channel)", + "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "msg 0.0.1", + "net_traits 0.0.1", + "plugins 0.0.1", + "profile_traits 0.0.1", + "range 0.0.1", + "script_traits 0.0.1", + "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "style 0.0.1", + "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "util 0.0.1", +] + [[package]] name = "script_traits" version = "0.0.1" @@ -1888,6 +1921,7 @@ dependencies = [ "profile 0.0.1", "profile_traits 0.0.1", "script 0.0.1", + "script_layout_interface 0.0.1", "script_traits 0.0.1", "style 0.0.1", "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/tests/unit/layout/size_of.rs b/tests/unit/layout/size_of.rs index 033916647ee..320f85e0169 100644 --- a/tests/unit/layout/size_of.rs +++ b/tests/unit/layout/size_of.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use layout::Fragment; -use layout::ServoThreadSafeLayoutNode; use std::mem::size_of; #[test] @@ -25,23 +24,3 @@ fn test_size_of_fragment() { expected, actual); } } - -#[test] -fn test_size_of_layout_node() { - let expected = 16; - let actual = size_of::(); - - if actual < expected { - panic!("Your changes have decreased the stack size of layout::wrapper::ServoThreadSafeLayoutNode \ - from {} to {}. Good work! Please update the size in tests/layout/unit/size_of.rs", - expected, actual); - } - - if actual > expected { - panic!("Your changes have increased the stack size of layout::wrapper::ServoThreadSafeLayoutNode \ - from {} to {}. Please consider choosing a design which avoids this increase. \ - If you feel that the increase is necessary, update the size in \ - tests/unit/layout/size_of.rs.", - expected, actual); - } -} diff --git a/tests/unit/script/size_of.rs b/tests/unit/script/size_of.rs index fc9cf6f09bf..21b8e4a4ba2 100644 --- a/tests/unit/script/size_of.rs +++ b/tests/unit/script/size_of.rs @@ -10,6 +10,7 @@ use script::dom::htmlelement::HTMLElement; use script::dom::htmlspanelement::HTMLSpanElement; use script::dom::node::Node; use script::dom::text::Text; +use script::layout_wrapper::ServoThreadSafeLayoutNode; use std::mem::size_of; // Macro so that we can stringify type names @@ -45,3 +46,4 @@ sizeof_checker!(size_div, HTMLDivElement, 328); sizeof_checker!(size_span, HTMLSpanElement, 328); sizeof_checker!(size_text, Text, 192); sizeof_checker!(size_characterdata, CharacterData, 192); +sizeof_checker!(size_servothreadsafelayoutnode, ServoThreadSafeLayoutNode, 16);