diff --git a/Cargo.lock b/Cargo.lock index 858bf9748c9..f8530a618fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1330,6 +1330,7 @@ dependencies = [ "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "servo_atoms 0.0.1", "servo_config 0.0.1", + "servo_geometry 0.0.1", "servo_url 0.0.1", "smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "style 0.0.1", diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index fb6aa952440..1438e89c652 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -25,7 +25,7 @@ use ipc_channel::ipc::IpcSharedMemory; use msg::constellation_msg::PipelineId; use net_traits::image::base::{Image, PixelFormat}; use range::Range; -use servo_geometry::{au_rect_to_f32_rect, f32_rect_to_au_rect, max_rect}; +use servo_geometry::max_rect; use std::cmp::{self, Ordering}; use std::collections::HashMap; use std::fmt; @@ -413,24 +413,6 @@ impl StackingContext { ScrollRootId::root()) } - pub fn overflow_rect_in_parent_space(&self) -> Rect { - // Transform this stacking context to get it into the same space as - // the parent stacking context. - // - // TODO: Take into account 3d transforms, even though it's a fairly - // uncommon case. - let origin_x = self.bounds.origin.x.to_f32_px(); - let origin_y = self.bounds.origin.y.to_f32_px(); - - let transform = Matrix4D::identity().pre_translated(origin_x, origin_y, 0.0) - .pre_mul(&self.transform); - let transform_2d = transform.to_2d(); - - let overflow = au_rect_to_f32_rect(self.overflow); - let overflow = transform_2d.transform_rect(&overflow); - f32_rect_to_au_rect(overflow) - } - pub fn to_display_list_items(self) -> (DisplayItem, DisplayItem) { let mut base_item = BaseDisplayItem::empty(); base_item.stacking_context_id = self.id; diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml index cddd3425d65..eb1c968b581 100644 --- a/components/layout/Cargo.toml +++ b/components/layout/Cargo.toml @@ -38,6 +38,7 @@ script_traits = {path = "../script_traits"} selectors = "0.15" serde = "0.8" serde_derive = "0.8" +servo_geometry = {path = "../geometry"} serde_json = "0.8" servo_atoms = {path = "../atoms"} servo_config = {path = "../config"} diff --git a/components/layout/block.rs b/components/layout/block.rs index c2bdfedccfb..a4eb30ce606 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -518,6 +518,8 @@ bitflags! { flags BlockFlowFlags: u8 { #[doc = "If this is set, then this block flow is the root flow."] const IS_ROOT = 0b0000_0001, + #[doc = "If this is set, then this block flow has overflow and it will scroll."] + const HAS_SCROLLING_OVERFLOW = 0b0000_0010, } } @@ -1675,7 +1677,7 @@ impl BlockFlow { } } - pub fn has_scrolling_overflow(&self) -> bool { + pub fn style_permits_scrolling_overflow(&self) -> bool { match (self.fragment.style().get_box().overflow_x, self.fragment.style().get_box().overflow_y.0) { (overflow_x::T::auto, _) | (overflow_x::T::scroll, _) | @@ -1823,6 +1825,19 @@ impl BlockFlow { Au::from_f32_px(clip_rect.size.height))); self.base.clip = ClippingRegion::from_rect(&clip_rect) } + + pub fn mark_scrolling_overflow(&mut self, has_scrolling_overflow: bool) { + if has_scrolling_overflow { + self.flags.insert(HAS_SCROLLING_OVERFLOW); + } else { + self.flags.remove(HAS_SCROLLING_OVERFLOW); + } + } + + pub fn has_scrolling_overflow(&mut self) -> bool { + self.flags.contains(HAS_SCROLLING_OVERFLOW) + } + } impl Flow for BlockFlow { diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 4c7e29866c7..2895ecc6ba5 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -128,14 +128,7 @@ impl<'a> DisplayListBuildState<'a> { fn add_stacking_context(&mut self, parent_id: StackingContextId, - mut stacking_context: StackingContext) { - self.update_overflow_for_stacking_context(&mut stacking_context); - self.add_stacking_context_without_calcuating_overflow(parent_id, stacking_context); - } - - fn add_stacking_context_without_calcuating_overflow(&mut self, - parent_id: StackingContextId, - stacking_context: StackingContext) { + stacking_context: StackingContext) { let contexts = self.stacking_context_children.entry(parent_id).or_insert(Vec::new()); contexts.push(stacking_context); } @@ -194,24 +187,6 @@ impl<'a> DisplayListBuildState<'a> { } } - fn update_overflow_for_stacking_context(&mut self, stacking_context: &mut StackingContext) { - if stacking_context.context_type != StackingContextType::Real { - return; - } - - let children = self.stacking_context_children.get_mut(&stacking_context.id); - if let Some(children) = children { - for child in children { - if child.context_type == StackingContextType::Real { - // This child might be transformed, so we need to take into account - // its transformed overflow rect too, but at the correct position. - let overflow = child.overflow_rect_in_parent_space(); - stacking_context.overflow = stacking_context.overflow.union(&overflow); - } - } - } - } - fn to_display_list_for_stacking_context(&mut self, list: &mut Vec, stacking_context: StackingContext, @@ -1842,12 +1817,10 @@ impl BlockFlowDisplayListBuilding for BlockFlow { } fn collect_scroll_root_for_block(&mut self, state: &mut DisplayListBuildState) -> ScrollRootId { - if !self.has_scrolling_overflow() { + if !self.style_permits_scrolling_overflow() { return state.current_scroll_root_id; } - let scroll_root_id = ScrollRootId::new_of_type(self.fragment.node.id() as usize, - self.fragment.fragment_type()); let coordinate_system = if self.fragment.establishes_stacking_context() { CoordinateSystem::Own } else { @@ -1859,13 +1832,24 @@ impl BlockFlowDisplayListBuilding for BlockFlow { &self.base.early_absolute_position_info.relative_containing_block_size, self.base.early_absolute_position_info.relative_containing_block_mode, coordinate_system); + let clip = self.fragment.stacking_relative_content_box(&border_box); + let has_scrolling_overflow = self.base.overflow.scroll.origin != Point2D::zero() || + self.base.overflow.scroll.size.width > clip.size.width || + self.base.overflow.scroll.size.height > clip.size.height; + self.mark_scrolling_overflow(has_scrolling_overflow); + if !has_scrolling_overflow { + return state.current_scroll_root_id; + } + + let scroll_root_id = ScrollRootId::new_of_type(self.fragment.node.id() as usize, + self.fragment.fragment_type()); let parent_scroll_root_id = state.current_scroll_root_id; state.add_scroll_root( ScrollRoot { id: scroll_root_id, parent_id: parent_scroll_root_id, - clip: self.fragment.stacking_relative_content_box(&border_box), + clip: clip, size: self.base.overflow.scroll.size, } ); @@ -1898,10 +1882,9 @@ impl BlockFlowDisplayListBuilding for BlockFlow { state.stacking_context_children.remove(&stacking_context_id).unwrap_or_else(Vec::new); for child in new_children { if child.context_type == StackingContextType::PseudoFloat { - state.add_stacking_context_without_calcuating_overflow(stacking_context_id, child); + state.add_stacking_context(stacking_context_id, child); } else { - state.add_stacking_context_without_calcuating_overflow(parent_stacking_context_id, - child); + state.add_stacking_context(parent_stacking_context_id, child); } } } @@ -1942,9 +1925,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow { DisplayListSection::BlockBackgroundsAndBorders }; - if self.has_scrolling_overflow() { - state.processing_scroll_root_element = true; - } + state.processing_scroll_root_element = self.has_scrolling_overflow(); // Add the box that starts the block context. self.fragment diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 3c171738fb7..9d1495b52f6 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -43,6 +43,7 @@ use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo}; use multicol::MulticolFlow; use parallel::FlowParallelInfo; use serde::{Serialize, Serializer}; +use servo_geometry::{au_rect_to_f32_rect, f32_rect_to_au_rect}; use std::{fmt, mem, raw}; use std::iter::Zip; use std::slice::IterMut; @@ -248,6 +249,46 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static { might_have_floats_in_or_out } + fn get_overflow_in_parent_coordinates(&self) -> Overflow { + // FIXME(#2795): Get the real container size. + let container_size = Size2D::zero(); + let position = base(self).position.to_physical(base(self).writing_mode, container_size); + + let mut overflow = base(self).overflow; + + match self.class() { + FlowClass::Block | FlowClass::TableCaption | FlowClass::TableCell => {} + _ => { + overflow.translate(&position.origin); + return overflow; + } + } + + if !self.as_block().fragment.establishes_stacking_context() || + self.as_block().fragment.style.get_box().transform.0.is_none() { + overflow.translate(&position.origin); + return overflow; + } + + // TODO: Take into account 3d transforms, even though it's a fairly + // uncommon case. + let transform_2d = self.as_block().fragment.transform_matrix(&position).to_2d(); + let transformed_overflow = Overflow { + paint: f32_rect_to_au_rect(transform_2d.transform_rect( + &au_rect_to_f32_rect(overflow.paint))), + scroll: f32_rect_to_au_rect(transform_2d.transform_rect( + &au_rect_to_f32_rect(overflow.scroll))), + }; + + // TODO: We are taking the union of the overflow and transformed overflow here, which + // happened implicitly in the previous version of this code. This will probably be + // unnecessary once we are taking into account 3D transformations above. + overflow.union(&transformed_overflow); + + overflow.translate(&position.origin); + overflow + } + /// /// CSS Section 11.1 /// This is the union of rectangles of the flows for which we define the @@ -266,17 +307,11 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static { FlowClass::Block | FlowClass::TableCaption | FlowClass::TableCell => { - // FIXME(#2795): Get the real container size. - let container_size = Size2D::zero(); - let overflow_x = self.as_block().fragment.style.get_box().overflow_x; let overflow_y = self.as_block().fragment.style.get_box().overflow_y; for kid in mut_base(self).children.iter_mut() { - let mut kid_overflow = base(kid).overflow; - let kid_position = base(kid).position.to_physical(base(kid).writing_mode, - container_size); - kid_overflow.translate(&kid_position.origin); + let mut kid_overflow = kid.get_overflow_in_parent_coordinates(); // If the overflow for this flow is hidden on a given axis, just // put the existing overflow in the kid rect, so that the union diff --git a/components/layout/lib.rs b/components/layout/lib.rs index ecca5b6eed1..8c8e8633fa0 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -50,6 +50,7 @@ extern crate serde_derive; extern crate serde_json; #[macro_use] extern crate servo_atoms; extern crate servo_config; +extern crate servo_geometry; extern crate servo_url; extern crate smallvec; extern crate style; diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index d49d2d362d1..79f3adc7d47 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -5232,6 +5232,18 @@ "url": "/_mozilla/css/table_margin_auto_a.html" } ], + "css/table_overflow.html": [ + { + "path": "css/table_overflow.html", + "references": [ + [ + "/_mozilla/css/table_overflow_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/table_overflow.html" + } + ], "css/table_padding_a.html": [ { "path": "css/table_padding_a.html", @@ -20556,6 +20568,18 @@ "url": "/_mozilla/css/table_margin_auto_a.html" } ], + "css/table_overflow.html": [ + { + "path": "css/table_overflow.html", + "references": [ + [ + "/_mozilla/css/table_overflow_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/table_overflow.html" + } + ], "css/table_padding_a.html": [ { "path": "css/table_padding_a.html", diff --git a/tests/wpt/mozilla/tests/css/table_overflow.html b/tests/wpt/mozilla/tests/css/table_overflow.html new file mode 100644 index 00000000000..f37c66ac3ef --- /dev/null +++ b/tests/wpt/mozilla/tests/css/table_overflow.html @@ -0,0 +1,19 @@ + + +Ensure that table overflow:auto and overflow:scroll do not crash + + + + + + +
+
+ + diff --git a/tests/wpt/mozilla/tests/css/table_overflow_ref.html b/tests/wpt/mozilla/tests/css/table_overflow_ref.html new file mode 100644 index 00000000000..763a95cf31c --- /dev/null +++ b/tests/wpt/mozilla/tests/css/table_overflow_ref.html @@ -0,0 +1,17 @@ + + +Ensure that table overflow:auto and overflow:scroll do not crash + + + + +
+
+ +