aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout')
-rw-r--r--components/layout/Cargo.toml4
-rw-r--r--components/layout/animation.rs1
-rw-r--r--components/layout/block.rs58
-rw-r--r--components/layout/construct.rs40
-rw-r--r--components/layout/context.rs96
-rw-r--r--components/layout/display_list_builder.rs107
-rw-r--r--components/layout/flex.rs8
-rw-r--r--components/layout/flow.rs1
-rw-r--r--components/layout/fragment.rs760
-rw-r--r--components/layout/generated_content.rs1
-rw-r--r--components/layout/incremental.rs1
-rw-r--r--components/layout/inline.rs6
-rw-r--r--components/layout/layout_debug.rs24
-rw-r--r--components/layout/lib.rs2
-rw-r--r--components/layout/list_item.rs4
-rw-r--r--components/layout/model.rs101
-rw-r--r--components/layout/multicol.rs10
-rw-r--r--components/layout/parallel.rs45
-rw-r--r--components/layout/query.rs104
-rw-r--r--components/layout/sequential.rs11
-rw-r--r--components/layout/traversal.rs131
-rw-r--r--components/layout/webrender_helpers.rs32
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());
}