diff options
Diffstat (limited to 'components/layout')
-rw-r--r-- | components/layout/Cargo.toml | 4 | ||||
-rw-r--r-- | components/layout/animation.rs | 1 | ||||
-rw-r--r-- | components/layout/block.rs | 58 | ||||
-rw-r--r-- | components/layout/construct.rs | 40 | ||||
-rw-r--r-- | components/layout/context.rs | 96 | ||||
-rw-r--r-- | components/layout/display_list_builder.rs | 107 | ||||
-rw-r--r-- | components/layout/flex.rs | 8 | ||||
-rw-r--r-- | components/layout/flow.rs | 1 | ||||
-rw-r--r-- | components/layout/fragment.rs | 760 | ||||
-rw-r--r-- | components/layout/generated_content.rs | 1 | ||||
-rw-r--r-- | components/layout/incremental.rs | 1 | ||||
-rw-r--r-- | components/layout/inline.rs | 6 | ||||
-rw-r--r-- | components/layout/layout_debug.rs | 24 | ||||
-rw-r--r-- | components/layout/lib.rs | 2 | ||||
-rw-r--r-- | components/layout/list_item.rs | 4 | ||||
-rw-r--r-- | components/layout/model.rs | 101 | ||||
-rw-r--r-- | components/layout/multicol.rs | 10 | ||||
-rw-r--r-- | components/layout/parallel.rs | 45 | ||||
-rw-r--r-- | components/layout/query.rs | 104 | ||||
-rw-r--r-- | components/layout/sequential.rs | 11 | ||||
-rw-r--r-- | components/layout/traversal.rs | 131 | ||||
-rw-r--r-- | components/layout/webrender_helpers.rs | 32 |
22 files changed, 667 insertions, 880 deletions
diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml index 6443c4a6aa3..d3da21bf1a6 100644 --- a/components/layout/Cargo.toml +++ b/components/layout/Cargo.toml @@ -31,7 +31,7 @@ parking_lot = "0.3.3" plugins = {path = "../plugins"} profile_traits = {path = "../profile_traits"} range = {path = "../range"} -rayon = "0.5" +rayon = "0.6" script_layout_interface = {path = "../script_layout_interface"} script_traits = {path = "../script_traits"} selectors = "0.15" @@ -39,13 +39,13 @@ serde = "0.8" serde_derive = "0.8" serde_json = "0.8" servo_atoms = {path = "../atoms"} +servo_config = {path = "../config"} servo_url = {path = "../url"} smallvec = "0.1" style = {path = "../style"} style_traits = {path = "../style_traits"} unicode-bidi = "0.2" unicode-script = {version = "0.1", features = ["harfbuzz"]} -util = {path = "../util"} [dependencies.webrender_traits] git = "https://github.com/servo/webrender" diff --git a/components/layout/animation.rs b/components/layout/animation.rs index b14771e5de9..7fdc452ccc3 100644 --- a/components/layout/animation.rs +++ b/components/layout/animation.rs @@ -13,7 +13,6 @@ use script_traits::{AnimationState, ConstellationControlMsg, LayoutMsg as Conste use std::collections::HashMap; use std::sync::mpsc::Receiver; use style::animation::{Animation, update_style_for_animation}; -use style::dom::TRestyleDamage; use style::selector_parser::RestyleDamage; use style::timer::Timer; diff --git a/components/layout/block.rs b/components/layout/block.rs index 45de8786778..f7370e00a4e 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -42,12 +42,11 @@ use flow::IS_ABSOLUTELY_POSITIONED; use flow_list::FlowList; use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow}; use fragment::{IS_INLINE_FLEX_ITEM, IS_BLOCK_FLEX_ITEM}; -use fragment::SpecificFragmentInfo; use gfx::display_list::{ClippingRegion, StackingContext}; use gfx_traits::ScrollRootId; use gfx_traits::print_tree::PrintTree; use layout_debug; -use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto}; +use model::{AdjoiningMargins, CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto}; use model::{specified, specified_or_none}; use sequential; use serde::{Serialize, Serializer}; @@ -56,13 +55,12 @@ use std::fmt; use std::sync::Arc; use style::computed_values::{border_collapse, box_sizing, display, float, overflow_x, overflow_y}; use style::computed_values::{position, text_align}; -use style::context::{SharedStyleContext, StyleContext}; +use style::context::SharedStyleContext; use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode}; use style::properties::ServoComputedValues; use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPOSITION}; use style::values::computed::{LengthOrPercentageOrNone, LengthOrPercentage}; use style::values::computed::LengthOrPercentageOrAuto; -use util::clamp; /// Information specific to floated blocks. #[derive(Clone, Serialize)] @@ -555,7 +553,7 @@ impl BlockFlow { /// relevant margins for this Block. pub fn block_type(&self) -> BlockType { if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { - if self.is_replaced_content() { + if self.fragment.is_replaced() { BlockType::AbsoluteReplaced } else { BlockType::AbsoluteNonReplaced @@ -563,19 +561,19 @@ impl BlockFlow { } else if self.is_inline_flex_item() { BlockType::InlineFlexItem } else if self.base.flags.is_float() { - if self.is_replaced_content() { + if self.fragment.is_replaced() { BlockType::FloatReplaced } else { BlockType::FloatNonReplaced } } else if self.is_inline_block() { - if self.is_replaced_content() { + if self.fragment.is_replaced() { BlockType::InlineBlockReplaced } else { BlockType::InlineBlockNonReplaced } } else { - if self.is_replaced_content() { + if self.fragment.is_replaced() { BlockType::Replaced } else { BlockType::NonReplaced @@ -668,22 +666,6 @@ impl BlockFlow { } } - /// Return true if this has a replaced fragment. - /// - /// Text, Images, Inline Block and Canvas - /// (https://html.spec.whatwg.org/multipage/#replaced-elements) fragments are considered as - /// replaced fragments. - fn is_replaced_content(&self) -> bool { - match self.fragment.specific { - SpecificFragmentInfo::ScannedText(_) | - SpecificFragmentInfo::Svg(_) | - SpecificFragmentInfo::Image(_) | - SpecificFragmentInfo::Canvas(_) | - SpecificFragmentInfo::InlineBlock(_) => true, - _ => false, - } - } - /// Return shrink-to-fit inline-size. /// /// This is where we use the preferred inline-sizes and minimum inline-sizes @@ -1267,11 +1249,11 @@ impl BlockFlow { let available_block_size = containing_block_block_size - self.fragment.border_padding.block_start_end(); - if self.is_replaced_content() { + if self.fragment.is_replaced() { // Calculate used value of block-size just like we do for inline replaced elements. // TODO: Pass in the containing block block-size when Fragment's // assign-block-size can handle it correctly. - self.fragment.assign_replaced_block_size_if_necessary(Some(containing_block_block_size)); + self.fragment.assign_replaced_block_size_if_necessary(); // TODO: Right now, this content block-size value includes the // margin because of erroneous block-size calculation in fragment. // Check this when that has been fixed. @@ -1551,7 +1533,7 @@ impl BlockFlow { size + self.fragment.border_padding.inline_start_end(), } } else { - clamp(min_inline_size, available_inline_size, max_inline_size) + max(min_inline_size, min(available_inline_size, max_inline_size)) }; self.base.position.size.inline = inline_size + self.fragment.margin.inline_start_end(); @@ -1896,16 +1878,22 @@ impl Flow for BlockFlow { fn fragment(&mut self, layout_context: &LayoutContext, fragmentation_context: Option<FragmentationContext>) -> Option<Arc<Flow>> { - if self.is_replaced_content() { + if self.fragment.is_replaced() { let _scope = layout_debug_scope!("assign_replaced_block_size_if_necessary {:x}", self.base.debug_id()); // Assign block-size for fragment if it is an image fragment. - let containing_block_block_size = - self.base.block_container_explicit_block_size; - self.fragment.assign_replaced_block_size_if_necessary(containing_block_block_size); + self.fragment.assign_replaced_block_size_if_necessary(); if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { self.base.position.size.block = self.fragment.border_box.size.block; + let mut block_start = AdjoiningMargins::from_margin(self.fragment.margin.block_start); + let block_end = AdjoiningMargins::from_margin(self.fragment.margin.block_end); + if self.fragment.border_box.size.block == Au(0) { + block_start.union(block_end); + self.base.collapsible_margins = CollapsibleMargins::CollapseThrough(block_start); + } else { + self.base.collapsible_margins = CollapsibleMargins::Collapse(block_start, block_end); + } self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW); self.fragment.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW); } @@ -2870,7 +2858,7 @@ impl ISizeAndMarginsComputer for AbsoluteReplaced { fragment.assign_replaced_inline_size_if_necessary(containing_block_inline_size, container_block_size); // For replaced absolute flow, the rest of the constraint solving will // take inline-size to be specified as the value computed here. - MaybeAuto::Specified(fragment.content_inline_size()) + MaybeAuto::Specified(fragment.content_box().size.inline) } fn containing_block_inline_size(&self, @@ -2929,7 +2917,7 @@ impl ISizeAndMarginsComputer for BlockReplaced { fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size, container_block_size); // For replaced block flow, the rest of the constraint solving will // take inline-size to be specified as the value computed here. - MaybeAuto::Specified(fragment.content_inline_size()) + MaybeAuto::Specified(fragment.content_box().size.inline) } } @@ -2987,7 +2975,7 @@ impl ISizeAndMarginsComputer for FloatReplaced { fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size, container_block_size); // For replaced block flow, the rest of the constraint solving will // take inline-size to be specified as the value computed here. - MaybeAuto::Specified(fragment.content_inline_size()) + MaybeAuto::Specified(fragment.content_box().size.inline) } } @@ -3075,7 +3063,7 @@ impl ISizeAndMarginsComputer for InlineBlockReplaced { fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size, container_block_size); // For replaced block flow, the rest of the constraint solving will // take inline-size to be specified as the value computed here. - MaybeAuto::Specified(fragment.content_inline_size()) + MaybeAuto::Specified(fragment.content_box().size.inline) } } diff --git a/components/layout/construct.rs b/components/layout/construct.rs index f3309b97b75..660d4f64834 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -38,6 +38,7 @@ use multicol::{MulticolColumnFlow, MulticolFlow}; use parallel; use script_layout_interface::{LayoutElementType, LayoutNodeType, is_image_data}; use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; +use servo_config::opts; use servo_url::ServoUrl; use std::borrow::ToOwned; use std::collections::LinkedList; @@ -64,7 +65,6 @@ use table_rowgroup::TableRowGroupFlow; use table_wrapper::TableWrapperFlow; use text::TextRunScanner; use traversal::PostorderNodeMutTraversal; -use util::opts; use wrapper::{LayoutNodeLayoutData, TextContent, ThreadSafeLayoutNodeHelpers}; /// The results of flow construction for a DOM node. @@ -141,17 +141,22 @@ pub struct InlineFragmentsConstructionResult { /// The resulting `ConstructionItem` for the outer `span` will be: /// /// ```ignore -/// ConstructionItem::InlineFragments(Some(~[ -/// InlineBlockSplit { -/// predecessor_fragments: ~[ -/// A +/// ConstructionItem::InlineFragments( +/// InlineFragmentsConstructionResult{ +/// splits: linked_list![ +/// InlineBlockSplit{ +/// predecessors: IntermediateInlineFragments{ +/// fragments: linked_list![A], +/// absolute_descendents: AbsoluteDescendents{ +/// descendant_links: vec![] +/// } +/// }, +/// flow: B +/// } /// ], -/// block: ~BlockFlow { -/// B -/// }, -/// }),~[ -/// C -/// ]) +/// fragments: linked_list![C], +/// } +/// ) /// ``` #[derive(Clone)] pub struct InlineBlockSplit { @@ -346,14 +351,12 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> SpecificFragmentInfo::Iframe(IframeFragmentInfo::new(node)) } Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) => { - let image_info = box ImageFragmentInfo::new(node, - node.image_url(), + let image_info = box ImageFragmentInfo::new(node.image_url(), &self.layout_context.shared); SpecificFragmentInfo::Image(image_info) } Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => { - let image_info = box ImageFragmentInfo::new(node, - node.object_data(), + let image_info = box ImageFragmentInfo::new(node.object_data(), &self.layout_context.shared); SpecificFragmentInfo::Image(image_info) } @@ -372,11 +375,11 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> } Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) => { let data = node.canvas_data().unwrap(); - SpecificFragmentInfo::Canvas(box CanvasFragmentInfo::new(node, data, self.style_context())) + SpecificFragmentInfo::Canvas(box CanvasFragmentInfo::new(data)) } Some(LayoutNodeType::Element(LayoutElementType::SVGSVGElement)) => { let data = node.svg_data().unwrap(); - SpecificFragmentInfo::Svg(box SvgFragmentInfo::new(node, data, self.style_context())) + SpecificFragmentInfo::Svg(box SvgFragmentInfo::new(data)) } _ => { // This includes pseudo-elements. @@ -1207,8 +1210,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> let flotation = FloatKind::from_property(flotation); let marker_fragments = match node.style(self.style_context()).get_list().list_style_image { Either::First(ref url_value) => { - let image_info = box ImageFragmentInfo::new(node, - url_value.url().map(|u| u.clone()), + let image_info = box ImageFragmentInfo::new(url_value.url().map(|u| u.clone()), &self.layout_context.shared); vec![Fragment::new(node, SpecificFragmentInfo::Image(image_info), self.layout_context)] } diff --git a/components/layout/context.rs b/components/layout/context.rs index cb5b9efb0ef..e2d1fa7831c 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -17,57 +17,74 @@ use net_traits::image::base::Image; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread, ImageResponse, ImageState}; use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder}; use parking_lot::RwLock; +use servo_config::opts; use servo_url::ServoUrl; use std::cell::{RefCell, RefMut}; use std::collections::HashMap; use std::hash::BuildHasherDefault; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use style::context::{LocalStyleContext, StyleContext, SharedStyleContext}; -use util::opts; +use style::context::{SharedStyleContext, ThreadLocalStyleContext}; +use style::dom::TElement; -struct LocalLayoutContext { - style_context: LocalStyleContext, +/// TLS data scoped to the traversal. +pub struct ScopedThreadLocalLayoutContext<E: TElement> { + pub style_context: ThreadLocalStyleContext<E>, +} + +impl<E: TElement> ScopedThreadLocalLayoutContext<E> { + pub fn new(shared: &SharedLayoutContext) -> Self { + ScopedThreadLocalLayoutContext { + style_context: ThreadLocalStyleContext::new(&shared.style_context), + } + } +} + +/// TLS data that persists across traversals. +pub struct PersistentThreadLocalLayoutContext { + // FontContext uses Rc all over the place and so isn't Send, which means we + // can't use ScopedTLS for it. There's also no reason to scope it to the + // traversal, and performance is probably better if we don't. + pub font_context: RefCell<FontContext>, +} - font_context: RefCell<FontContext>, +impl PersistentThreadLocalLayoutContext { + pub fn new(shared: &SharedLayoutContext) -> Rc<Self> { + let font_cache_thread = shared.font_cache_thread.lock().unwrap().clone(); + Rc::new(PersistentThreadLocalLayoutContext { + font_context: RefCell::new(FontContext::new(font_cache_thread)), + }) + } } -impl HeapSizeOf for LocalLayoutContext { - // FIXME(njn): measure other fields eventually. +impl HeapSizeOf for PersistentThreadLocalLayoutContext { fn heap_size_of_children(&self) -> usize { self.font_context.heap_size_of_children() } } -thread_local!(static LOCAL_CONTEXT_KEY: RefCell<Option<Rc<LocalLayoutContext>>> = RefCell::new(None)); +thread_local!(static LOCAL_CONTEXT_KEY: RefCell<Option<Rc<PersistentThreadLocalLayoutContext>>> = RefCell::new(None)); -pub fn heap_size_of_local_context() -> usize { - LOCAL_CONTEXT_KEY.with(|r| { - r.borrow().clone().map_or(0, |context| context.heap_size_of_children()) - }) -} - -// Keep this implementation in sync with the one in ports/geckolib/traversal.rs. -fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext) - -> Rc<LocalLayoutContext> { +fn create_or_get_persistent_context(shared: &SharedLayoutContext) + -> Rc<PersistentThreadLocalLayoutContext> { LOCAL_CONTEXT_KEY.with(|r| { let mut r = r.borrow_mut(); if let Some(context) = r.clone() { context } else { - let font_cache_thread = shared_layout_context.font_cache_thread.lock().unwrap().clone(); - let local_style_data = shared_layout_context.style_context.local_context_creation_data.lock().unwrap(); - - let context = Rc::new(LocalLayoutContext { - style_context: LocalStyleContext::new(&local_style_data), - font_context: RefCell::new(FontContext::new(font_cache_thread)), - }); + let context = PersistentThreadLocalLayoutContext::new(shared); *r = Some(context.clone()); context } }) } +pub fn heap_size_of_persistent_local_context() -> usize { + LOCAL_CONTEXT_KEY.with(|r| { + r.borrow().clone().map_or(0, |context| context.heap_size_of_children()) + }) +} + /// Layout information shared among all workers. This must be thread-safe. pub struct SharedLayoutContext { /// Bits shared by the layout and style system. @@ -90,27 +107,26 @@ pub struct SharedLayoutContext { pub struct LayoutContext<'a> { pub shared: &'a SharedLayoutContext, - cached_local_layout_context: Rc<LocalLayoutContext>, + pub persistent: Rc<PersistentThreadLocalLayoutContext>, } -impl<'a> StyleContext<'a> for LayoutContext<'a> { - fn shared_context(&self) -> &'a SharedStyleContext { - &self.shared.style_context - } - - fn local_context(&self) -> &LocalStyleContext { - &self.cached_local_layout_context.style_context +impl<'a> LayoutContext<'a> { + pub fn new(shared: &'a SharedLayoutContext) -> Self + { + LayoutContext { + shared: shared, + persistent: create_or_get_persistent_context(shared), + } } } impl<'a> LayoutContext<'a> { - pub fn new(shared_layout_context: &'a SharedLayoutContext) -> LayoutContext<'a> { - let local_context = create_or_get_local_context(shared_layout_context); - - LayoutContext { - shared: shared_layout_context, - cached_local_layout_context: local_context, - } + // FIXME(bholley): The following two methods are identical and should be merged. + // shared_context() is the appropriate name, but it involves renaming a lot of + // calls. + #[inline(always)] + pub fn shared_context(&self) -> &SharedStyleContext { + &self.shared.style_context } #[inline(always)] @@ -120,7 +136,7 @@ impl<'a> LayoutContext<'a> { #[inline(always)] pub fn font_context(&self) -> RefMut<FontContext> { - self.cached_local_layout_context.font_context.borrow_mut() + self.persistent.font_context.borrow_mut() } } diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 0c34a147655..be11ba66edd 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -35,6 +35,7 @@ use model::{self, MaybeAuto, ToGfxMatrix}; use net_traits::image::base::PixelFormat; use net_traits::image_cache_thread::UsePlaceholder; use range::Range; +use servo_config::opts; use servo_url::ServoUrl; use std::{cmp, f32}; use std::collections::HashMap; @@ -53,11 +54,10 @@ use style::properties::{self, ServoComputedValues}; use style::properties::style_structs; use style::servo::restyle_damage::REPAINT; use style::values::{self, Either, RGBA, computed}; -use style::values::computed::{Gradient, GradientKind, LengthOrPercentage, LengthOrPercentageOrAuto}; -use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection}; +use style::values::computed::{AngleOrCorner, Gradient, GradientKind, LengthOrPercentage, LengthOrPercentageOrAuto}; +use style::values::specified::{HorizontalDirection, VerticalDirection}; use style_traits::cursor::Cursor; use table_cell::CollapsedBordersForCell; -use util::opts; use webrender_traits::{ColorF, GradientStop}; trait RgbColor { @@ -755,11 +755,12 @@ impl FragmentDisplayListBuilding for Fragment { } }; - let position = *get_cyclic(&background.background_position.0, index); + let horiz_position = *get_cyclic(&background.background_position_x.0, index); + let vert_position = *get_cyclic(&background.background_position_y.0, index); // Use `background-position` to get the offset. - let horizontal_position = model::specified(position.horizontal, + let horizontal_position = model::specified(horiz_position.0, bounds.size.width - image_size.width); - let vertical_position = model::specified(position.vertical, + let vertical_position = model::specified(vert_position.0, bounds.size.height - image_size.height); // The anchor position for this background, based on both the background-attachment @@ -1489,57 +1490,51 @@ impl FragmentDisplayListBuilding for Fragment { } } SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => { - let width = canvas_fragment_info.replaced_image_fragment_info - .computed_inline_size.map_or(0, |w| w.to_px() as usize); - let height = canvas_fragment_info.replaced_image_fragment_info - .computed_block_size.map_or(0, |h| h.to_px() as usize); - if width > 0 && height > 0 { - let computed_width = canvas_fragment_info.canvas_inline_size().to_px(); - let computed_height = canvas_fragment_info.canvas_block_size().to_px(); - - let canvas_data = match canvas_fragment_info.ipc_renderer { - Some(ref ipc_renderer) => { - let ipc_renderer = ipc_renderer.lock().unwrap(); - let (sender, receiver) = ipc::channel().unwrap(); - ipc_renderer.send(CanvasMsg::FromLayout( - FromLayoutMsg::SendData(sender))).unwrap(); - receiver.recv().unwrap() - }, - None => return, - }; + let computed_width = canvas_fragment_info.dom_width.to_px(); + let computed_height = canvas_fragment_info.dom_height.to_px(); + + let canvas_data = match canvas_fragment_info.ipc_renderer { + Some(ref ipc_renderer) => { + let ipc_renderer = ipc_renderer.lock().unwrap(); + let (sender, receiver) = ipc::channel().unwrap(); + ipc_renderer.send(CanvasMsg::FromLayout( + FromLayoutMsg::SendData(sender))).unwrap(); + receiver.recv().unwrap() + }, + None => return, + }; + + let base = state.create_base_display_item( + &stacking_relative_content_box, + clip, + self.node, + self.style.get_cursor(Cursor::Default), + DisplayListSection::Content); + let display_item = match canvas_data { + CanvasData::Image(canvas_data) => { + DisplayItem::Image(box ImageDisplayItem { + base: base, + webrender_image: WebRenderImageInfo { + width: computed_width as u32, + height: computed_height as u32, + format: PixelFormat::RGBA8, + key: Some(canvas_data.image_key), + }, + image_data: None, + stretch_size: stacking_relative_content_box.size, + tile_spacing: Size2D::zero(), + image_rendering: image_rendering::T::auto, + }) + } + CanvasData::WebGL(context_id) => { + DisplayItem::WebGL(box WebGLDisplayItem { + base: base, + context_id: context_id, + }) + } + }; - let base = state.create_base_display_item( - &stacking_relative_content_box, - clip, - self.node, - self.style.get_cursor(Cursor::Default), - DisplayListSection::Content); - let display_item = match canvas_data { - CanvasData::Image(canvas_data) => { - DisplayItem::Image(box ImageDisplayItem { - base: base, - webrender_image: WebRenderImageInfo { - width: computed_width as u32, - height: computed_height as u32, - format: PixelFormat::RGBA8, - key: Some(canvas_data.image_key), - }, - image_data: None, - stretch_size: stacking_relative_content_box.size, - tile_spacing: Size2D::zero(), - image_rendering: image_rendering::T::auto, - }) - } - CanvasData::WebGL(context_id) => { - DisplayItem::WebGL(box WebGLDisplayItem { - base: base, - context_id: context_id, - }) - } - }; - - state.add_display_item(display_item); - } + state.add_display_item(display_item); } SpecificFragmentInfo::UnscannedText(_) => { panic!("Shouldn't see unscanned fragments here.") diff --git a/components/layout/flex.rs b/components/layout/flex.rs index d84373a7c21..7b8d694494c 100644 --- a/components/layout/flex.rs +++ b/components/layout/flex.rs @@ -19,14 +19,14 @@ use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; use gfx::display_list::StackingContext; use gfx_traits::ScrollRootId; use layout_debug; -use model::{IntrinsicISizes, MaybeAuto, MinMaxConstraint}; +use model::{IntrinsicISizes, MaybeAuto, SizeConstraint}; use model::{specified, specified_or_none}; use std::cmp::{max, min}; use std::ops::Range; use std::sync::Arc; use style::computed_values::{align_content, align_self, flex_direction, flex_wrap, justify_content}; use style::computed_values::border_collapse; -use style::context::{SharedStyleContext, StyleContext}; +use style::context::SharedStyleContext; use style::logical_geometry::{Direction, LogicalSize}; use style::properties::ServoComputedValues; use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW}; @@ -38,7 +38,7 @@ use style::values::computed::{LengthOrPercentageOrAutoOrContent, LengthOrPercent #[derive(Debug)] enum AxisSize { Definite(Au), - MinMax(MinMaxConstraint), + MinMax(SizeConstraint), Infinite, } @@ -62,7 +62,7 @@ impl AxisSize { } } LengthOrPercentageOrAuto::Auto => { - AxisSize::MinMax(MinMaxConstraint::new(content_size, min, max)) + AxisSize::MinMax(SizeConstraint::new(content_size, min, max, None)) } } } diff --git a/components/layout/flow.rs b/components/layout/flow.rs index df7a6510ff2..34fcd3bf300 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -50,7 +50,6 @@ use std::sync::Arc; use std::sync::atomic::Ordering; use style::computed_values::{clear, float, overflow_x, position, text_align}; use style::context::SharedStyleContext; -use style::dom::TRestyleDamage; use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; use style::properties::ServoComputedValues; use style::selector_parser::RestyleDamage; diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 104c45fd918..e5d86e78ed5 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -23,7 +23,8 @@ use inline::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT, LineMetrics}; use ipc_channel::ipc::IpcSender; #[cfg(debug_assertions)] use layout_debug; -use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto}; +use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeConstraint}; +use model::style_length; use msg::constellation_msg::PipelineId; use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder}; @@ -34,7 +35,7 @@ use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayou use serde::{Serialize, Serializer}; use servo_url::ServoUrl; use std::borrow::ToOwned; -use std::cmp::{max, min}; +use std::cmp::{Ordering, max, min}; use std::collections::LinkedList; use std::fmt; use std::sync::{Arc, Mutex}; @@ -43,8 +44,6 @@ use style::computed_values::{border_collapse, box_sizing, clear, color, display, use style::computed_values::{overflow_wrap, overflow_x, position, text_decoration}; use style::computed_values::{transform_style, vertical_align, white_space, word_break, z_index}; use style::computed_values::content::ContentItem; -use style::context::SharedStyleContext; -use style::dom::TRestyleDamage; use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode}; use style::properties::ServoComputedValues; use style::selector_parser::RestyleDamage; @@ -52,7 +51,6 @@ use style::servo::restyle_damage::RECONSTRUCT_FLOW; use style::str::char_is_whitespace; use style::values::Either; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; -use style::values::computed::LengthOrPercentageOrNone; use text; use text::TextRunScanner; @@ -60,6 +58,10 @@ use text::TextRunScanner; static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20; static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34; +// https://drafts.csswg.org/css-images/#default-object-size +static DEFAULT_REPLACED_WIDTH: i32 = 300; +static DEFAULT_REPLACED_HEIGHT: i32 = 150; + /// 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 /// specification: @@ -248,21 +250,6 @@ impl fmt::Debug for SpecificFragmentInfo { } } -/// Clamp a value obtained from style_length, based on min / max lengths. -fn clamp_size(size: Au, - min_size: LengthOrPercentage, - max_size: LengthOrPercentageOrNone, - container_size: Au) - -> Au { - let min_size = model::specified(min_size, container_size); - let max_size = model::specified_or_none(max_size, container_size); - - max(min_size, match max_size { - None => size, - Some(max_size) => min(size, max_size), - }) -} - /// Information for generated content. #[derive(Clone)] pub enum GeneratedContentInfo { @@ -327,89 +314,41 @@ impl InlineAbsoluteFragmentInfo { #[derive(Clone)] pub struct CanvasFragmentInfo { - pub replaced_image_fragment_info: ReplacedImageFragmentInfo, pub ipc_renderer: Option<Arc<Mutex<IpcSender<CanvasMsg>>>>, pub dom_width: Au, pub dom_height: Au, } impl CanvasFragmentInfo { - pub fn new<N: ThreadSafeLayoutNode>(node: &N, - data: HTMLCanvasData, - ctx: &SharedStyleContext) - -> CanvasFragmentInfo { + pub fn new(data: HTMLCanvasData) -> CanvasFragmentInfo { CanvasFragmentInfo { - replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, ctx), ipc_renderer: data.ipc_renderer .map(|renderer| Arc::new(Mutex::new(renderer))), dom_width: Au::from_px(data.width as i32), dom_height: Au::from_px(data.height as i32), } } - - /// Returns the original inline-size of the canvas. - pub fn canvas_inline_size(&self) -> Au { - if self.replaced_image_fragment_info.writing_mode_is_vertical { - self.dom_height - } else { - self.dom_width - } - } - - /// Returns the original block-size of the canvas. - pub fn canvas_block_size(&self) -> Au { - if self.replaced_image_fragment_info.writing_mode_is_vertical { - self.dom_width - } else { - self.dom_height - } - } } #[derive(Clone)] pub struct SvgFragmentInfo { - pub replaced_image_fragment_info: ReplacedImageFragmentInfo, pub dom_width: Au, pub dom_height: Au, } impl SvgFragmentInfo { - pub fn new<N: ThreadSafeLayoutNode>(node: &N, - data: SVGSVGData, - ctx: &SharedStyleContext) - -> SvgFragmentInfo { + pub fn new(data: SVGSVGData) -> SvgFragmentInfo { SvgFragmentInfo { - replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, ctx), dom_width: Au::from_px(data.width as i32), dom_height: Au::from_px(data.height as i32), } } - - /// Returns the original inline-size of the SVG element. - pub fn svg_inline_size(&self) -> Au { - if self.replaced_image_fragment_info.writing_mode_is_vertical { - self.dom_height - } else { - self.dom_width - } - } - - /// Returns the original block-size of the SVG element. - pub fn svg_block_size(&self) -> Au { - if self.replaced_image_fragment_info.writing_mode_is_vertical { - self.dom_width - } else { - self.dom_height - } - } } /// A fragment that represents a replaced content image and its accompanying borders, shadows, etc. #[derive(Clone)] pub struct ImageFragmentInfo { - /// The image held within this fragment. - pub replaced_image_fragment_info: ReplacedImageFragmentInfo, pub image: Option<Arc<Image>>, pub metadata: Option<ImageMetadata>, } @@ -419,9 +358,9 @@ impl ImageFragmentInfo { /// /// FIXME(pcwalton): The fact that image fragments store the cache in the fragment makes little /// sense to me. - pub fn new<N: ThreadSafeLayoutNode>(node: &N, url: Option<ServoUrl>, - shared_layout_context: &SharedLayoutContext) - -> ImageFragmentInfo { + pub fn new(url: Option<ServoUrl>, + shared_layout_context: &SharedLayoutContext) + -> ImageFragmentInfo { let image_or_metadata = url.and_then(|url| { shared_layout_context.get_or_request_image_or_meta(url, UsePlaceholder::Yes) }); @@ -439,40 +378,11 @@ impl ImageFragmentInfo { }; ImageFragmentInfo { - replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, &shared_layout_context.style_context), image: image, metadata: metadata, } } - /// Returns the original inline-size of the image. - pub fn image_inline_size(&mut self) -> Au { - match self.metadata { - Some(ref metadata) => { - Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical { - metadata.height - } else { - metadata.width - } as i32) - } - None => Au(0) - } - } - - /// Returns the original block-size of the image. - pub fn image_block_size(&mut self) -> Au { - match self.metadata { - Some(ref metadata) => { - Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical { - metadata.width - } else { - metadata.height - } as i32) - } - None => Au(0) - } - } - pub fn tile_image_round(position: &mut Au, size: &mut Au, absolute_anchor_origin: Au, @@ -545,149 +455,6 @@ impl ImageFragmentInfo { } } -#[derive(Clone)] -pub struct ReplacedImageFragmentInfo { - pub computed_inline_size: Option<Au>, - pub computed_block_size: Option<Au>, - pub writing_mode_is_vertical: bool, -} - -impl ReplacedImageFragmentInfo { - pub fn new<N>(node: &N, ctx: &SharedStyleContext) -> ReplacedImageFragmentInfo - where N: ThreadSafeLayoutNode { - let is_vertical = node.style(ctx).writing_mode.is_vertical(); - ReplacedImageFragmentInfo { - computed_inline_size: None, - computed_block_size: None, - writing_mode_is_vertical: is_vertical, - } - } - - /// Returns the calculated inline-size of the image, accounting for the inline-size attribute. - pub fn computed_inline_size(&self) -> Au { - self.computed_inline_size.expect("image inline_size is not computed yet!") - } - - /// Returns the calculated block-size of the image, accounting for the block-size attribute. - pub fn computed_block_size(&self) -> Au { - self.computed_block_size.expect("image block_size is not computed yet!") - } - - // Return used value for inline-size or block-size. - // - // `dom_length`: inline-size or block-size as specified in the `img` tag. - // `style_length`: inline-size as given in the CSS - pub fn style_length(style_length: LengthOrPercentageOrAuto, - container_size: Option<Au>) -> MaybeAuto { - match (style_length, container_size) { - (LengthOrPercentageOrAuto::Length(length), _) => MaybeAuto::Specified(length), - (LengthOrPercentageOrAuto::Percentage(pc), Some(container_size)) => { - MaybeAuto::Specified(container_size.scale_by(pc)) - } - (LengthOrPercentageOrAuto::Percentage(_), None) => MaybeAuto::Auto, - (LengthOrPercentageOrAuto::Calc(calc), Some(container_size)) => { - MaybeAuto::Specified(calc.length() + container_size.scale_by(calc.percentage())) - } - (LengthOrPercentageOrAuto::Calc(_), None) => MaybeAuto::Auto, - (LengthOrPercentageOrAuto::Auto, _) => MaybeAuto::Auto, - } - } - - pub fn calculate_replaced_inline_size(&mut self, - style: &ServoComputedValues, - noncontent_inline_size: Au, - container_inline_size: Au, - container_block_size: Option<Au>, - fragment_inline_size: Au, - fragment_block_size: Au) - -> Au { - let style_inline_size = style.content_inline_size(); - let style_block_size = style.content_block_size(); - let style_min_inline_size = style.min_inline_size(); - let style_max_inline_size = style.max_inline_size(); - let style_min_block_size = style.min_block_size(); - let style_max_block_size = style.max_block_size(); - - // TODO(ksh8281): compute border,margin - let inline_size = ReplacedImageFragmentInfo::style_length( - style_inline_size, - Some(container_inline_size)); - - let inline_size = match inline_size { - MaybeAuto::Auto => { - let intrinsic_width = fragment_inline_size; - let intrinsic_height = fragment_block_size; - if intrinsic_height == Au(0) { - intrinsic_width - } else { - let ratio = intrinsic_width.to_f32_px() / - intrinsic_height.to_f32_px(); - - let specified_height = ReplacedImageFragmentInfo::style_length( - style_block_size, - container_block_size); - let specified_height = match specified_height { - MaybeAuto::Auto => intrinsic_height, - MaybeAuto::Specified(h) => h, - }; - let specified_height = clamp_size(specified_height, - style_min_block_size, - style_max_block_size, - Au(0)); - Au::from_f32_px(specified_height.to_f32_px() * ratio) - } - }, - MaybeAuto::Specified(w) => w, - }; - - let inline_size = clamp_size(inline_size, - style_min_inline_size, - style_max_inline_size, - container_inline_size); - - self.computed_inline_size = Some(inline_size); - inline_size + noncontent_inline_size - } - - /// Here, `noncontent_block_size` represents the sum of border and padding, but not margin. - pub fn calculate_replaced_block_size(&mut self, - style: &ServoComputedValues, - noncontent_block_size: Au, - containing_block_block_size: Option<Au>, - fragment_inline_size: Au, - fragment_block_size: Au) - -> Au { - let style_block_size = style.content_block_size(); - let style_min_block_size = style.min_block_size(); - let style_max_block_size = style.max_block_size(); - - let inline_size = self.computed_inline_size(); - let block_size = ReplacedImageFragmentInfo::style_length( - style_block_size, - containing_block_block_size); - - let block_size = match block_size { - MaybeAuto::Auto => { - let intrinsic_width = fragment_inline_size; - let intrinsic_height = fragment_block_size; - let scale = intrinsic_width.to_f32_px() / inline_size.to_f32_px(); - Au::from_f32_px(intrinsic_height.to_f32_px() / scale) - }, - MaybeAuto::Specified(h) => { - h - } - }; - - let block_size = clamp_size(block_size, - style_min_block_size, - style_max_block_size, - Au(0)); - - self.computed_block_size = Some(block_size); - block_size + noncontent_block_size - } -} - /// A fragment that represents an inline frame (iframe). This stores the pipeline ID so that the /// size of this iframe can be communicated via the constellation to the iframe's own layout thread. #[derive(Clone)] @@ -704,52 +471,6 @@ impl IframeFragmentInfo { pipeline_id: pipeline_id, } } - - #[inline] - pub fn calculate_replaced_inline_size(&self, style: &ServoComputedValues, containing_size: Au) - -> Au { - // Calculate the replaced inline size (or default) as per CSS 2.1 § 10.3.2 - IframeFragmentInfo::calculate_replaced_size(style.content_inline_size(), - style.min_inline_size(), - style.max_inline_size(), - Some(containing_size), - Au::from_px(300)) - } - - #[inline] - pub fn calculate_replaced_block_size(&self, style: &ServoComputedValues, containing_size: Option<Au>) - -> Au { - // Calculate the replaced block size (or default) as per CSS 2.1 § 10.3.2 - IframeFragmentInfo::calculate_replaced_size(style.content_block_size(), - style.min_block_size(), - style.max_block_size(), - containing_size, - Au::from_px(150)) - - } - - fn calculate_replaced_size(content_size: LengthOrPercentageOrAuto, - style_min_size: LengthOrPercentage, - style_max_size: LengthOrPercentageOrNone, - containing_size: Option<Au>, - default_size: Au) -> Au { - let computed_size = match (content_size, containing_size) { - (LengthOrPercentageOrAuto::Length(length), _) => length, - (LengthOrPercentageOrAuto::Percentage(pc), Some(container_size)) => container_size.scale_by(pc), - (LengthOrPercentageOrAuto::Calc(calc), Some(container_size)) => { - container_size.scale_by(calc.percentage()) + calc.length() - }, - (LengthOrPercentageOrAuto::Calc(calc), None) => calc.length(), - (LengthOrPercentageOrAuto::Percentage(_), None) => default_size, - (LengthOrPercentageOrAuto::Auto, _) => default_size, - }; - - let containing_size = containing_size.unwrap_or(Au(0)); - clamp_size(computed_size, - style_min_size, - style_max_size, - containing_size) - } } /// A scanned text fragment represents a single run of text with a distinct style. A `TextFragment` @@ -1203,6 +924,198 @@ impl Fragment { } } + /// intrinsic width of this replaced element. + #[inline] + pub fn intrinsic_width(&self) -> Au { + match self.specific { + SpecificFragmentInfo::Image(ref info) => { + if let Some(ref data) = info.metadata { + Au::from_px(data.width as i32) + } else { + Au(0) + } + } + SpecificFragmentInfo::Canvas(ref info) => info.dom_width, + SpecificFragmentInfo::Svg(ref info) => info.dom_width, + // Note: Currently for replaced element with no intrinsic size, + // this function simply returns the default object size. As long as + // these elements do not have intrinsic aspect ratio this should be + // sufficient, but we may need to investigate if this is enough for + // use cases like SVG. + SpecificFragmentInfo::Iframe(_) => Au::from_px(DEFAULT_REPLACED_WIDTH), + _ => panic!("Trying to get intrinsic width on non-replaced element!") + } + } + + /// intrinsic width of this replaced element. + #[inline] + pub fn intrinsic_height(&self) -> Au { + match self.specific { + SpecificFragmentInfo::Image(ref info) => { + if let Some(ref data) = info.metadata { + Au::from_px(data.height as i32) + } else { + Au(0) + } + } + SpecificFragmentInfo::Canvas(ref info) => info.dom_height, + SpecificFragmentInfo::Svg(ref info) => info.dom_height, + SpecificFragmentInfo::Iframe(_) => Au::from_px(DEFAULT_REPLACED_HEIGHT), + _ => panic!("Trying to get intrinsic height on non-replaced element!") + } + } + + /// Whether this replace element has intrinsic aspect ratio. + pub fn has_intrinsic_ratio(&self) -> bool { + match self.specific { + SpecificFragmentInfo::Image(_) | + SpecificFragmentInfo::Canvas(_) | + // TODO(stshine): According to the SVG spec, whether a SVG element has intrinsic + // aspect ratio is determined by the `preserveAspectRatio` attribute. Since for + // now SVG is far from implemented, we simply choose the default behavior that + // the intrinsic aspect ratio is preserved. + // https://svgwg.org/svg2-draft/coords.html#PreserveAspectRatioAttribute + SpecificFragmentInfo::Svg(_) => + self.intrinsic_width() != Au(0) && self.intrinsic_height() != Au(0), + _ => false + } + } + + /// CSS 2.1 § 10.3.2 & 10.6.2 Calculate the used width and height of a replaced element. + /// When a parameter is `None` it means the specified size in certain direction + /// is unconstrained. The inline containing size can also be `None` since this + /// method is also used for calculating intrinsic inline size contribution. + pub fn calculate_replaced_sizes(&self, + containing_inline_size: Option<Au>, + containing_block_size: Option<Au>) + -> (Au, Au) { + let (intrinsic_inline_size, intrinsic_block_size) = if self.style.writing_mode.is_vertical() { + (self.intrinsic_height(), self.intrinsic_width()) + } else { + (self.intrinsic_width(), self.intrinsic_height()) + }; + + // Make sure the size we used here is for content box since they may be + // transferred by the intrinsic aspect ratio. + let inline_size = style_length(self.style.content_inline_size(), containing_inline_size) + .map(|x| x - self.box_sizing_boundary(Direction::Inline)); + let block_size = style_length(self.style.content_block_size(), containing_block_size) + .map(|x| x - self.box_sizing_boundary(Direction::Block)); + let inline_constraint = self.size_constraint(containing_inline_size, Direction::Inline); + let block_constraint = self.size_constraint(containing_block_size, Direction::Block); + + // https://drafts.csswg.org/css-images-3/#default-sizing + match (inline_size, block_size) { + // If the specified size is a definite width and height, the concrete + // object size is given that width and height. + (MaybeAuto::Specified(inline_size), MaybeAuto::Specified(block_size)) => + (inline_constraint.clamp(inline_size), block_constraint.clamp(block_size)), + + // If the specified size is only a width or height (but not both) + // then the concrete object size is given that specified width or + // height. The other dimension is calculated as follows: + // + // If the object has an intrinsic aspect ratio, the missing dimension + // of the concrete object size is calculated using the intrinsic + // aspect ratio and the present dimension. + // + // Otherwise, if the missing dimension is present in the object’s intrinsic + // dimensions, the missing dimension is taken from the object’s intrinsic + // dimensions. Otherwise it is taken from the default object size. + (MaybeAuto::Specified(inline_size), MaybeAuto::Auto) => { + let inline_size = inline_constraint.clamp(inline_size); + let block_size = if self.has_intrinsic_ratio() { + // Note: We can not precompute the ratio and store it as a float, because + // doing so may result one pixel difference in calculation for certain + // images, thus make some tests fail. + inline_size * intrinsic_block_size.0 / intrinsic_inline_size.0 + } else { + intrinsic_block_size + }; + (inline_size, block_constraint.clamp(block_size)) + } + (MaybeAuto::Auto, MaybeAuto::Specified(block_size)) => { + let block_size = block_constraint.clamp(block_size); + let inline_size = if self.has_intrinsic_ratio() { + block_size * intrinsic_inline_size.0 / intrinsic_block_size.0 + } else { + intrinsic_inline_size + }; + (inline_constraint.clamp(inline_size), block_size) + } + // https://drafts.csswg.org/css2/visudet.html#min-max-widths + (MaybeAuto::Auto, MaybeAuto::Auto) => { + if self.has_intrinsic_ratio() { + // This approch follows the spirit of cover and contain constraint. + // https://drafts.csswg.org/css-images-3/#cover-contain + + // First, create two rectangles that keep aspect ratio while may be clamped + // by the contraints; + let first_isize = inline_constraint.clamp(intrinsic_inline_size); + let first_bsize = first_isize * intrinsic_block_size.0 / intrinsic_inline_size.0; + let second_bsize = block_constraint.clamp(intrinsic_block_size); + let second_isize = second_bsize * intrinsic_inline_size.0 / intrinsic_block_size.0; + + let (inline_size, block_size) = match (first_isize.cmp(&intrinsic_inline_size) , + second_isize.cmp(&intrinsic_inline_size)) { + (Ordering::Equal, Ordering::Equal) => + (first_isize, first_bsize), + // When only one rectangle is clamped, use it; + (Ordering::Equal, _) => + (second_isize, second_bsize), + (_, Ordering::Equal) => + (first_isize, first_bsize), + // When both rectangles grow (smaller than min sizes), + // Choose the larger one; + (Ordering::Greater, Ordering::Greater) => + if first_isize > second_isize { + (first_isize, first_bsize) + } else { + (second_isize, second_bsize) + }, + // When both rectangles shrink (larger than max sizes), + // Choose the smaller one; + (Ordering::Less, Ordering::Less) => + if first_isize > second_isize { + (second_isize, second_bsize) + } else { + (first_isize, first_bsize) + }, + // It does not matter which we choose here, because both sizes + // will be clamped to constraint; + (Ordering::Less, Ordering::Greater) | (Ordering::Greater, Ordering::Less) => + (first_isize, first_bsize) + }; + // Clamp the result and we are done :-) + (inline_constraint.clamp(inline_size), block_constraint.clamp(block_size)) + } else { + (inline_constraint.clamp(intrinsic_inline_size), + block_constraint.clamp(intrinsic_block_size)) + } + } + } + } + + /// Return a size constraint that can be used the clamp size in given direction. + /// To take `box-sizing: border-box` into account, the `border_padding` field + /// must be initialized first. + /// + /// TODO(stshine): Maybe there is a more convenient way. + pub fn size_constraint(&self, containing_size: Option<Au>, direction: Direction) -> SizeConstraint { + let (style_min_size, style_max_size) = match direction { + Direction::Inline => (self.style.min_inline_size(), self.style.max_inline_size()), + Direction::Block => (self.style.min_block_size(), self.style.max_block_size()) + }; + + let border = if self.style().get_position().box_sizing == box_sizing::T::border_box { + Some(self.border_padding.start_end(direction)) + } else { + None + }; + + SizeConstraint::new(containing_size, style_min_size, style_max_size, border) + } + /// Returns a guess as to the distances from the margin edge of this fragment to its content /// in the inline direction. This will generally be correct unless percentages are involved. /// @@ -1255,7 +1168,7 @@ impl Fragment { } /// Returns the border width in given direction if this fragment has property - /// 'box-sizing: border-box'. The `border_padding` field should have been initialized. + /// 'box-sizing: border-box'. The `border_padding` field must have been initialized. pub fn box_sizing_boundary(&self, direction: Direction) -> Au { match (self.style().get_position().box_sizing, direction) { (box_sizing::T::border_box, Direction::Inline) => { @@ -1532,7 +1445,6 @@ impl Fragment { match self.specific { SpecificFragmentInfo::Generic | SpecificFragmentInfo::GeneratedContent(_) | - SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | SpecificFragmentInfo::TableColumn(_) | @@ -1549,66 +1461,42 @@ impl Fragment { let block_flow = info.flow_ref.as_block(); result.union_block(&block_flow.base.intrinsic_inline_sizes) } - SpecificFragmentInfo::Image(ref mut image_fragment_info) => { - let mut image_inline_size = match self.style.content_inline_size() { - LengthOrPercentageOrAuto::Auto | - LengthOrPercentageOrAuto::Percentage(_) => { - image_fragment_info.image_inline_size() - } - LengthOrPercentageOrAuto::Length(length) => length, - LengthOrPercentageOrAuto::Calc(calc) => calc.length(), - }; - - image_inline_size = max(model::specified(self.style.min_inline_size(), Au(0)), image_inline_size); - if let Some(max) = model::specified_or_none(self.style.max_inline_size(), Au(0)) { - image_inline_size = min(image_inline_size, max) - } - - result.union_block(&IntrinsicISizes { - minimum_inline_size: image_inline_size, - preferred_inline_size: image_inline_size, - }); - } - SpecificFragmentInfo::Canvas(ref mut canvas_fragment_info) => { - let mut canvas_inline_size = match self.style.content_inline_size() { + SpecificFragmentInfo::Image(_) | + SpecificFragmentInfo::Canvas(_) | + SpecificFragmentInfo::Iframe(_) | + SpecificFragmentInfo::Svg(_) => { + let mut inline_size = match self.style.content_inline_size() { LengthOrPercentageOrAuto::Auto | LengthOrPercentageOrAuto::Percentage(_) => { - canvas_fragment_info.canvas_inline_size() + // We have to initialize the `border_padding` field first to make + // the size constraints work properly. + // TODO(stshine): Find a cleaner way to do this. + let padding = self.style.logical_padding(); + self.border_padding.inline_start = model::specified(padding.inline_start, Au(0)); + self.border_padding.inline_end = model::specified(padding.inline_end, Au(0)); + self.border_padding.block_start = model::specified(padding.block_start, Au(0)); + self.border_padding.block_end = model::specified(padding.block_end, Au(0)); + let border = self.border_width(); + self.border_padding.inline_start += border.inline_start; + self.border_padding.inline_end += border.inline_end; + self.border_padding.block_start += border.block_start; + self.border_padding.block_end += border.block_end; + let (result_inline, _) = self.calculate_replaced_sizes(None, None); + result_inline } LengthOrPercentageOrAuto::Length(length) => length, LengthOrPercentageOrAuto::Calc(calc) => calc.length(), }; - canvas_inline_size = max(model::specified(self.style.min_inline_size(), Au(0)), canvas_inline_size); - if let Some(max) = model::specified_or_none(self.style.max_inline_size(), Au(0)) { - canvas_inline_size = min(canvas_inline_size, max) - } + let size_constraint = self.size_constraint(None, Direction::Inline); + inline_size = size_constraint.clamp(inline_size); result.union_block(&IntrinsicISizes { - minimum_inline_size: canvas_inline_size, - preferred_inline_size: canvas_inline_size, + minimum_inline_size: inline_size, + preferred_inline_size: inline_size, }); } - SpecificFragmentInfo::Svg(ref mut svg_fragment_info) => { - let mut svg_inline_size = match self.style.content_inline_size() { - LengthOrPercentageOrAuto::Auto | - LengthOrPercentageOrAuto::Percentage(_) => { - svg_fragment_info.svg_inline_size() - } - LengthOrPercentageOrAuto::Length(length) => length, - LengthOrPercentageOrAuto::Calc(calc) => calc.length(), - }; - - svg_inline_size = max(model::specified(self.style.min_inline_size(), Au(0)), svg_inline_size); - if let Some(max) = model::specified_or_none(self.style.max_inline_size(), Au(0)) { - svg_inline_size = min(svg_inline_size, max) - } - result.union_block(&IntrinsicISizes { - minimum_inline_size: svg_inline_size, - preferred_inline_size: svg_inline_size, - }); - } SpecificFragmentInfo::ScannedText(ref text_fragment_info) => { let range = &text_fragment_info.range; @@ -1677,45 +1565,6 @@ impl Fragment { } } - /// TODO: What exactly does this function return? Why is it Au(0) for - /// `SpecificFragmentInfo::Generic`? - pub fn content_inline_size(&self) -> Au { - match self.specific { - SpecificFragmentInfo::Generic | - SpecificFragmentInfo::GeneratedContent(_) | - SpecificFragmentInfo::Iframe(_) | - SpecificFragmentInfo::Table | - SpecificFragmentInfo::TableCell | - SpecificFragmentInfo::TableRow | - SpecificFragmentInfo::TableWrapper | - SpecificFragmentInfo::Multicol | - SpecificFragmentInfo::MulticolColumn | - SpecificFragmentInfo::InlineBlock(_) | - SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | - SpecificFragmentInfo::InlineAbsolute(_) => Au(0), - SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => { - canvas_fragment_info.replaced_image_fragment_info.computed_inline_size() - } - SpecificFragmentInfo::Svg(ref svg_fragment_info) => { - svg_fragment_info.replaced_image_fragment_info.computed_inline_size() - } - SpecificFragmentInfo::Image(ref image_fragment_info) => { - image_fragment_info.replaced_image_fragment_info.computed_inline_size() - } - SpecificFragmentInfo::ScannedText(ref text_fragment_info) => { - let (range, run) = (&text_fragment_info.range, &text_fragment_info.run); - let text_bounds = run.metrics_for_range(range).bounding_box; - text_bounds.size.width - } - SpecificFragmentInfo::TableColumn(_) => { - panic!("Table column fragments do not have inline_size") - } - SpecificFragmentInfo::UnscannedText(_) => { - panic!("Unscanned text fragments should have been scanned by now!") - } - } - } - /// Returns the dimensions of the content box. /// /// This is marked `#[inline]` because it is frequently called when only one or two of the @@ -1992,10 +1841,8 @@ impl Fragment { SpecificFragmentInfo::Svg(_) => {} }; - let style = &*self.style; - let noncontent_inline_size = self.border_padding.inline_start_end(); - match self.specific { + // Inline blocks SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) => { let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_mut_block(); block_flow.base.position.size.inline = @@ -2020,54 +1867,24 @@ impl Fragment { block_flow.base.block_container_inline_size = self.border_box.size.inline; block_flow.base.block_container_writing_mode = self.style.writing_mode; } + + // Text SpecificFragmentInfo::ScannedText(ref info) => { // Scanned text fragments will have already had their content inline-sizes assigned // by this point. - self.border_box.size.inline = info.content_size.inline + noncontent_inline_size + self.border_box.size.inline = info.content_size.inline + + self.border_padding.inline_start_end(); } - SpecificFragmentInfo::Image(ref mut image_fragment_info) => { - let fragment_inline_size = image_fragment_info.image_inline_size(); - let fragment_block_size = image_fragment_info.image_block_size(); - self.border_box.size.inline = - image_fragment_info.replaced_image_fragment_info - .calculate_replaced_inline_size(style, - noncontent_inline_size, - container_inline_size, - container_block_size, - fragment_inline_size, - fragment_block_size); - } - SpecificFragmentInfo::Canvas(ref mut canvas_fragment_info) => { - let fragment_inline_size = canvas_fragment_info.canvas_inline_size(); - let fragment_block_size = canvas_fragment_info.canvas_block_size(); - self.border_box.size.inline = - canvas_fragment_info.replaced_image_fragment_info - .calculate_replaced_inline_size(style, - noncontent_inline_size, - container_inline_size, - container_block_size, - fragment_inline_size, - fragment_block_size); - } - SpecificFragmentInfo::Svg(ref mut svg_fragment_info) => { - let fragment_inline_size = svg_fragment_info.svg_inline_size(); - let fragment_block_size = svg_fragment_info.svg_block_size(); - self.border_box.size.inline = - svg_fragment_info.replaced_image_fragment_info - .calculate_replaced_inline_size(style, - noncontent_inline_size, - container_inline_size, - container_block_size, - fragment_inline_size, - fragment_block_size); - } - SpecificFragmentInfo::Iframe(ref iframe_fragment_info) => { - self.border_box.size.inline = - iframe_fragment_info.calculate_replaced_inline_size(style, - container_inline_size) + - noncontent_inline_size; + + // Replaced elements + _ if self.is_replaced() => { + let (inline_size, block_size) = + self.calculate_replaced_sizes(Some(container_inline_size), container_block_size); + self.border_box.size.inline = inline_size + self.border_padding.inline_start_end(); + self.border_box.size.block = block_size + self.border_padding.block_start_end(); } - _ => panic!("this case should have been handled above"), + + ref unhandled @ _ => panic!("this case should have been handled above: {:?}", unhandled), } } @@ -2075,7 +1892,7 @@ impl Fragment { /// been assigned first. /// /// Ideally, this should follow CSS 2.1 § 10.6.2. - pub fn assign_replaced_block_size_if_necessary(&mut self, containing_block_block_size: Option<Au>) { + pub fn assign_replaced_block_size_if_necessary(&mut self) { match self.specific { SpecificFragmentInfo::Generic | SpecificFragmentInfo::GeneratedContent(_) | @@ -2101,48 +1918,16 @@ impl Fragment { SpecificFragmentInfo::Svg(_) => {} } - let style = &*self.style; - let noncontent_block_size = self.border_padding.block_start_end(); - match self.specific { - SpecificFragmentInfo::Image(ref mut image_fragment_info) => { - let fragment_inline_size = image_fragment_info.image_inline_size(); - let fragment_block_size = image_fragment_info.image_block_size(); - self.border_box.size.block = - image_fragment_info.replaced_image_fragment_info - .calculate_replaced_block_size(style, - noncontent_block_size, - containing_block_block_size, - fragment_inline_size, - fragment_block_size); - } - SpecificFragmentInfo::Canvas(ref mut canvas_fragment_info) => { - let fragment_inline_size = canvas_fragment_info.canvas_inline_size(); - let fragment_block_size = canvas_fragment_info.canvas_block_size(); - self.border_box.size.block = - canvas_fragment_info.replaced_image_fragment_info - .calculate_replaced_block_size(style, - noncontent_block_size, - containing_block_block_size, - fragment_inline_size, - fragment_block_size); - } - SpecificFragmentInfo::Svg(ref mut svg_fragment_info) => { - let fragment_inline_size = svg_fragment_info.svg_inline_size(); - let fragment_block_size = svg_fragment_info.svg_block_size(); - self.border_box.size.block = - svg_fragment_info.replaced_image_fragment_info - .calculate_replaced_block_size(style, - noncontent_block_size, - containing_block_block_size, - fragment_inline_size, - fragment_block_size); - } + // Text SpecificFragmentInfo::ScannedText(ref info) => { // Scanned text fragments' content block-sizes are calculated by the text run // scanner during flow construction. - self.border_box.size.block = info.content_size.block + noncontent_block_size + self.border_box.size.block = info.content_size.block + + self.border_padding.block_start_end(); } + + // Inline blocks SpecificFragmentInfo::InlineBlock(ref mut info) => { // Not the primary fragment, so we do not take the noncontent size into account. let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_block(); @@ -2160,36 +1945,31 @@ impl Fragment { self.border_box.size.block = block_flow.base.position.size.block + block_flow.fragment.margin.block_start_end() } - SpecificFragmentInfo::Iframe(ref info) => { - self.border_box.size.block = - info.calculate_replaced_block_size(style, containing_block_block_size) + - noncontent_block_size; - } - _ => panic!("should have been handled above"), + + // Replaced elements + _ if self.is_replaced() => {}, + + ref unhandled @ _ => panic!("should have been handled above: {:?}", unhandled), } } - /// Returns true if this fragment is replaced content or an inline-block or false otherwise. - pub fn is_replaced_or_inline_block(&self) -> bool { + /// Returns true if this fragment is replaced content. + pub fn is_replaced(&self) -> bool { match self.specific { - SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Iframe(_) | + SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Image(_) | - SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | - SpecificFragmentInfo::InlineBlock(_) | SpecificFragmentInfo::Svg(_) => true, - SpecificFragmentInfo::Generic | - SpecificFragmentInfo::GeneratedContent(_) | - SpecificFragmentInfo::InlineAbsolute(_) | - SpecificFragmentInfo::Table | - SpecificFragmentInfo::TableCell | - SpecificFragmentInfo::TableColumn(_) | - SpecificFragmentInfo::TableRow | - SpecificFragmentInfo::TableWrapper | - SpecificFragmentInfo::Multicol | - SpecificFragmentInfo::MulticolColumn | - SpecificFragmentInfo::ScannedText(_) | - SpecificFragmentInfo::UnscannedText(_) => false, + _ => false + } + } + + /// Returns true if this fragment is replaced content or an inline-block or false otherwise. + pub fn is_replaced_or_inline_block(&self) -> bool { + match self.specific { + SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | + SpecificFragmentInfo::InlineBlock(_) => true, + _ => self.is_replaced(), } } @@ -2210,10 +1990,10 @@ impl Fragment { SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::Svg(_) | SpecificFragmentInfo::Generic | SpecificFragmentInfo::GeneratedContent(_) => { - let ascent = self.border_box.size.block + self.margin.block_start; + let ascent = self.border_box.size.block + self.margin.block_end; InlineMetrics { - space_above_baseline: ascent, - space_below_baseline: self.margin.block_end, + space_above_baseline: ascent + self.margin.block_start, + space_below_baseline: Au(0), ascent: ascent, } } diff --git a/components/layout/generated_content.rs b/components/layout/generated_content.rs index d3eed080a8f..fe5ca470506 100644 --- a/components/layout/generated_content.rs +++ b/components/layout/generated_content.rs @@ -19,7 +19,6 @@ use std::collections::{HashMap, LinkedList}; use std::sync::Arc; use style::computed_values::{display, list_style_type}; use style::computed_values::content::ContentItem; -use style::dom::TRestyleDamage; use style::properties::ServoComputedValues; use style::selector_parser::RestyleDamage; use style::servo::restyle_damage::RESOLVE_GENERATED_CONTENT; diff --git a/components/layout/incremental.rs b/components/layout/incremental.rs index e38f55bec08..d24cda3307b 100644 --- a/components/layout/incremental.rs +++ b/components/layout/incremental.rs @@ -4,7 +4,6 @@ use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, IS_ABSOLUTELY_POSITIONED}; use style::computed_values::float; -use style::dom::TRestyleDamage; use style::selector_parser::RestyleDamage; use style::servo::restyle_damage::{REFLOW, RECONSTRUCT_FLOW}; diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 57eb16f1b84..372aa2ea088 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -32,7 +32,7 @@ use std::sync::Arc; use style::arc_ptr_eq; use style::computed_values::{display, overflow_x, position, text_align, text_justify}; use style::computed_values::{vertical_align, white_space}; -use style::context::{SharedStyleContext, StyleContext}; +use style::context::SharedStyleContext; use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; use style::properties::{longhands, ServoComputedValues}; use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPOSITION, RESOLVE_GENERATED_CONTENT}; @@ -1394,11 +1394,9 @@ impl Flow for InlineFlow { debug!("assign_block_size_inline: floats in: {:?}", self.base.floats); // Assign the block-size and late-computed inline-sizes for the inline fragments. - let containing_block_block_size = - self.base.block_container_explicit_block_size; for fragment in &mut self.fragments.fragments { fragment.update_late_computed_replaced_inline_size_if_necessary(); - fragment.assign_replaced_block_size_if_necessary(containing_block_block_size); + fragment.assign_replaced_block_size_if_necessary(); } // Reset our state, so that we handle incremental reflow correctly. diff --git a/components/layout/layout_debug.rs b/components/layout/layout_debug.rs index f98f253881f..3a8a1171287 100644 --- a/components/layout/layout_debug.rs +++ b/components/layout/layout_debug.rs @@ -65,13 +65,10 @@ struct State { impl Scope { pub fn new(name: String) -> Scope { STATE_KEY.with(|ref r| { - match *r.borrow_mut() { - Some(ref mut state) => { - let flow_trace = to_value(&flow::base(&*state.flow_root)); - let data = box ScopeData::new(name.clone(), flow_trace); - state.scope_stack.push(data); - } - None => {} + if let Some(ref mut state) = *r.borrow_mut() { + let flow_trace = to_value(&flow::base(&*state.flow_root)); + let data = box ScopeData::new(name.clone(), flow_trace); + state.scope_stack.push(data); } }); Scope @@ -82,14 +79,11 @@ impl Scope { impl Drop for Scope { fn drop(&mut self) { STATE_KEY.with(|ref r| { - match *r.borrow_mut() { - Some(ref mut state) => { - let mut current_scope = state.scope_stack.pop().unwrap(); - current_scope.post = to_value(&flow::base(&*state.flow_root)); - let previous_scope = state.scope_stack.last_mut().unwrap(); - previous_scope.children.push(current_scope); - } - None => {} + if let Some(ref mut state) = *r.borrow_mut() { + let mut current_scope = state.scope_stack.pop().unwrap(); + current_scope.post = to_value(&flow::base(&*state.flow_root)); + let previous_scope = state.scope_stack.last_mut().unwrap(); + previous_scope.children.push(current_scope); } }); } diff --git a/components/layout/lib.rs b/components/layout/lib.rs index a74bea33431..b2aaa006a2a 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -49,13 +49,13 @@ extern crate serde; extern crate serde_derive; extern crate serde_json; #[macro_use] extern crate servo_atoms; +extern crate servo_config; extern crate servo_url; extern crate smallvec; extern crate style; extern crate style_traits; extern crate unicode_bidi; extern crate unicode_script; -extern crate util; extern crate webrender_traits; #[macro_use] diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs index b3f8df9752d..20cc7bfa850 100644 --- a/components/layout/list_item.rs +++ b/components/layout/list_item.rs @@ -111,9 +111,7 @@ impl Flow for ListItemFlow { &mut layout_context.font_context(), &*self.block_flow.fragment.style); for marker in &mut self.marker_fragments { - let containing_block_block_size = - self.block_flow.base.block_container_explicit_block_size; - marker.assign_replaced_block_size_if_necessary(containing_block_block_size); + marker.assign_replaced_block_size_if_necessary(); let marker_inline_metrics = marker.aligned_inline_metrics(layout_context, &marker_line_metrics, Some(&marker_line_metrics)); diff --git a/components/layout/model.rs b/components/layout/model.rs index a0693e0d8f8..89847e0190b 100644 --- a/components/layout/model.rs +++ b/components/layout/model.rs @@ -436,6 +436,21 @@ impl MaybeAuto { } } +/// Receive an optional container size and return used value for width or height. +/// +/// `style_length`: content size as given in the CSS. +pub fn style_length(style_length: LengthOrPercentageOrAuto, + container_size: Option<Au>) -> MaybeAuto { + match container_size { + Some(length) => MaybeAuto::from_style(style_length, length), + None => if let LengthOrPercentageOrAuto::Length(length) = style_length { + MaybeAuto::Specified(length) + } else { + MaybeAuto::Auto + } + } +} + pub fn specified_or_none(length: LengthOrPercentageOrNone, containing_length: Au) -> Option<Au> { match length { LengthOrPercentageOrNone::None => None, @@ -504,64 +519,60 @@ impl ToGfxMatrix for ComputedMatrix { } } -// https://drafts.csswg.org/css2/visudet.html#min-max-widths -// https://drafts.csswg.org/css2/visudet.html#min-max-heights -/// A min or max constraint -/// -/// A `max` of `None` is equivalent to no limmit for the size in the given -/// dimension. The `min` is >= 0, as negative values are illegal and by -/// default `min` is 0. -#[derive(Debug)] -pub struct MinMaxConstraint { - min: Au, - max: Option<Au> +/// A min-size and max-size constraint. The constructor has a optional `border` +/// parameter, and when it is present the constraint will be subtracted. This is +/// used to adjust the constraint for `box-sizing: border-box`, and when you do so +/// make sure the size you want to clamp is intended to be used for content box. +#[derive(Clone, Copy, Debug)] +pub struct SizeConstraint { + min_size: Au, + max_size: Option<Au>, } -impl MinMaxConstraint { - /// Create a `MinMaxConstraint` for a dimension given the min, max, and content box size for - /// an axis - pub fn new(content_size: Option<Au>, min: LengthOrPercentage, - max: LengthOrPercentageOrNone) -> MinMaxConstraint { - let min = match min { - LengthOrPercentage::Length(length) => length, - LengthOrPercentage::Percentage(percent) => { - match content_size { - Some(size) => size.scale_by(percent), - None => Au(0), - } - }, - LengthOrPercentage::Calc(calc) => { - match content_size { - Some(size) => size.scale_by(calc.percentage()), - None => Au(0), - } +impl SizeConstraint { + /// Create a `SizeConstraint` for an axis. + pub fn new(container_size: Option<Au>, + min_size: LengthOrPercentage, + max_size: LengthOrPercentageOrNone, + border: Option<Au>) -> SizeConstraint { + let mut min_size = match container_size { + Some(container_size) => specified(min_size, container_size), + None => if let LengthOrPercentage::Length(length) = min_size { + length + } else { + Au(0) } }; - let max = match max { - LengthOrPercentageOrNone::Length(length) => Some(length), - LengthOrPercentageOrNone::Percentage(percent) => { - content_size.map(|size| size.scale_by(percent)) - }, - LengthOrPercentageOrNone::Calc(calc) => { - content_size.map(|size| size.scale_by(calc.percentage())) + let mut max_size = match container_size { + Some(container_size) => specified_or_none(max_size, container_size), + None => if let LengthOrPercentageOrNone::Length(length) = max_size { + Some(length) + } else { + None } - LengthOrPercentageOrNone::None => None, }; + // Make sure max size is not smaller than min size. + max_size = max_size.map(|x| max(x, min_size)); + + if let Some(border) = border { + min_size = max((min_size - border), Au(0)); + max_size = max_size.map(|x| max(x - border, Au(0))); + } - MinMaxConstraint { - min: min, - max: max + SizeConstraint { + min_size: min_size, + max_size: max_size } } - /// Clamp the given size by the given `min` and `max` constraints. + /// Clamp the given size by the given min size and max size constraint. pub fn clamp(&self, other: Au) -> Au { - if other < self.min { - self.min + if other < self.min_size { + self.min_size } else { - match self.max { - Some(max) if max < other => max, + match self.max_size { + Some(max_size) if max_size < other => max_size, _ => other } } diff --git a/components/layout/multicol.rs b/components/layout/multicol.rs index c1773fc0c63..b5651369ebf 100644 --- a/components/layout/multicol.rs +++ b/components/layout/multicol.rs @@ -21,7 +21,7 @@ use gfx_traits::print_tree::PrintTree; use std::cmp::{min, max}; use std::fmt; use std::sync::Arc; -use style::context::{StyleContext, SharedStyleContext}; +use style::context::SharedStyleContext; use style::logical_geometry::LogicalSize; use style::properties::ServoComputedValues; use style::values::Either; @@ -99,9 +99,11 @@ impl Flow for MulticolFlow { { let column_style = self.block_flow.fragment.style.get_column(); - // `None` is 'normal': "UA-specified length. A value of 1em is suggested." - let column_gap = column_style.column_gap.0.unwrap_or_else(|| - self.block_flow.fragment.style.get_font().font_size); + let column_gap = match column_style.column_gap { + Either::First(len) => len, + Either::Second(_normal) => self.block_flow.fragment.style.get_font().font_size, + }; + let mut column_count; if let Either::First(column_width) = column_style.column_width { column_count = diff --git a/components/layout/parallel.rs b/components/layout/parallel.rs index 03aaa512d3e..a51f585eb3c 100644 --- a/components/layout/parallel.rs +++ b/components/layout/parallel.rs @@ -13,13 +13,13 @@ use flow::{self, Flow, MutableFlowUtils, PostorderFlowTraversal, PreorderFlowTra use flow_ref::FlowRef; use profile_traits::time::{self, TimerMetadata, profile}; use rayon; +use servo_config::opts; use std::mem; use std::sync::atomic::{AtomicIsize, Ordering}; use style::dom::UnsafeNode; use style::parallel::CHUNK_SIZE; use traversal::{AssignISizes, BubbleISizes}; use traversal::AssignBSizes; -use util::opts; pub use style::parallel::traverse_dom; @@ -50,9 +50,9 @@ pub fn borrowed_flow_to_unsafe_flow(flow: &Flow) -> UnsafeFlow { } pub type ChunkedFlowTraversalFunction<'scope> = - extern "Rust" fn(Box<[UnsafeFlow]>, &'scope SharedLayoutContext, &rayon::Scope<'scope>); + extern "Rust" fn(Box<[UnsafeFlow]>, &rayon::Scope<'scope>, &'scope SharedLayoutContext); -pub type FlowTraversalFunction = extern "Rust" fn(UnsafeFlow, &SharedLayoutContext); +pub type FlowTraversalFunction = extern "Rust" fn(UnsafeFlow, &LayoutContext); /// Information that we need stored in each flow. pub struct FlowParallelInfo { @@ -132,20 +132,22 @@ trait ParallelPostorderFlowTraversal : PostorderFlowTraversal { trait ParallelPreorderFlowTraversal : PreorderFlowTraversal { fn run_parallel<'scope>(&self, unsafe_flows: &[UnsafeFlow], - layout_context: &'scope SharedLayoutContext, - scope: &rayon::Scope<'scope>); + scope: &rayon::Scope<'scope>, + shared: &'scope SharedLayoutContext); fn should_record_thread_ids(&self) -> bool; #[inline(always)] fn run_parallel_helper<'scope>(&self, unsafe_flows: &[UnsafeFlow], - layout_context: &'scope SharedLayoutContext, scope: &rayon::Scope<'scope>, + shared: &'scope SharedLayoutContext, top_down_func: ChunkedFlowTraversalFunction<'scope>, bottom_up_func: FlowTraversalFunction) { let mut discovered_child_flows = vec![]; + let context = LayoutContext::new(&shared); + for unsafe_flow in unsafe_flows { let mut had_children = false; unsafe { @@ -175,7 +177,7 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal { // If there were no more children, start assigning block-sizes. if !had_children { - bottom_up_func(*unsafe_flow, layout_context) + bottom_up_func(*unsafe_flow, &context) } } @@ -183,7 +185,7 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal { let nodes = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice(); scope.spawn(move |scope| { - top_down_func(nodes, layout_context, scope); + top_down_func(nodes, scope, shared); }); } } @@ -192,12 +194,12 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal { impl<'a> ParallelPreorderFlowTraversal for AssignISizes<'a> { fn run_parallel<'scope>(&self, unsafe_flows: &[UnsafeFlow], - layout_context: &'scope SharedLayoutContext, - scope: &rayon::Scope<'scope>) + scope: &rayon::Scope<'scope>, + shared: &'scope SharedLayoutContext) { self.run_parallel_helper(unsafe_flows, - layout_context, scope, + shared, assign_inline_sizes, assign_block_sizes_and_store_overflow) } @@ -210,20 +212,19 @@ impl<'a> ParallelPreorderFlowTraversal for AssignISizes<'a> { impl<'a> ParallelPostorderFlowTraversal for AssignBSizes<'a> {} fn assign_inline_sizes<'scope>(unsafe_flows: Box<[UnsafeFlow]>, - shared_layout_context: &'scope SharedLayoutContext, - scope: &rayon::Scope<'scope>) { + scope: &rayon::Scope<'scope>, + shared: &'scope SharedLayoutContext) { let assign_inline_sizes_traversal = AssignISizes { - shared_context: &shared_layout_context.style_context, + shared_context: &shared.style_context, }; - assign_inline_sizes_traversal.run_parallel(&unsafe_flows, shared_layout_context, scope) + assign_inline_sizes_traversal.run_parallel(&unsafe_flows, scope, shared) } fn assign_block_sizes_and_store_overflow( unsafe_flow: UnsafeFlow, - shared_layout_context: &SharedLayoutContext) { - let layout_context = LayoutContext::new(shared_layout_context); + context: &LayoutContext) { let assign_block_sizes_traversal = AssignBSizes { - layout_context: &layout_context, + layout_context: context, }; assign_block_sizes_traversal.run_parallel(unsafe_flow) } @@ -232,11 +233,11 @@ pub fn traverse_flow_tree_preorder( root: &mut Flow, profiler_metadata: Option<TimerMetadata>, time_profiler_chan: time::ProfilerChan, - shared_layout_context: &SharedLayoutContext, + shared: &SharedLayoutContext, queue: &rayon::ThreadPool) { if opts::get().bubble_inline_sizes_separately { - let layout_context = LayoutContext::new(shared_layout_context); - let bubble_inline_sizes = BubbleISizes { layout_context: &layout_context }; + let context = LayoutContext::new(shared); + let bubble_inline_sizes = BubbleISizes { layout_context: &context }; root.traverse_postorder(&bubble_inline_sizes); } @@ -246,7 +247,7 @@ pub fn traverse_flow_tree_preorder( rayon::scope(move |scope| { profile(time::ProfilerCategory::LayoutParallelWarmup, profiler_metadata, time_profiler_chan, move || { - assign_inline_sizes(nodes, &shared_layout_context, scope); + assign_inline_sizes(nodes, scope, &shared); }); }); }); diff --git a/components/layout/query.rs b/components/layout/query.rs index 765cd2bdab4..0a44a9b1b49 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -6,6 +6,7 @@ use app_units::Au; use construct::ConstructionResult; +use context::{ScopedThreadLocalLayoutContext, SharedLayoutContext}; use euclid::point::Point2D; use euclid::rect::Rect; use euclid::size::Size2D; @@ -24,7 +25,6 @@ use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutElemen use script_traits::LayoutMsg as ConstellationMsg; use script_traits::UntrustedNodeAddress; use sequential; -use servo_atoms::Atom; use std::cmp::{min, max}; use std::ops::Deref; use std::sync::{Arc, Mutex}; @@ -32,8 +32,8 @@ use style::computed_values; use style::context::StyleContext; use style::dom::TElement; use style::logical_geometry::{WritingMode, BlockFlowDirection, InlineBaseDirection}; +use style::properties::{style_structs, PropertyId, PropertyDeclarationId, LonghandId}; use style::properties::longhands::{display, position}; -use style::properties::style_structs; use style::selector_parser::PseudoElement; use style::stylist::Stylist; use style_traits::ToCss; @@ -75,7 +75,7 @@ pub struct LayoutThreadData { pub scroll_area_response: Rect<i32>, /// A queued response for the resolved style property of an element. - pub resolved_style_response: Option<String>, + pub resolved_style_response: String, /// A queued response for the offset parent/rect of a node. pub offset_parent_response: OffsetParentResponse, @@ -625,13 +625,12 @@ pub fn process_node_scroll_area_request< N: LayoutNode>(requested_node: N, layou /// Return the resolved value of property for a given (pseudo)element. /// https://drafts.csswg.org/cssom/#resolved-value -pub fn process_resolved_style_request<'a, N, C>(requested_node: N, - style_context: &'a C, - pseudo: &Option<PseudoElement>, - property: &Atom, - layout_root: &mut Flow) -> Option<String> +pub fn process_resolved_style_request<'a, N>(shared: &SharedLayoutContext, + requested_node: N, + pseudo: &Option<PseudoElement>, + property: &PropertyId, + layout_root: &mut Flow) -> String where N: LayoutNode, - C: StyleContext<'a> { use style::traversal::{clear_descendant_data, style_element_in_display_none_subtree}; let element = requested_node.as_element().unwrap(); @@ -646,8 +645,14 @@ pub fn process_resolved_style_request<'a, N, C>(requested_node: N, // we'd need a mechanism to prevent detect when it's stale (since we don't // traverse display:none subtrees during restyle). let display_none_root = if element.get_data().is_none() { - Some(style_element_in_display_none_subtree(element, &|e| e.as_node().initialize_data(), - style_context)) + let mut tlc = ScopedThreadLocalLayoutContext::new(shared); + let context = StyleContext { + shared: &shared.style_context, + thread_local: &mut tlc.style_context, + }; + + Some(style_element_in_display_none_subtree(&context, element, + &|e| e.as_node().initialize_data())) } else { None }; @@ -667,13 +672,25 @@ pub fn process_resolved_style_request<'a, N, C>(requested_node: N, // The pseudo doesn't exist, return nothing. Chrome seems to query // the element itself in this case, Firefox uses the resolved value. // https://www.w3.org/Bugs/Public/show_bug.cgi?id=29006 - return None; + return String::new(); } Some(layout_el) => layout_el }; let style = &*layout_el.resolved_style(); + let longhand_id = match *property { + PropertyId::Longhand(id) => id, + + // Firefox returns blank strings for the computed value of shorthands, + // so this should be web-compatible. + PropertyId::Shorthand(_) => return String::new(), + + PropertyId::Custom(ref name) => { + return style.computed_value_to_string(PropertyDeclarationId::Custom(name)) + } + }; + // Clear any temporarily-resolved data to maintain our invariants. See the comment // at the top of this function. display_none_root.map(|r| clear_descendant_data(r, &|e| e.as_node().clear_data())); @@ -697,7 +714,7 @@ pub fn process_resolved_style_request<'a, N, C>(requested_node: N, layout_el: <N::ConcreteThreadSafeLayoutNode as ThreadSafeLayoutNode>::ConcreteThreadSafeLayoutElement, layout_root: &mut Flow, requested_node: N, - property: &Atom) -> Option<String> { + longhand_id: LonghandId) -> String { let maybe_data = layout_el.borrow_layout_data(); let position = maybe_data.map_or(Point2D::zero(), |data| { match (*data).flow_construction_result { @@ -708,13 +725,13 @@ pub fn process_resolved_style_request<'a, N, C>(requested_node: N, _ => Point2D::zero() } }); - let property = match *property { - atom!("bottom") => PositionProperty::Bottom, - atom!("top") => PositionProperty::Top, - atom!("left") => PositionProperty::Left, - atom!("right") => PositionProperty::Right, - atom!("width") => PositionProperty::Width, - atom!("height") => PositionProperty::Height, + let property = match longhand_id { + LonghandId::Bottom => PositionProperty::Bottom, + LonghandId::Top => PositionProperty::Top, + LonghandId::Left => PositionProperty::Left, + LonghandId::Right => PositionProperty::Right, + LonghandId::Width => PositionProperty::Width, + LonghandId::Height => PositionProperty::Height, _ => unreachable!() }; let mut iterator = @@ -723,27 +740,25 @@ pub fn process_resolved_style_request<'a, N, C>(requested_node: N, position); sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator); - iterator.result.map(|r| r.to_css_string()) + iterator.result.map(|r| r.to_css_string()).unwrap_or(String::new()) } // TODO: we will return neither the computed nor used value for margin and padding. - // Firefox returns blank strings for the computed value of shorthands, - // so this should be web-compatible. - match *property { - atom!("margin-bottom") | atom!("margin-top") | - atom!("margin-left") | atom!("margin-right") | - atom!("padding-bottom") | atom!("padding-top") | - atom!("padding-left") | atom!("padding-right") + match longhand_id { + LonghandId::MarginBottom | LonghandId::MarginTop | + LonghandId::MarginLeft | LonghandId::MarginRight | + LonghandId::PaddingBottom | LonghandId::PaddingTop | + LonghandId::PaddingLeft | LonghandId::PaddingRight if applies && style.get_box().display != display::computed_value::T::none => { - let (margin_padding, side) = match *property { - atom!("margin-bottom") => (MarginPadding::Margin, Side::Bottom), - atom!("margin-top") => (MarginPadding::Margin, Side::Top), - atom!("margin-left") => (MarginPadding::Margin, Side::Left), - atom!("margin-right") => (MarginPadding::Margin, Side::Right), - atom!("padding-bottom") => (MarginPadding::Padding, Side::Bottom), - atom!("padding-top") => (MarginPadding::Padding, Side::Top), - atom!("padding-left") => (MarginPadding::Padding, Side::Left), - atom!("padding-right") => (MarginPadding::Padding, Side::Right), + let (margin_padding, side) = match longhand_id { + LonghandId::MarginBottom => (MarginPadding::Margin, Side::Bottom), + LonghandId::MarginTop => (MarginPadding::Margin, Side::Top), + LonghandId::MarginLeft => (MarginPadding::Margin, Side::Left), + LonghandId::MarginRight => (MarginPadding::Margin, Side::Right), + LonghandId::PaddingBottom => (MarginPadding::Padding, Side::Bottom), + LonghandId::PaddingTop => (MarginPadding::Padding, Side::Top), + LonghandId::PaddingLeft => (MarginPadding::Padding, Side::Left), + LonghandId::PaddingRight => (MarginPadding::Padding, Side::Right), _ => unreachable!() }; let mut iterator = @@ -753,23 +768,22 @@ pub fn process_resolved_style_request<'a, N, C>(requested_node: N, style.writing_mode); sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator); - iterator.result.map(|r| r.to_css_string()) + iterator.result.map(|r| r.to_css_string()).unwrap_or(String::new()) }, - atom!("bottom") | atom!("top") | atom!("right") | - atom!("left") + LonghandId::Bottom | LonghandId::Top | LonghandId::Right | LonghandId::Left if applies && positioned && style.get_box().display != display::computed_value::T::none => { - used_value_for_position_property(layout_el, layout_root, requested_node, property) + used_value_for_position_property(layout_el, layout_root, requested_node, longhand_id) } - atom!("width") | atom!("height") + LonghandId::Width | LonghandId::Height if applies && style.get_box().display != display::computed_value::T::none => { - used_value_for_position_property(layout_el, layout_root, requested_node, property) + used_value_for_position_property(layout_el, layout_root, requested_node, longhand_id) } // FIXME: implement used value computation for line-height - ref property => { - style.computed_value_to_string(&*property).ok() + _ => { + style.computed_value_to_string(PropertyDeclarationId::Longhand(longhand_id)) } } } diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index 71626243206..b8c62aaafb0 100644 --- a/components/layout/sequential.rs +++ b/components/layout/sequential.rs @@ -15,14 +15,13 @@ use flow::IS_ABSOLUTELY_POSITIONED; use fragment::FragmentBorderBoxIterator; use generated_content::ResolveGeneratedContent; use gfx_traits::ScrollRootId; -use style::context::StyleContext; +use servo_config::opts; use style::servo::restyle_damage::{REFLOW, STORE_OVERFLOW}; use traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList}; -use util::opts; pub use style::sequential::traverse_dom; -pub fn resolve_generated_content(root: &mut Flow, shared_layout_context: &SharedLayoutContext) { +pub fn resolve_generated_content(root: &mut Flow, shared: &SharedLayoutContext) { fn doit(flow: &mut Flow, level: u32, traversal: &mut ResolveGeneratedContent) { if !traversal.should_process(flow) { return @@ -35,13 +34,13 @@ pub fn resolve_generated_content(root: &mut Flow, shared_layout_context: &Shared } } - let layout_context = LayoutContext::new(shared_layout_context); + let layout_context = LayoutContext::new(shared); let mut traversal = ResolveGeneratedContent::new(&layout_context); doit(root, 0, &mut traversal) } pub fn traverse_flow_tree_preorder(root: &mut Flow, - shared_layout_context: &SharedLayoutContext) { + shared: &SharedLayoutContext) { fn doit(flow: &mut Flow, assign_inline_sizes: AssignISizes, assign_block_sizes: AssignBSizes) { @@ -58,7 +57,7 @@ pub fn traverse_flow_tree_preorder(root: &mut Flow, } } - let layout_context = LayoutContext::new(shared_layout_context); + let layout_context = LayoutContext::new(shared); if opts::get().bubble_inline_sizes_separately { let bubble_inline_sizes = BubbleISizes { layout_context: &layout_context }; diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index e20d3ab98ef..b3672ecf32c 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -5,102 +5,83 @@ //! Traversals over the DOM and flow trees, running the layout computations. use construct::FlowConstructor; -use context::{LayoutContext, SharedLayoutContext}; +use context::{LayoutContext, ScopedThreadLocalLayoutContext, SharedLayoutContext}; use display_list_builder::DisplayListBuildState; use flow::{self, PreorderFlowTraversal}; use flow::{CAN_BE_FRAGMENTED, Flow, ImmutableFlowUtils, PostorderFlowTraversal}; -use gfx::display_list::OpaqueNode; use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; -use std::mem; +use servo_config::opts; use style::atomic_refcell::AtomicRefCell; -use style::context::{LocalStyleContext, SharedStyleContext, StyleContext}; +use style::context::{SharedStyleContext, StyleContext}; use style::data::ElementData; -use style::dom::{StylingMode, TElement, TNode}; +use style::dom::{TElement, TNode}; use style::selector_parser::RestyleDamage; use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT}; -use style::traversal::{DomTraversalContext, recalc_style_at, remove_from_bloom_filter}; +use style::traversal::{DomTraversal, recalc_style_at}; use style::traversal::PerLevelTraversalData; -use util::opts; use wrapper::{GetRawData, LayoutNodeHelpers, LayoutNodeLayoutData}; -pub struct RecalcStyleAndConstructFlows<'lc> { - context: LayoutContext<'lc>, - root: OpaqueNode, +pub struct RecalcStyleAndConstructFlows { + shared: SharedLayoutContext, } -#[allow(unsafe_code)] -impl<'lc, N> DomTraversalContext<N> for RecalcStyleAndConstructFlows<'lc> - where N: LayoutNode + TNode, - N::ConcreteElement: TElement +impl RecalcStyleAndConstructFlows { + pub fn shared_layout_context(&self) -> &SharedLayoutContext { + &self.shared + } +} -{ - type SharedContext = SharedLayoutContext; - #[allow(unsafe_code)] - fn new<'a>(shared: &'a Self::SharedContext, root: OpaqueNode) -> Self { - // FIXME(bholley): This transmutation from &'a to &'lc is very unfortunate, but I haven't - // found a way to avoid it despite spending several days on it (and consulting Manishearth, - // brson, and nmatsakis). - // - // The crux of the problem is that parameterizing DomTraversalContext on the lifetime of - // the SharedContext doesn't work for a variety of reasons [1]. However, the code in - // parallel.rs needs to be able to use the DomTraversalContext trait (or something similar) - // to stack-allocate a struct (a generalized LayoutContext<'a>) that holds a borrowed - // SharedContext, which means that the struct needs to be parameterized on a lifetime. - // Given the aforementioned constraint, the only way to accomplish this is to avoid - // propagating the borrow lifetime from the struct to the trait, but that means that the - // new() method on the trait cannot require the lifetime of its argument to match the - // lifetime of the Self object it creates. - // - // This could be solved with an associated type with an unbound lifetime parameter, but - // that would require higher-kinded types, which don't exist yet and probably aren't coming - // for a while. - // - // So we transmute. :-( This is safe because the DomTravesalContext is stack-allocated on - // the worker thread while processing a WorkUnit, whereas the borrowed SharedContext is - // live for the entire duration of the restyle. This really could _almost_ compile: all - // we'd need to do is change the signature to to |new<'a: 'lc>|, and everything would - // work great. But we can't do that, because that would cause a mismatch with the signature - // in the trait we're implementing, and we can't mention 'lc in that trait at all for the - // reasons described above. - // - // [1] For example, the WorkQueue type needs to be parameterized on the concrete type of - // DomTraversalContext::SharedContext, and the WorkQueue lifetime is similar to that of the - // LayoutThread, generally much longer than that of a given SharedLayoutContext borrow. - let shared_lc: &'lc SharedLayoutContext = unsafe { mem::transmute(shared) }; +impl RecalcStyleAndConstructFlows { + /// Creates a traversal context, taking ownership of the shared layout context. + pub fn new(shared: SharedLayoutContext) -> Self { RecalcStyleAndConstructFlows { - context: LayoutContext::new(shared_lc), - root: root, + shared: shared, } } - fn process_preorder(&self, node: N, data: &mut PerLevelTraversalData) { + /// Consumes this traversal context, returning ownership of the shared layout + /// context to the caller. + pub fn destroy(self) -> SharedLayoutContext { + self.shared + } +} + +#[allow(unsafe_code)] +impl<N> DomTraversal<N> for RecalcStyleAndConstructFlows + where N: LayoutNode + TNode, + N::ConcreteElement: TElement +{ + type ThreadLocalContext = ScopedThreadLocalLayoutContext<N::ConcreteElement>; + + fn process_preorder(&self, traversal_data: &mut PerLevelTraversalData, + thread_local: &mut Self::ThreadLocalContext, node: N) { // FIXME(pcwalton): Stop allocating here. Ideally this should just be // done by the HTML parser. node.initialize_data(); if !node.is_text_node() { let el = node.as_element().unwrap(); - recalc_style_at::<_, _, Self>(&self.context, data, el); + let mut data = el.mutate_data().unwrap(); + let mut context = StyleContext { + shared: &self.shared.style_context, + thread_local: &mut thread_local.style_context, + }; + recalc_style_at(self, traversal_data, &mut context, el, &mut data); } } - fn process_postorder(&self, node: N) { - construct_flows_at(&self.context, self.root, node); + fn process_postorder(&self, thread_local: &mut Self::ThreadLocalContext, node: N) { + let context = LayoutContext::new(&self.shared); + construct_flows_at(&context, thread_local, node); } - fn should_traverse_child(child: N) -> bool { - match child.as_element() { - // Elements should be traversed if they need styling or flow construction. - Some(el) => el.styling_mode() != StylingMode::Stop || - el.as_node().to_threadsafe().restyle_damage() != RestyleDamage::empty(), - - // Text nodes never need styling. However, there are two cases they may need - // flow construction: - // (1) They child doesn't yet have layout data (preorder traversal initializes it). - // (2) The parent element has restyle damage (so the text flow also needs fixup). - None => child.get_raw_data().is_none() || - child.parent_node().unwrap().to_threadsafe().restyle_damage() != RestyleDamage::empty(), - } + fn text_node_needs_traversal(node: N) -> bool { + // Text nodes never need styling. However, there are two cases they may need + // flow construction: + // (1) They child doesn't yet have layout data (preorder traversal initializes it). + // (2) The parent element has restyle damage (so the text flow also needs fixup). + node.get_raw_data().is_none() || + node.parent_node().unwrap().to_threadsafe().restyle_damage() != RestyleDamage::empty() } unsafe fn ensure_element_data(element: &N::ConcreteElement) -> &AtomicRefCell<ElementData> { @@ -112,8 +93,12 @@ impl<'lc, N> DomTraversalContext<N> for RecalcStyleAndConstructFlows<'lc> element.as_node().clear_data(); } - fn local_context(&self) -> &LocalStyleContext { - self.context.local_context() + fn shared_context(&self) -> &SharedStyleContext { + &self.shared.style_context + } + + fn create_thread_local_context(&self) -> Self::ThreadLocalContext { + ScopedThreadLocalLayoutContext::new(&self.shared) } } @@ -126,7 +111,11 @@ pub trait PostorderNodeMutTraversal<ConcreteThreadSafeLayoutNode: ThreadSafeLayo /// The flow construction traversal, which builds flows for styled nodes. #[inline] #[allow(unsafe_code)] -fn construct_flows_at<'a, N: LayoutNode>(context: &'a LayoutContext<'a>, root: OpaqueNode, node: N) { +fn construct_flows_at<'a, N>(context: &LayoutContext<'a>, + _thread_local: &mut ScopedThreadLocalLayoutContext<N::ConcreteElement>, + node: N) + where N: LayoutNode, +{ debug!("construct_flows_at: {:?}", node); // Construct flows for this node. @@ -150,8 +139,6 @@ fn construct_flows_at<'a, N: LayoutNode>(context: &'a LayoutContext<'a>, root: O if let Some(el) = node.as_element() { el.mutate_data().unwrap().persist(); unsafe { el.unset_dirty_descendants(); } - - remove_from_bloom_filter(context, root, el); } } diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs index 69d3de29e72..0887e7d4b0c 100644 --- a/components/layout/webrender_helpers.rs +++ b/components/layout/webrender_helpers.rs @@ -16,7 +16,7 @@ use msg::constellation_msg::PipelineId; use style::computed_values::{image_rendering, mix_blend_mode}; use style::computed_values::filter::{self, Filter}; use style::values::computed::BorderStyle; -use webrender_traits::{self, DisplayListBuilder}; +use webrender_traits::{self, DisplayListBuilder, LayoutTransform}; pub trait WebRenderDisplayListConverter { fn convert_to_webrender(&self, pipeline_id: PipelineId) -> DisplayListBuilder; @@ -61,36 +61,38 @@ impl ToBoxShadowClipMode for BoxShadowClipMode { } trait ToSizeF { - fn to_sizef(&self) -> Size2D<f32>; + fn to_sizef(&self) -> webrender_traits::LayoutSize; } trait ToPointF { - fn to_pointf(&self) -> Point2D<f32>; + fn to_pointf(&self) -> webrender_traits::LayoutPoint; } impl ToPointF for Point2D<Au> { - fn to_pointf(&self) -> Point2D<f32> { - Point2D::new(self.x.to_f32_px(), self.y.to_f32_px()) + fn to_pointf(&self) -> webrender_traits::LayoutPoint { + webrender_traits::LayoutPoint::new(self.x.to_f32_px(), self.y.to_f32_px()) } } impl ToSizeF for Size2D<Au> { - fn to_sizef(&self) -> Size2D<f32> { - Size2D::new(self.width.to_f32_px(), self.height.to_f32_px()) + fn to_sizef(&self) -> webrender_traits::LayoutSize { + webrender_traits::LayoutSize::new(self.width.to_f32_px(), self.height.to_f32_px()) } } trait ToRectF { - fn to_rectf(&self) -> Rect<f32>; + fn to_rectf(&self) -> webrender_traits::LayoutRect; } impl ToRectF for Rect<Au> { - fn to_rectf(&self) -> Rect<f32> { + fn to_rectf(&self) -> webrender_traits::LayoutRect { let x = self.origin.x.to_f32_px(); let y = self.origin.y.to_f32_px(); let w = self.size.width.to_f32_px(); let h = self.size.height.to_f32_px(); - Rect::new(Point2D::new(x, y), Size2D::new(w, h)) + let point = webrender_traits::LayoutPoint::new(x, y); + let size = webrender_traits::LayoutSize::new(w, h); + webrender_traits::LayoutRect::new(point, size) } } @@ -340,12 +342,16 @@ impl WebRenderDisplayItemConverter for DisplayItem { ScrollPolicy::FixedPosition => webrender_traits::ScrollPolicy::Fixed, }; + let clip = builder.new_clip_region(&stacking_context.overflow.to_rectf(), + vec![], + None); + builder.push_stacking_context(webrender_scroll_policy, stacking_context.bounds.to_rectf(), - stacking_context.overflow.to_rectf(), + clip, stacking_context.z_index, - &stacking_context.transform, - &stacking_context.perspective, + &LayoutTransform::from_untyped(&stacking_context.transform), + &LayoutTransform::from_untyped(&stacking_context.perspective), stacking_context.blend_mode.to_blend_mode(), stacking_context.filters.to_filter_ops()); } |