aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnthony Ramine <n.oxyde@gmail.com>2019-07-24 11:19:23 +0200
committerAnthony Ramine <n.oxyde@gmail.com>2019-07-31 17:09:17 +0200
commit317d700f5d977c80ec5404243586449203561a0b (patch)
treea073af20601d09dd97731db954bbe60c7eef1736
parent4846d76e82e2d60875472fb8ea375e22d40a0800 (diff)
downloadservo-317d700f5d977c80ec5404243586449203561a0b.tar.gz
servo-317d700f5d977c80ec5404243586449203561a0b.zip
Remove most of the things in layout 2020
We keep mostly the query system. There is probably more to delete but that's a good start I think.
-rw-r--r--Cargo.lock23
-rw-r--r--components/layout_2020/Cargo.toml25
-rw-r--r--components/layout_2020/animation.rs211
-rw-r--r--components/layout_2020/block.rs3697
-rw-r--r--components/layout_2020/construct.rs2443
-rw-r--r--components/layout_2020/context.rs183
-rw-r--r--components/layout_2020/data.rs50
-rw-r--r--components/layout_2020/display_list/background.rs336
-rw-r--r--components/layout_2020/display_list/border.rs199
-rw-r--r--components/layout_2020/display_list/builder.rs2977
-rw-r--r--components/layout_2020/display_list/conversions.rs167
-rw-r--r--components/layout_2020/display_list/gradient.rs323
-rw-r--r--components/layout_2020/display_list/items.rs780
-rw-r--r--components/layout_2020/display_list/mod.rs9
-rw-r--r--components/layout_2020/display_list/webrender_helpers.rs305
-rw-r--r--components/layout_2020/flex.rs1128
-rw-r--r--components/layout_2020/floats.rs605
-rw-r--r--components/layout_2020/flow.rs1422
-rw-r--r--components/layout_2020/flow_list.rs197
-rw-r--r--components/layout_2020/flow_ref.rs38
-rw-r--r--components/layout_2020/fragment.rs3365
-rw-r--r--components/layout_2020/generated_content.rs651
-rw-r--r--components/layout_2020/incremental.rs101
-rw-r--r--components/layout_2020/inline.rs2220
-rw-r--r--components/layout_2020/layout_debug.rs124
-rw-r--r--components/layout_2020/lib.rs37
-rw-r--r--components/layout_2020/linked_list.rs21
-rw-r--r--components/layout_2020/list_item.rs323
-rw-r--r--components/layout_2020/model.rs609
-rw-r--r--components/layout_2020/multicol.rs384
-rw-r--r--components/layout_2020/opaque_node.rs2
-rw-r--r--components/layout_2020/parallel.rs232
-rw-r--r--components/layout_2020/persistent_list.rs101
-rw-r--r--components/layout_2020/query.rs105
-rw-r--r--components/layout_2020/sequential.rs193
-rw-r--r--components/layout_2020/table.rs1378
-rw-r--r--components/layout_2020/table_caption.rs139
-rw-r--r--components/layout_2020/table_cell.rs506
-rw-r--r--components/layout_2020/table_colgroup.rs131
-rw-r--r--components/layout_2020/table_row.rs1158
-rw-r--r--components/layout_2020/table_rowgroup.rs274
-rw-r--r--components/layout_2020/table_wrapper.rs996
-rw-r--r--components/layout_2020/tests/size_of.rs18
-rw-r--r--components/layout_2020/text.rs798
-rw-r--r--components/layout_2020/traversal.rs296
-rw-r--r--components/layout_2020/wrapper.rs155
-rw-r--r--components/layout_thread_2020/lib.rs494
47 files changed, 75 insertions, 29854 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3cbc5cc30cc..80ba227042e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2332,47 +2332,24 @@ name = "layout_2020"
version = "0.0.1"
dependencies = [
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "canvas_traits 0.0.1",
- "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "embedder_traits 0.0.1",
"euclid 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx 0.0.1",
"gfx_traits 0.0.1",
- "html5ever 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-channel 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.53 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"malloc_size_of 0.0.1",
"msg 0.0.1",
- "net_traits 0.0.1",
- "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "ordered-float 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "profile_traits 0.0.1",
"range 0.0.1",
"rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"script_layout_interface 0.0.1",
"script_traits 0.0.1",
- "selectors 0.21.0",
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_arc 0.1.1",
- "servo_atoms 0.0.1",
- "servo_config 0.0.1",
- "servo_geometry 0.0.1",
"servo_url 0.0.1",
- "size_of_test 0.0.1",
- "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
"style 0.0.1",
"style_traits 0.0.1",
- "unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-script 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"webrender_api 0.60.0 (git+https://github.com/servo/webrender)",
- "xi-unicode 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
diff --git a/components/layout_2020/Cargo.toml b/components/layout_2020/Cargo.toml
index daa5170b23a..ece58df9a0d 100644
--- a/components/layout_2020/Cargo.toml
+++ b/components/layout_2020/Cargo.toml
@@ -14,46 +14,21 @@ doctest = false
[dependencies]
app_units = "0.7"
-atomic_refcell = "0.1"
-bitflags = "1.0"
-canvas_traits = {path = "../canvas_traits"}
-crossbeam-channel = "0.3"
-embedder_traits = {path = "../embedder_traits"}
euclid = "0.19"
fnv = "1.0"
-fxhash = "0.2"
gfx = {path = "../gfx"}
gfx_traits = {path = "../gfx_traits"}
-html5ever = "0.23"
ipc-channel = "0.11"
libc = "0.2"
-log = "0.4"
malloc_size_of = { path = "../malloc_size_of" }
msg = {path = "../msg"}
-net_traits = {path = "../net_traits"}
-num-traits = "0.2"
-ordered-float = "1.0"
-parking_lot = "0.8"
-profile_traits = {path = "../profile_traits"}
range = {path = "../range"}
rayon = "1"
script_layout_interface = {path = "../script_layout_interface"}
script_traits = {path = "../script_traits"}
-selectors = { path = "../selectors" }
serde = "1.0"
servo_arc = {path = "../servo_arc"}
-servo_atoms = {path = "../atoms"}
-servo_geometry = {path = "../geometry"}
-serde_json = "1.0"
-servo_config = {path = "../config"}
servo_url = {path = "../url"}
-smallvec = { version = "0.6", features = ["std", "union"] }
style = {path = "../style", features = ["servo", "servo-layout-2020"]}
style_traits = {path = "../style_traits"}
-unicode-bidi = {version = "0.3", features = ["with_serde"]}
-unicode-script = {version = "0.3", features = ["harfbuzz"]}
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
-xi-unicode = "0.1.0"
-
-[dev-dependencies]
-size_of_test = {path = "../size_of_test"}
diff --git a/components/layout_2020/animation.rs b/components/layout_2020/animation.rs
deleted file mode 100644
index 96e4801fa4e..00000000000
--- a/components/layout_2020/animation.rs
+++ /dev/null
@@ -1,211 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS transitions and animations.
-
-use crate::context::LayoutContext;
-use crate::display_list::items::OpaqueNode;
-use crate::flow::{Flow, GetBaseFlow};
-use crate::opaque_node::OpaqueNodeMethods;
-use crossbeam_channel::Receiver;
-use fxhash::{FxHashMap, FxHashSet};
-use ipc_channel::ipc::IpcSender;
-use msg::constellation_msg::PipelineId;
-use script_traits::UntrustedNodeAddress;
-use script_traits::{AnimationState, ConstellationControlMsg, LayoutMsg as ConstellationMsg};
-use style::animation::{update_style_for_animation, Animation};
-use style::dom::TElement;
-use style::font_metrics::ServoMetricsProvider;
-use style::selector_parser::RestyleDamage;
-use style::timer::Timer;
-
-/// Processes any new animations that were discovered after style recalculation.
-/// Also expire any old animations that have completed, inserting them into
-/// `expired_animations`.
-pub fn update_animation_state<E>(
- constellation_chan: &IpcSender<ConstellationMsg>,
- script_chan: &IpcSender<ConstellationControlMsg>,
- running_animations: &mut FxHashMap<OpaqueNode, Vec<Animation>>,
- expired_animations: &mut FxHashMap<OpaqueNode, Vec<Animation>>,
- mut keys_to_remove: FxHashSet<OpaqueNode>,
- mut newly_transitioning_nodes: Option<&mut Vec<UntrustedNodeAddress>>,
- new_animations_receiver: &Receiver<Animation>,
- pipeline_id: PipelineId,
- timer: &Timer,
-) where
- E: TElement,
-{
- let mut new_running_animations = vec![];
- while let Ok(animation) = new_animations_receiver.try_recv() {
- let mut should_push = true;
- if let Animation::Keyframes(ref node, _, ref name, ref state) = animation {
- // If the animation was already present in the list for the
- // node, just update its state, else push the new animation to
- // run.
- if let Some(ref mut animations) = running_animations.get_mut(node) {
- // TODO: This being linear is probably not optimal.
- for anim in animations.iter_mut() {
- if let Animation::Keyframes(_, _, ref anim_name, ref mut anim_state) = *anim {
- if *name == *anim_name {
- debug!("update_animation_state: Found other animation {}", name);
- anim_state.update_from_other(&state, timer);
- should_push = false;
- break;
- }
- }
- }
- }
- }
-
- if should_push {
- new_running_animations.push(animation);
- }
- }
-
- if running_animations.is_empty() && new_running_animations.is_empty() {
- // Nothing to do. Return early so we don't flood the compositor with
- // `ChangeRunningAnimationsState` messages.
- return;
- }
-
- let now = timer.seconds();
- // Expire old running animations.
- //
- // TODO: Do not expunge Keyframes animations, since we need that state if
- // the animation gets re-triggered. Probably worth splitting in two
- // different maps, or at least using a linked list?
- for (key, running_animations) in running_animations.iter_mut() {
- let mut animations_still_running = vec![];
- for mut running_animation in running_animations.drain(..) {
- let still_running = !running_animation.is_expired() &&
- match running_animation {
- Animation::Transition(_, started_at, ref frame) => {
- now < started_at + frame.duration
- },
- Animation::Keyframes(_, _, _, ref mut state) => {
- // This animation is still running, or we need to keep
- // iterating.
- now < state.started_at + state.duration || state.tick()
- },
- };
-
- debug!(
- "update_animation_state({:?}): {:?}",
- still_running, running_animation
- );
-
- if still_running {
- animations_still_running.push(running_animation);
- continue;
- }
-
- if let Animation::Transition(node, _, ref frame) = running_animation {
- script_chan
- .send(ConstellationControlMsg::TransitionEnd(
- node.to_untrusted_node_address(),
- frame.property_animation.property_name().into(),
- frame.duration,
- ))
- .unwrap();
- }
-
- expired_animations
- .entry(*key)
- .or_insert_with(Vec::new)
- .push(running_animation);
- }
-
- if animations_still_running.is_empty() {
- keys_to_remove.insert(*key);
- } else {
- *running_animations = animations_still_running
- }
- }
-
- for key in keys_to_remove {
- running_animations.remove(&key).unwrap();
- }
-
- // Add new running animations.
- for new_running_animation in new_running_animations {
- if new_running_animation.is_transition() {
- match newly_transitioning_nodes {
- Some(ref mut nodes) => {
- nodes.push(new_running_animation.node().to_untrusted_node_address());
- },
- None => {
- warn!("New transition encountered from compositor-initiated layout.");
- },
- }
- }
-
- running_animations
- .entry(*new_running_animation.node())
- .or_insert_with(Vec::new)
- .push(new_running_animation)
- }
-
- let animation_state = if running_animations.is_empty() {
- AnimationState::NoAnimationsPresent
- } else {
- AnimationState::AnimationsPresent
- };
-
- constellation_chan
- .send(ConstellationMsg::ChangeRunningAnimationsState(
- pipeline_id,
- animation_state,
- ))
- .unwrap();
-}
-
-/// Recalculates style for a set of animations. This does *not* run with the DOM
-/// lock held. Returns a set of nodes associated with animations that are no longer
-/// valid.
-pub fn recalc_style_for_animations<E>(
- context: &LayoutContext,
- flow: &mut dyn Flow,
- animations: &FxHashMap<OpaqueNode, Vec<Animation>>,
-) -> FxHashSet<OpaqueNode>
-where
- E: TElement,
-{
- let mut invalid_nodes = animations.keys().cloned().collect();
- do_recalc_style_for_animations::<E>(context, flow, animations, &mut invalid_nodes);
- invalid_nodes
-}
-
-fn do_recalc_style_for_animations<E>(
- context: &LayoutContext,
- flow: &mut dyn Flow,
- animations: &FxHashMap<OpaqueNode, Vec<Animation>>,
- invalid_nodes: &mut FxHashSet<OpaqueNode>,
-) where
- E: TElement,
-{
- let mut damage = RestyleDamage::empty();
- flow.mutate_fragments(&mut |fragment| {
- if let Some(ref animations) = animations.get(&fragment.node) {
- invalid_nodes.remove(&fragment.node);
- for animation in animations.iter() {
- let old_style = fragment.style.clone();
- update_style_for_animation::<E>(
- &context.style_context,
- animation,
- &mut fragment.style,
- &ServoMetricsProvider,
- );
- let difference =
- RestyleDamage::compute_style_difference(&old_style, &fragment.style);
- damage |= difference.damage;
- }
- }
- });
-
- let base = flow.mut_base();
- base.restyle_damage.insert(damage);
- for kid in base.children.iter_mut() {
- do_recalc_style_for_animations::<E>(context, kid, animations, invalid_nodes)
- }
-}
diff --git a/components/layout_2020/block.rs b/components/layout_2020/block.rs
deleted file mode 100644
index 24128ca2ae7..00000000000
--- a/components/layout_2020/block.rs
+++ /dev/null
@@ -1,3697 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Layout for CSS block-level elements.
-//!
-//! As a terminology note, the term *absolute positioning* here refers to elements with position
-//! `absolute` or `fixed`. The term *positioned element* refers to elements with position
-//! `relative`, `absolute`, and `fixed`. The term *containing block* (occasionally abbreviated as
-//! *CB*) is the containing block for the current flow, which differs from the static containing
-//! block if the flow is absolutely-positioned.
-//!
-//! "CSS 2.1" or "CSS 2.2" refers to the editor's draft of the W3C "Cascading Style Sheets Level 2
-//! Revision 2 (CSS 2.2) Specification" available here:
-//!
-//! http://dev.w3.org/csswg/css2/
-//!
-//! "INTRINSIC" refers to L. David Baron's "More Precise Definitions of Inline Layout and Table
-//! Layout" available here:
-//!
-//! http://dbaron.org/css/intrinsic/
-//!
-//! "CSS-SIZING" refers to the W3C "CSS Intrinsic & Extrinsic Sizing Module Level 3" document
-//! available here:
-//!
-//! http://dev.w3.org/csswg/css-sizing/
-
-use crate::context::LayoutContext;
-use crate::display_list::items::DisplayListSection;
-use crate::display_list::{
- BorderPaintingMode, DisplayListBuildState, StackingContextCollectionFlags,
- StackingContextCollectionState,
-};
-use crate::floats::{ClearType, FloatKind, Floats, PlacementInfo};
-use crate::flow::{
- BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ForceNonfloatedFlag, GetBaseFlow,
-};
-use crate::flow::{
- FlowFlags, FragmentationContext, ImmutableFlowUtils, LateAbsolutePositionInfo, OpaqueFlow,
-};
-use crate::flow_list::FlowList;
-use crate::fragment::{
- CoordinateSystem, Fragment, FragmentBorderBoxIterator, FragmentFlags, Overflow,
-};
-use crate::incremental::RelayoutMode;
-use crate::layout_debug;
-use crate::model::{
- AdjoiningMargins, CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto,
-};
-use crate::sequential;
-use crate::traversal::PreorderFlowTraversal;
-use app_units::{Au, MAX_AU};
-use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
-use gfx_traits::print_tree::PrintTree;
-use serde::{Serialize, Serializer};
-use servo_geometry::MaxRect;
-use std::cmp::{max, min};
-use std::fmt;
-use std::sync::Arc;
-use style::computed_values::box_sizing::T as BoxSizing;
-use style::computed_values::display::T as Display;
-use style::computed_values::float::T as Float;
-use style::computed_values::overflow_x::T as StyleOverflow;
-use style::computed_values::position::T as Position;
-use style::computed_values::text_align::T as TextAlign;
-use style::context::SharedStyleContext;
-use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect, LogicalSize, WritingMode};
-use style::properties::ComputedValues;
-use style::servo::restyle_damage::ServoRestyleDamage;
-use style::values::computed::{LengthPercentageOrAuto, MaxSize, Size};
-
-/// Information specific to floated blocks.
-#[derive(Clone, Serialize)]
-pub struct FloatedBlockInfo {
- /// The amount of inline size that is available for the float.
- pub containing_inline_size: Au,
-
- /// The float ceiling, relative to `BaseFlow::position::cur_b` (i.e. the top part of the border
- /// box).
- pub float_ceiling: Au,
-
- /// Left or right?
- pub float_kind: FloatKind,
-}
-
-impl FloatedBlockInfo {
- pub fn new(float_kind: FloatKind) -> FloatedBlockInfo {
- FloatedBlockInfo {
- containing_inline_size: Au(0),
- float_ceiling: Au(0),
- float_kind: float_kind,
- }
- }
-}
-
-/// The solutions for the block-size-and-margins constraint equation.
-#[derive(Clone, Copy)]
-struct BSizeConstraintSolution {
- block_start: Au,
- block_size: Au,
- margin_block_start: Au,
- margin_block_end: Au,
-}
-
-impl BSizeConstraintSolution {
- fn new(
- block_start: Au,
- block_size: Au,
- margin_block_start: Au,
- margin_block_end: Au,
- ) -> BSizeConstraintSolution {
- BSizeConstraintSolution {
- block_start: block_start,
- block_size: block_size,
- margin_block_start: margin_block_start,
- margin_block_end: margin_block_end,
- }
- }
-
- /// Solve the vertical constraint equation for absolute non-replaced elements.
- ///
- /// CSS Section 10.6.4
- /// Constraint equation:
- /// block-start + block-end + block-size + margin-block-start + margin-block-end
- /// = absolute containing block block-size - (vertical padding and border)
- /// [aka available_block-size]
- ///
- /// Return the solution for the equation.
- fn solve_vertical_constraints_abs_nonreplaced(
- block_size: MaybeAuto,
- block_start_margin: MaybeAuto,
- block_end_margin: MaybeAuto,
- block_start: MaybeAuto,
- block_end: MaybeAuto,
- content_block_size: Au,
- available_block_size: Au,
- ) -> BSizeConstraintSolution {
- let (block_start, block_size, margin_block_start, margin_block_end) =
- match (block_start, block_end, block_size) {
- (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Auto) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- // Now it is the same situation as block-start Specified and block-end
- // and block-size Auto.
- let block_size = content_block_size;
- // Use a dummy value for `block_start`, since it has the static position.
- (Au(0), block_size, margin_block_start, margin_block_end)
- },
- (
- MaybeAuto::Specified(block_start),
- MaybeAuto::Specified(block_end),
- MaybeAuto::Specified(block_size),
- ) => {
- match (block_start_margin, block_end_margin) {
- (MaybeAuto::Auto, MaybeAuto::Auto) => {
- let total_margin_val =
- available_block_size - block_start - block_end - block_size;
- (
- block_start,
- block_size,
- total_margin_val.scale_by(0.5),
- total_margin_val.scale_by(0.5),
- )
- },
- (MaybeAuto::Specified(margin_block_start), MaybeAuto::Auto) => {
- let sum = block_start + block_end + block_size + margin_block_start;
- (
- block_start,
- block_size,
- margin_block_start,
- available_block_size - sum,
- )
- },
- (MaybeAuto::Auto, MaybeAuto::Specified(margin_block_end)) => {
- let sum = block_start + block_end + block_size + margin_block_end;
- (
- block_start,
- block_size,
- available_block_size - sum,
- margin_block_end,
- )
- },
- (
- MaybeAuto::Specified(margin_block_start),
- MaybeAuto::Specified(margin_block_end),
- ) => {
- // Values are over-constrained. Ignore value for 'block-end'.
- (
- block_start,
- block_size,
- margin_block_start,
- margin_block_end,
- )
- },
- }
- },
-
- // For the rest of the cases, auto values for margin are set to 0
-
- // If only one is Auto, solve for it
- (
- MaybeAuto::Auto,
- MaybeAuto::Specified(block_end),
- MaybeAuto::Specified(block_size),
- ) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let sum = block_end + block_size + margin_block_start + margin_block_end;
- (
- available_block_size - sum,
- block_size,
- margin_block_start,
- margin_block_end,
- )
- },
- (
- MaybeAuto::Specified(block_start),
- MaybeAuto::Auto,
- MaybeAuto::Specified(block_size),
- ) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- (
- block_start,
- block_size,
- margin_block_start,
- margin_block_end,
- )
- },
- (
- MaybeAuto::Specified(block_start),
- MaybeAuto::Specified(block_end),
- MaybeAuto::Auto,
- ) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let sum = block_start + block_end + margin_block_start + margin_block_end;
- (
- block_start,
- available_block_size - sum,
- margin_block_start,
- margin_block_end,
- )
- },
-
- // If block-size is auto, then block-size is content block-size. Solve for the
- // non-auto value.
- (MaybeAuto::Specified(block_start), MaybeAuto::Auto, MaybeAuto::Auto) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let block_size = content_block_size;
- (
- block_start,
- block_size,
- margin_block_start,
- margin_block_end,
- )
- },
- (MaybeAuto::Auto, MaybeAuto::Specified(block_end), MaybeAuto::Auto) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let block_size = content_block_size;
- let sum = block_end + block_size + margin_block_start + margin_block_end;
- (
- available_block_size - sum,
- block_size,
- margin_block_start,
- margin_block_end,
- )
- },
-
- (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Specified(block_size)) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- // Use a dummy value for `block_start`, since it has the static position.
- (Au(0), block_size, margin_block_start, margin_block_end)
- },
- };
-
- BSizeConstraintSolution::new(
- block_start,
- block_size,
- margin_block_start,
- margin_block_end,
- )
- }
-
- /// Solve the vertical constraint equation for absolute replaced elements.
- ///
- /// Assumption: The used value for block-size has already been calculated.
- ///
- /// CSS Section 10.6.5
- /// Constraint equation:
- /// block-start + block-end + block-size + margin-block-start + margin-block-end
- /// = absolute containing block block-size - (vertical padding and border)
- /// [aka available block-size]
- ///
- /// Return the solution for the equation.
- fn solve_vertical_constraints_abs_replaced(
- block_size: Au,
- block_start_margin: MaybeAuto,
- block_end_margin: MaybeAuto,
- block_start: MaybeAuto,
- block_end: MaybeAuto,
- _: Au,
- available_block_size: Au,
- ) -> BSizeConstraintSolution {
- let (block_start, block_size, margin_block_start, margin_block_end) =
- match (block_start, block_end) {
- (MaybeAuto::Auto, MaybeAuto::Auto) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- // Use a dummy value for `block_start`, since it has the static position.
- (Au(0), block_size, margin_block_start, margin_block_end)
- },
- (MaybeAuto::Specified(block_start), MaybeAuto::Specified(block_end)) => {
- match (block_start_margin, block_end_margin) {
- (MaybeAuto::Auto, MaybeAuto::Auto) => {
- let total_margin_val =
- available_block_size - block_start - block_end - block_size;
- (
- block_start,
- block_size,
- total_margin_val.scale_by(0.5),
- total_margin_val.scale_by(0.5),
- )
- },
- (MaybeAuto::Specified(margin_block_start), MaybeAuto::Auto) => {
- let sum = block_start + block_end + block_size + margin_block_start;
- (
- block_start,
- block_size,
- margin_block_start,
- available_block_size - sum,
- )
- },
- (MaybeAuto::Auto, MaybeAuto::Specified(margin_block_end)) => {
- let sum = block_start + block_end + block_size + margin_block_end;
- (
- block_start,
- block_size,
- available_block_size - sum,
- margin_block_end,
- )
- },
- (
- MaybeAuto::Specified(margin_block_start),
- MaybeAuto::Specified(margin_block_end),
- ) => {
- // Values are over-constrained. Ignore value for 'block-end'.
- (
- block_start,
- block_size,
- margin_block_start,
- margin_block_end,
- )
- },
- }
- },
-
- // If only one is Auto, solve for it
- (MaybeAuto::Auto, MaybeAuto::Specified(block_end)) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let sum = block_end + block_size + margin_block_start + margin_block_end;
- (
- available_block_size - sum,
- block_size,
- margin_block_start,
- margin_block_end,
- )
- },
- (MaybeAuto::Specified(block_start), MaybeAuto::Auto) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- (
- block_start,
- block_size,
- margin_block_start,
- margin_block_end,
- )
- },
- };
- BSizeConstraintSolution::new(
- block_start,
- block_size,
- margin_block_start,
- margin_block_end,
- )
- }
-}
-
-/// Performs block-size calculations potentially multiple times, taking
-/// (assuming an horizontal writing mode) `height`, `min-height`, and `max-height`
-/// into account. After each call to `next()`, the caller must call `.try()` with the
-/// current calculated value of `height`.
-///
-/// See CSS 2.1 § 10.7.
-pub struct CandidateBSizeIterator {
- block_size: MaybeAuto,
- max_block_size: Option<Au>,
- min_block_size: Au,
- pub candidate_value: Au,
- status: CandidateBSizeIteratorStatus,
-}
-
-impl CandidateBSizeIterator {
- /// Creates a new candidate block-size iterator. `block_container_block-size` is `None` if the block-size
- /// of the block container has not been determined yet. It will always be `Some` in the case of
- /// absolutely-positioned containing blocks.
- pub fn new(
- fragment: &Fragment,
- block_container_block_size: Option<Au>,
- ) -> CandidateBSizeIterator {
- // Per CSS 2.1 § 10.7, (assuming an horizontal writing mode,)
- // percentages in `min-height` and `max-height` refer to the height of
- // the containing block.
- // If that is not determined yet by the time we need to resolve
- // `min-height` and `max-height`, percentage values are ignored.
-
- let block_size = match fragment.style.content_block_size() {
- Size::Auto => MaybeAuto::Auto,
- Size::LengthPercentage(ref lp) => {
- MaybeAuto::from_option(lp.maybe_to_used_value(block_container_block_size))
- },
- };
-
- let max_block_size = match fragment.style.max_block_size() {
- MaxSize::None => None,
- MaxSize::LengthPercentage(ref lp) => lp.maybe_to_used_value(block_container_block_size),
- };
-
- let min_block_size = match fragment.style.min_block_size() {
- Size::Auto => MaybeAuto::Auto,
- Size::LengthPercentage(ref lp) => {
- MaybeAuto::from_option(lp.maybe_to_used_value(block_container_block_size))
- },
- }
- .specified_or_zero();
-
- // If the style includes `box-sizing: border-box`, subtract the border and padding.
- let adjustment_for_box_sizing = match fragment.style.get_position().box_sizing {
- BoxSizing::BorderBox => fragment.border_padding.block_start_end(),
- BoxSizing::ContentBox => Au(0),
- };
-
- return CandidateBSizeIterator {
- block_size: block_size.map(|size| adjust(size, adjustment_for_box_sizing)),
- max_block_size: max_block_size.map(|size| adjust(size, adjustment_for_box_sizing)),
- min_block_size: adjust(min_block_size, adjustment_for_box_sizing),
- candidate_value: Au(0),
- status: CandidateBSizeIteratorStatus::Initial,
- };
-
- fn adjust(size: Au, delta: Au) -> Au {
- max(size - delta, Au(0))
- }
- }
-}
-
-impl Iterator for CandidateBSizeIterator {
- type Item = MaybeAuto;
- fn next(&mut self) -> Option<MaybeAuto> {
- self.status = match self.status {
- CandidateBSizeIteratorStatus::Initial => CandidateBSizeIteratorStatus::Trying,
- CandidateBSizeIteratorStatus::Trying => match self.max_block_size {
- Some(max_block_size) if self.candidate_value > max_block_size => {
- CandidateBSizeIteratorStatus::TryingMax
- },
- _ if self.candidate_value < self.min_block_size => {
- CandidateBSizeIteratorStatus::TryingMin
- },
- _ => CandidateBSizeIteratorStatus::Found,
- },
- CandidateBSizeIteratorStatus::TryingMax => {
- if self.candidate_value < self.min_block_size {
- CandidateBSizeIteratorStatus::TryingMin
- } else {
- CandidateBSizeIteratorStatus::Found
- }
- },
- CandidateBSizeIteratorStatus::TryingMin | CandidateBSizeIteratorStatus::Found => {
- CandidateBSizeIteratorStatus::Found
- },
- };
-
- match self.status {
- CandidateBSizeIteratorStatus::Trying => Some(self.block_size),
- CandidateBSizeIteratorStatus::TryingMax => {
- Some(MaybeAuto::Specified(self.max_block_size.unwrap()))
- },
- CandidateBSizeIteratorStatus::TryingMin => {
- Some(MaybeAuto::Specified(self.min_block_size))
- },
- CandidateBSizeIteratorStatus::Found => None,
- CandidateBSizeIteratorStatus::Initial => panic!(),
- }
- }
-}
-
-enum CandidateBSizeIteratorStatus {
- Initial,
- Trying,
- TryingMax,
- TryingMin,
- Found,
-}
-
-// A helper function used in block-size calculation.
-fn translate_including_floats(cur_b: &mut Au, delta: Au, floats: &mut Floats) {
- *cur_b = *cur_b + delta;
- let writing_mode = floats.writing_mode;
- floats.translate(LogicalSize::new(writing_mode, Au(0), -delta));
-}
-
-/// The real assign-block-sizes traversal for flows with position 'absolute'.
-///
-/// This is a traversal of an Absolute Flow tree.
-/// - Relatively positioned flows and the Root flow start new Absolute flow trees.
-/// - The kids of a flow in this tree will be the flows for which it is the
-/// absolute Containing Block.
-/// - Thus, leaf nodes and inner non-root nodes are all Absolute Flows.
-///
-/// A Flow tree can have several Absolute Flow trees (depending on the number
-/// of relatively positioned flows it has).
-///
-/// Note that flows with position 'fixed' just form a flat list as they all
-/// have the Root flow as their CB.
-pub struct AbsoluteAssignBSizesTraversal<'a>(pub &'a SharedStyleContext<'a>);
-
-impl<'a> PreorderFlowTraversal for AbsoluteAssignBSizesTraversal<'a> {
- #[inline]
- fn process(&self, flow: &mut dyn Flow) {
- if !flow.is_block_like() {
- return;
- }
-
- // This flow might not be an absolutely positioned flow if it is the root of the tree.
- let block = flow.as_mut_block();
- if !block
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- return;
- }
-
- if !block
- .base
- .restyle_damage
- .intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW)
- {
- return;
- }
-
- block.calculate_absolute_block_size_and_margins(self.0);
- }
-}
-
-pub enum BlockType {
- Replaced,
- NonReplaced,
- AbsoluteReplaced,
- AbsoluteNonReplaced,
- FloatReplaced,
- FloatNonReplaced,
- InlineBlockReplaced,
- InlineBlockNonReplaced,
- InlineFlexItem,
-}
-
-#[derive(Clone, PartialEq)]
-pub enum MarginsMayCollapseFlag {
- MarginsMayCollapse,
- MarginsMayNotCollapse,
-}
-
-#[derive(Debug, PartialEq)]
-pub enum FormattingContextType {
- None,
- Block,
- Other,
-}
-
-#[allow(unsafe_code)]
-unsafe impl crate::flow::HasBaseFlow for BlockFlow {}
-
-// A block formatting context.
-#[derive(Serialize)]
-#[repr(C)]
-pub struct BlockFlow {
- /// Data common to all flows.
- pub base: BaseFlow,
-
- /// The associated fragment.
- pub fragment: Fragment,
-
- /// Additional floating flow members.
- pub float: Option<Box<FloatedBlockInfo>>,
-
- /// Various flags.
- flags: BlockFlowFlags,
-}
-
-bitflags! {
- struct 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;
- }
-}
-
-impl Serialize for BlockFlowFlags {
- fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
- self.bits().serialize(serializer)
- }
-}
-
-impl BlockFlow {
- pub fn from_fragment(fragment: Fragment) -> BlockFlow {
- BlockFlow::from_fragment_and_float_kind(fragment, None)
- }
-
- pub fn from_fragment_and_float_kind(
- fragment: Fragment,
- float_kind: Option<FloatKind>,
- ) -> BlockFlow {
- let writing_mode = fragment.style().writing_mode;
- BlockFlow {
- base: BaseFlow::new(
- Some(fragment.style()),
- writing_mode,
- match float_kind {
- Some(_) => ForceNonfloatedFlag::FloatIfNecessary,
- None => ForceNonfloatedFlag::ForceNonfloated,
- },
- ),
- fragment: fragment,
- float: float_kind.map(|kind| Box::new(FloatedBlockInfo::new(kind))),
- flags: BlockFlowFlags::empty(),
- }
- }
-
- /// Return the type of this block.
- ///
- /// This determines the algorithm used to calculate inline-size, block-size, and the
- /// relevant margins for this Block.
- pub fn block_type(&self) -> BlockType {
- if self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- if self.fragment.is_replaced() {
- BlockType::AbsoluteReplaced
- } else {
- BlockType::AbsoluteNonReplaced
- }
- } else if self.is_inline_flex_item() {
- BlockType::InlineFlexItem
- } else if self.base.flags.is_float() {
- if self.fragment.is_replaced() {
- BlockType::FloatReplaced
- } else {
- BlockType::FloatNonReplaced
- }
- } else if self.is_inline_block_or_inline_flex() {
- if self.fragment.is_replaced() {
- BlockType::InlineBlockReplaced
- } else {
- BlockType::InlineBlockNonReplaced
- }
- } else {
- if self.fragment.is_replaced() {
- BlockType::Replaced
- } else {
- BlockType::NonReplaced
- }
- }
- }
-
- /// Compute the actual inline size and position for this block.
- pub fn compute_used_inline_size(
- &mut self,
- shared_context: &SharedStyleContext,
- containing_block_inline_size: Au,
- ) {
- let block_type = self.block_type();
- match block_type {
- BlockType::AbsoluteReplaced => {
- let inline_size_computer = AbsoluteReplaced;
- inline_size_computer.compute_used_inline_size(
- self,
- shared_context,
- containing_block_inline_size,
- );
- },
- BlockType::AbsoluteNonReplaced => {
- let inline_size_computer = AbsoluteNonReplaced;
- inline_size_computer.compute_used_inline_size(
- self,
- shared_context,
- containing_block_inline_size,
- );
- },
- BlockType::FloatReplaced => {
- let inline_size_computer = FloatReplaced;
- inline_size_computer.compute_used_inline_size(
- self,
- shared_context,
- containing_block_inline_size,
- );
- },
- BlockType::FloatNonReplaced => {
- let inline_size_computer = FloatNonReplaced;
- inline_size_computer.compute_used_inline_size(
- self,
- shared_context,
- containing_block_inline_size,
- );
- },
- BlockType::InlineBlockReplaced => {
- let inline_size_computer = InlineBlockReplaced;
- inline_size_computer.compute_used_inline_size(
- self,
- shared_context,
- containing_block_inline_size,
- );
- },
- BlockType::InlineBlockNonReplaced => {
- let inline_size_computer = InlineBlockNonReplaced;
- inline_size_computer.compute_used_inline_size(
- self,
- shared_context,
- containing_block_inline_size,
- );
- },
- BlockType::Replaced => {
- let inline_size_computer = BlockReplaced;
- inline_size_computer.compute_used_inline_size(
- self,
- shared_context,
- containing_block_inline_size,
- );
- },
- BlockType::NonReplaced => {
- let inline_size_computer = BlockNonReplaced;
- inline_size_computer.compute_used_inline_size(
- self,
- shared_context,
- containing_block_inline_size,
- );
- },
- BlockType::InlineFlexItem => {
- let inline_size_computer = InlineFlexItem;
- inline_size_computer.compute_used_inline_size(
- self,
- shared_context,
- containing_block_inline_size,
- );
- },
- }
- }
-
- /// Return this flow's fragment.
- pub fn fragment(&mut self) -> &mut Fragment {
- &mut self.fragment
- }
-
- pub fn stacking_relative_border_box(&self, coor: CoordinateSystem) -> Rect<Au> {
- return self.fragment.stacking_relative_border_box(
- &self.base.stacking_relative_position,
- &self
- .base
- .early_absolute_position_info
- .relative_containing_block_size,
- self.base
- .early_absolute_position_info
- .relative_containing_block_mode,
- coor,
- );
- }
-
- /// Return the size of the containing block for the given immediate absolute descendant of this
- /// flow.
- ///
- /// Right now, this only gets the containing block size for absolutely positioned elements.
- /// Note: We assume this is called in a top-down traversal, so it is ok to reference the CB.
- #[inline]
- pub fn containing_block_size(
- &self,
- viewport_size: &Size2D<Au>,
- descendant: OpaqueFlow,
- ) -> LogicalSize<Au> {
- debug_assert!(self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED));
- if self.is_fixed() || self.is_root() {
- // Initial containing block is the CB for the root
- LogicalSize::from_physical(self.base.writing_mode, *viewport_size)
- } else {
- self.base
- .absolute_cb
- .generated_containing_block_size(descendant)
- }
- }
-
- /// Return shrink-to-fit inline-size.
- ///
- /// This is where we use the preferred inline-sizes and minimum inline-sizes
- /// calculated in the bubble-inline-sizes traversal.
- pub fn get_shrink_to_fit_inline_size(&self, available_inline_size: Au) -> Au {
- let content_intrinsic_inline_sizes = self.content_intrinsic_inline_sizes();
- min(
- content_intrinsic_inline_sizes.preferred_inline_size,
- max(
- content_intrinsic_inline_sizes.minimum_inline_size,
- available_inline_size,
- ),
- )
- }
-
- /// If this is the root flow, shifts all kids down and adjusts our size to account for
- /// root flow margins, which should never be collapsed according to CSS § 8.3.1.
- ///
- /// TODO(#2017, pcwalton): This is somewhat inefficient (traverses kids twice); can we do
- /// better?
- fn adjust_fragments_for_collapsed_margins_if_root(
- &mut self,
- shared_context: &SharedStyleContext,
- ) {
- if !self.is_root() {
- return;
- }
-
- let (block_start_margin_value, block_end_margin_value) = match self.base.collapsible_margins
- {
- CollapsibleMargins::CollapseThrough(_) => {
- panic!("Margins unexpectedly collapsed through root flow.")
- },
- CollapsibleMargins::Collapse(block_start_margin, block_end_margin) => {
- (block_start_margin.collapse(), block_end_margin.collapse())
- },
- CollapsibleMargins::None(block_start, block_end) => (block_start, block_end),
- };
-
- // Shift all kids down (or up, if margins are negative) if necessary.
- if block_start_margin_value != Au(0) {
- for kid in self.base.child_iter_mut() {
- let kid_base = kid.mut_base();
- kid_base.position.start.b = kid_base.position.start.b + block_start_margin_value
- }
- }
-
- // FIXME(#2003, pcwalton): The max is taken here so that you can scroll the page, but this
- // is not correct behavior according to CSS 2.1 § 10.5. Instead I think we should treat the
- // root element as having `overflow: scroll` and use the layers-based scrolling
- // infrastructure to make it scrollable.
- let viewport_size = LogicalSize::from_physical(
- self.fragment.style.writing_mode,
- shared_context.viewport_size(),
- );
- let block_size = max(
- viewport_size.block,
- self.fragment.border_box.size.block + block_start_margin_value + block_end_margin_value,
- );
-
- self.base.position.size.block = block_size;
- self.fragment.border_box.size.block = block_size;
- }
-
- // FIXME: Record enough info to deal with fragmented decorations.
- // See https://drafts.csswg.org/css-break/#break-decoration
- // For borders, this might be `enum FragmentPosition { First, Middle, Last }`
- fn clone_with_children(&self, new_children: FlowList) -> BlockFlow {
- BlockFlow {
- base: self.base.clone_with_children(new_children),
- fragment: self.fragment.clone(),
- float: self.float.clone(),
- ..*self
- }
- }
-
- /// Writes in the size of the relative containing block for children. (This information
- /// is also needed to handle RTL.)
- fn propagate_early_absolute_position_info_to_children(&mut self) {
- for kid in self.base.child_iter_mut() {
- kid.mut_base().early_absolute_position_info = EarlyAbsolutePositionInfo {
- relative_containing_block_size: self.fragment.content_box().size,
- relative_containing_block_mode: self.fragment.style().writing_mode,
- }
- }
- }
-
- /// Assign block-size for current flow.
- ///
- /// * Collapse margins for flow's children and set in-flow child flows' block offsets now that
- /// we know their block-sizes.
- /// * Calculate and set the block-size of the current flow.
- /// * Calculate block-size, vertical margins, and block offset for the flow's box using CSS §
- /// 10.6.7.
- ///
- /// For absolute flows, we store the calculated content block-size for the flow. We defer the
- /// calculation of the other values until a later traversal.
- ///
- /// When `fragmentation_context` is given (not `None`), this should fit as much of the content
- /// as possible within the available block size.
- /// If there is more content (that doesn’t fit), this flow is *fragmented*
- /// with the extra content moved to another fragment (a flow like this one) which is returned.
- /// See `Flow::fragment`.
- ///
- /// The return value is always `None` when `fragmentation_context` is `None`.
- ///
- /// `inline(always)` because this is only ever called by in-order or non-in-order top-level
- /// methods.
- #[inline(always)]
- pub fn assign_block_size_block_base(
- &mut self,
- layout_context: &LayoutContext,
- mut fragmentation_context: Option<FragmentationContext>,
- margins_may_collapse: MarginsMayCollapseFlag,
- ) -> Option<Arc<dyn Flow>> {
- let _scope = layout_debug_scope!("assign_block_size_block_base {:x}", self.base.debug_id());
-
- let mut break_at = None;
- let content_box = self.fragment.content_box();
- if self
- .base
- .restyle_damage
- .contains(ServoRestyleDamage::REFLOW)
- {
- // Our current border-box position.
- let mut cur_b = Au(0);
-
- // Absolute positioning establishes a block formatting context. Don't propagate floats
- // in or out. (But do propagate them between kids.)
- if self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) ||
- margins_may_collapse != MarginsMayCollapseFlag::MarginsMayCollapse
- {
- self.base.floats = Floats::new(self.fragment.style.writing_mode);
- }
-
- let writing_mode = self.base.floats.writing_mode;
- self.base.floats.translate(LogicalSize::new(
- writing_mode,
- -self.fragment.inline_start_offset(),
- Au(0),
- ));
-
- // The sum of our block-start border and block-start padding.
- let block_start_offset = self.fragment.border_padding.block_start;
- translate_including_floats(&mut cur_b, block_start_offset, &mut self.base.floats);
-
- let can_collapse_block_start_margin_with_kids = margins_may_collapse ==
- MarginsMayCollapseFlag::MarginsMayCollapse &&
- !self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) &&
- self.fragment.border_padding.block_start == Au(0);
- let mut margin_collapse_info = MarginCollapseInfo::initialize_block_start_margin(
- &self.fragment,
- can_collapse_block_start_margin_with_kids,
- );
-
- // At this point, `cur_b` is at the content edge of our box. Now iterate over children.
- let mut floats = self.base.floats.clone();
- let thread_id = self.base.thread_id;
- let (mut had_floated_children, mut had_children_with_clearance) = (false, false);
- for (child_index, kid) in self.base.child_iter_mut().enumerate() {
- if kid
- .base()
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- // Assume that the *hypothetical box* for an absolute flow starts immediately
- // after the margin-end border edge of the previous flow.
- if kid
- .base()
- .flags
- .contains(FlowFlags::BLOCK_POSITION_IS_STATIC)
- {
- let previous_bottom_margin = margin_collapse_info.current_float_ceiling();
-
- kid.mut_base().position.start.b = cur_b +
- kid.base()
- .collapsible_margins
- .block_start_margin_for_noncollapsible_context() +
- previous_bottom_margin
- }
- kid.place_float_if_applicable();
- if !kid.base().flags.is_float() {
- kid.assign_block_size_for_inorder_child_if_necessary(
- layout_context,
- thread_id,
- content_box,
- );
- }
-
- // Skip the collapsing and float processing for absolute flow kids and continue
- // with the next flow.
- continue;
- }
-
- let previous_b = cur_b;
- if let Some(ctx) = fragmentation_context {
- let child_ctx = FragmentationContext {
- available_block_size: ctx.available_block_size - cur_b,
- this_fragment_is_empty: ctx.this_fragment_is_empty,
- };
- if let Some(remaining) = kid.fragment(layout_context, Some(child_ctx)) {
- break_at = Some((child_index + 1, Some(remaining)));
- }
- }
-
- // Assign block-size now for the child if it might have floats in and we couldn't
- // before.
- kid.mut_base().floats = floats.clone();
- if kid.base().flags.is_float() {
- had_floated_children = true;
- kid.mut_base().position.start.b = cur_b;
- {
- let kid_block = kid.as_mut_block();
- let float_ceiling = margin_collapse_info.current_float_ceiling();
- kid_block.float.as_mut().unwrap().float_ceiling = float_ceiling
- }
- kid.place_float_if_applicable();
-
- let kid_base = kid.mut_base();
- floats = kid_base.floats.clone();
- continue;
- }
-
- // If we have clearance, assume there are no floats in.
- //
- // FIXME(#2008, pcwalton): This could be wrong if we have `clear: left` or `clear:
- // right` and there are still floats to impact, of course. But this gets
- // complicated with margin collapse. Possibly the right thing to do is to lay out
- // the block again in this rare case. (Note that WebKit can lay blocks out twice;
- // this may be related, although I haven't looked into it closely.)
- if kid.base().flags.clears_floats() {
- kid.mut_base().floats = Floats::new(self.fragment.style.writing_mode)
- }
-
- // Lay the child out if this was an in-order traversal.
- let need_to_process_child_floats = kid
- .assign_block_size_for_inorder_child_if_necessary(
- layout_context,
- thread_id,
- content_box,
- );
-
- if !had_children_with_clearance &&
- floats.is_present() &&
- (kid.base().flags.contains(FlowFlags::CLEARS_LEFT) ||
- kid.base().flags.contains(FlowFlags::CLEARS_RIGHT))
- {
- had_children_with_clearance = true
- }
-
- // Handle any (possibly collapsed) top margin.
- let delta = margin_collapse_info.advance_block_start_margin(
- &kid.base().collapsible_margins,
- !had_children_with_clearance,
- );
- translate_including_floats(&mut cur_b, delta, &mut floats);
-
- // Collapse-through margins should be placed at the top edge,
- // so we'll handle the delta after the bottom margin is processed
- if let CollapsibleMargins::CollapseThrough(_) = kid.base().collapsible_margins {
- cur_b = cur_b - delta;
- }
-
- // Clear past the floats that came in, if necessary.
- let clearance = match (
- kid.base().flags.contains(FlowFlags::CLEARS_LEFT),
- kid.base().flags.contains(FlowFlags::CLEARS_RIGHT),
- ) {
- (false, false) => Au(0),
- (true, false) => floats.clearance(ClearType::Left),
- (false, true) => floats.clearance(ClearType::Right),
- (true, true) => floats.clearance(ClearType::Both),
- };
- translate_including_floats(&mut cur_b, clearance, &mut floats);
-
- // At this point, `cur_b` is at the border edge of the child.
- kid.mut_base().position.start.b = cur_b;
-
- // Now pull out the child's outgoing floats. We didn't do this immediately after
- // the `assign_block_size_for_inorder_child_if_necessary` call because clearance on
- // a block operates on the floats that come *in*, not the floats that go *out*.
- if need_to_process_child_floats {
- floats = kid.mut_base().floats.clone()
- }
-
- // Move past the child's border box. Do not use the `translate_including_floats`
- // function here because the child has already translated floats past its border
- // box.
- let kid_base = kid.mut_base();
- cur_b = cur_b + kid_base.position.size.block;
-
- // Handle any (possibly collapsed) block-end margin.
- let delta =
- margin_collapse_info.advance_block_end_margin(&kid_base.collapsible_margins);
- translate_including_floats(&mut cur_b, delta, &mut floats);
-
- // Collapse-through margin should be placed at the top edge of the flow.
- let collapse_delta = match kid_base.collapsible_margins {
- CollapsibleMargins::CollapseThrough(_) => {
- let delta = margin_collapse_info.current_float_ceiling();
- cur_b = cur_b + delta;
- kid_base.position.start.b = kid_base.position.start.b + delta;
- delta
- },
- _ => Au(0),
- };
-
- if break_at.is_some() {
- break;
- }
-
- if let Some(ref mut ctx) = fragmentation_context {
- if cur_b > ctx.available_block_size && !ctx.this_fragment_is_empty {
- break_at = Some((child_index, None));
- cur_b = previous_b;
- break;
- }
- ctx.this_fragment_is_empty = false
- }
-
- // For consecutive collapse-through flows, their top margin should be calculated
- // from the same baseline.
- cur_b = cur_b - collapse_delta;
- }
-
- // Add in our block-end margin and compute our collapsible margins.
- let can_collapse_block_end_margin_with_kids = margins_may_collapse ==
- MarginsMayCollapseFlag::MarginsMayCollapse &&
- !self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) &&
- self.fragment.border_padding.block_end == Au(0);
- let (collapsible_margins, delta) = margin_collapse_info
- .finish_and_compute_collapsible_margins(
- &self.fragment,
- self.base.block_container_explicit_block_size,
- can_collapse_block_end_margin_with_kids,
- !had_floated_children,
- );
- self.base.collapsible_margins = collapsible_margins;
- translate_including_floats(&mut cur_b, delta, &mut floats);
-
- let mut block_size = cur_b - block_start_offset;
- let is_root = self.is_root();
-
- if is_root ||
- self.formatting_context_type() != FormattingContextType::None ||
- self.base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- // The content block-size includes all the floats per CSS 2.1 § 10.6.7. The easiest
- // way to handle this is to just treat it as clearance.
- block_size = block_size + floats.clearance(ClearType::Both);
- }
-
- if self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- // FIXME(#2003, pcwalton): The max is taken here so that you can scroll the page,
- // but this is not correct behavior according to CSS 2.1 § 10.5. Instead I think we
- // should treat the root element as having `overflow: scroll` and use the layers-
- // based scrolling infrastructure to make it scrollable.
- if is_root {
- let viewport_size = LogicalSize::from_physical(
- self.fragment.style.writing_mode,
- layout_context.shared_context().viewport_size(),
- );
- block_size = max(viewport_size.block, block_size)
- }
-
- // Store the content block-size for use in calculating the absolute flow's
- // dimensions later.
- //
- // FIXME(pcwalton): This looks not idempotent. Is it?
- self.fragment.border_box.size.block = block_size;
- }
-
- if self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- self.propagate_early_absolute_position_info_to_children();
- return None;
- }
-
- // Compute any explicitly-specified block size.
- // Can't use `for` because we assign to `candidate_block_size_iterator.candidate_value`.
- let mut candidate_block_size_iterator = CandidateBSizeIterator::new(
- &self.fragment,
- self.base.block_container_explicit_block_size,
- );
- while let Some(candidate_block_size) = candidate_block_size_iterator.next() {
- candidate_block_size_iterator.candidate_value = match candidate_block_size {
- MaybeAuto::Auto => block_size,
- MaybeAuto::Specified(value) => value,
- }
- }
-
- // Adjust `cur_b` as necessary to account for the explicitly-specified block-size.
- block_size = candidate_block_size_iterator.candidate_value;
- let delta = block_size - (cur_b - block_start_offset);
- translate_including_floats(&mut cur_b, delta, &mut floats);
-
- // Take border and padding into account.
- let block_end_offset = self.fragment.border_padding.block_end;
- translate_including_floats(&mut cur_b, block_end_offset, &mut floats);
-
- // Now that `cur_b` is at the block-end of the border box, compute the final border box
- // position.
- self.fragment.border_box.size.block = cur_b;
- self.fragment.border_box.start.b = Au(0);
- self.base.position.size.block = cur_b;
-
- self.propagate_early_absolute_position_info_to_children();
-
- // Translate the current set of floats back into the parent coordinate system in the
- // inline direction, and store them in the flow so that flows that come later in the
- // document can access them.
- floats.translate(LogicalSize::new(
- writing_mode,
- self.fragment.inline_start_offset(),
- Au(0),
- ));
- self.base.floats = floats;
- self.adjust_fragments_for_collapsed_margins_if_root(layout_context.shared_context());
- } else {
- // We don't need to reflow, but we still need to perform in-order traversals if
- // necessary.
- let thread_id = self.base.thread_id;
- for kid in self.base.child_iter_mut() {
- kid.assign_block_size_for_inorder_child_if_necessary(
- layout_context,
- thread_id,
- content_box,
- );
- }
- }
-
- if (&*self as &dyn Flow).contains_roots_of_absolute_flow_tree() {
- // Assign block-sizes for all flows in this absolute flow tree.
- // This is preorder because the block-size of an absolute flow may depend on
- // the block-size of its containing block, which may also be an absolute flow.
- let assign_abs_b_sizes = AbsoluteAssignBSizesTraversal(layout_context.shared_context());
- assign_abs_b_sizes.traverse_absolute_flows(&mut *self);
- }
-
- // Don't remove the dirty bits yet if we're absolutely-positioned, since our final size
- // has not been calculated yet. (See `calculate_absolute_block_size_and_margins` for that.)
- // Also don't remove the dirty bits if we're a block formatting context since our inline
- // size has not yet been computed. (See `assign_inline_position_for_formatting_context()`.)
- if (self.base.flags.is_float() ||
- self.formatting_context_type() == FormattingContextType::None) &&
- !self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- self.base
- .restyle_damage
- .remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
- self.fragment
- .restyle_damage
- .remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
- }
-
- break_at.and_then(|(i, child_remaining)| {
- if i == self.base.children.len() && child_remaining.is_none() {
- None
- } else {
- let mut children = self.base.children.split_off(i);
- if let Some(child) = child_remaining {
- children.push_front_arc(child);
- }
- Some(Arc::new(self.clone_with_children(children)) as Arc<dyn Flow>)
- }
- })
- }
-
- /// Add placement information about current float flow for use by the parent.
- ///
- /// Also, use information given by parent about other floats to find out our relative position.
- ///
- /// This does not give any information about any float descendants because they do not affect
- /// elements outside of the subtree rooted at this float.
- ///
- /// This function is called on a kid flow by a parent. Therefore, `assign_block_size_float` was
- /// already called on this kid flow by the traversal function. So, the values used are
- /// well-defined.
- pub fn place_float(&mut self) {
- let block_size = self.fragment.border_box.size.block;
- let clearance = match self.fragment.clear() {
- None => Au(0),
- Some(clear) => self.base.floats.clearance(clear),
- };
-
- let float_info: FloatedBlockInfo = (**self.float.as_ref().unwrap()).clone();
-
- // Our `position` field accounts for positive margins, but not negative margins. (See
- // calculation of `extra_inline_size_from_margin` below.) Negative margins must be taken
- // into account for float placement, however. So we add them in here.
- let inline_size_for_float_placement =
- self.base.position.size.inline + min(Au(0), self.fragment.margin.inline_start_end());
-
- let info = PlacementInfo {
- size: LogicalSize::new(
- self.fragment.style.writing_mode,
- inline_size_for_float_placement,
- block_size + self.fragment.margin.block_start_end(),
- )
- .convert(
- self.fragment.style.writing_mode,
- self.base.floats.writing_mode,
- ),
- ceiling: clearance + float_info.float_ceiling,
- max_inline_size: float_info.containing_inline_size,
- kind: float_info.float_kind,
- };
-
- // Place the float and return the `Floats` back to the parent flow.
- // After, grab the position and use that to set our position.
- self.base.floats.add_float(&info);
-
- // FIXME (mbrubeck) Get the correct container size for self.base.floats;
- let container_size = Size2D::new(self.base.block_container_inline_size, Au(0));
-
- // Move in from the margin edge, as per CSS 2.1 § 9.5, floats may not overlap anything on
- // their margin edges.
- let float_offset = self
- .base
- .floats
- .last_float_pos()
- .unwrap()
- .convert(
- self.base.floats.writing_mode,
- self.base.writing_mode,
- container_size,
- )
- .start;
- let margin_offset = LogicalPoint::new(
- self.base.writing_mode,
- Au(0),
- self.fragment.margin.block_start,
- );
-
- let mut origin = LogicalPoint::new(
- self.base.writing_mode,
- self.base.position.start.i,
- self.base.position.start.b,
- );
- origin = origin.add_point(&float_offset).add_point(&margin_offset);
- self.base.position =
- LogicalRect::from_point_size(self.base.writing_mode, origin, self.base.position.size);
- }
-
- pub fn explicit_block_containing_size(
- &self,
- shared_context: &SharedStyleContext,
- ) -> Option<Au> {
- if self.is_root() || self.is_fixed() {
- let viewport_size = LogicalSize::from_physical(
- self.fragment.style.writing_mode,
- shared_context.viewport_size(),
- );
- Some(viewport_size.block)
- } else if self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) &&
- self.base.block_container_explicit_block_size.is_none()
- {
- self.base
- .absolute_cb
- .explicit_block_containing_size(shared_context)
- } else {
- self.base.block_container_explicit_block_size
- }
- }
-
- pub fn explicit_block_size(&self, containing_block_size: Option<Au>) -> Option<Au> {
- let content_block_size = self.fragment.style().content_block_size();
-
- match content_block_size {
- Size::Auto => {
- let container_size = containing_block_size?;
- let (block_start, block_end) = {
- let position = self.fragment.style().logical_position();
- (
- MaybeAuto::from_style(position.block_start, container_size),
- MaybeAuto::from_style(position.block_end, container_size),
- )
- };
-
- match (block_start, block_end) {
- (MaybeAuto::Specified(block_start), MaybeAuto::Specified(block_end)) => {
- let available_block_size =
- container_size - self.fragment.border_padding.block_start_end();
-
- // Non-auto margin-block-start and margin-block-end values have already been
- // calculated during assign-inline-size.
- let margin = self.fragment.style().logical_margin();
- let margin_block_start = match margin.block_start {
- LengthPercentageOrAuto::Auto => MaybeAuto::Auto,
- _ => MaybeAuto::Specified(self.fragment.margin.block_start),
- };
- let margin_block_end = match margin.block_end {
- LengthPercentageOrAuto::Auto => MaybeAuto::Auto,
- _ => MaybeAuto::Specified(self.fragment.margin.block_end),
- };
-
- let margin_block_start = margin_block_start.specified_or_zero();
- let margin_block_end = margin_block_end.specified_or_zero();
- let sum = block_start + block_end + margin_block_start + margin_block_end;
- Some(available_block_size - sum)
- },
- (_, _) => None,
- }
- },
- Size::LengthPercentage(ref lp) => lp.maybe_to_used_value(containing_block_size),
- }
- }
-
- fn calculate_absolute_block_size_and_margins(&mut self, shared_context: &SharedStyleContext) {
- let opaque_self = OpaqueFlow::from_flow(self);
- let containing_block_block_size = self
- .containing_block_size(&shared_context.viewport_size(), opaque_self)
- .block;
-
- // This is the stored content block-size value from assign-block-size
- let content_block_size = self.fragment.border_box.size.block;
-
- let mut solution = None;
- {
- // Non-auto margin-block-start and margin-block-end values have already been
- // calculated during assign-inline-size.
- let margin = self.fragment.style().logical_margin();
- let margin_block_start = match margin.block_start {
- LengthPercentageOrAuto::Auto => MaybeAuto::Auto,
- _ => MaybeAuto::Specified(self.fragment.margin.block_start),
- };
- let margin_block_end = match margin.block_end {
- LengthPercentageOrAuto::Auto => MaybeAuto::Auto,
- _ => MaybeAuto::Specified(self.fragment.margin.block_end),
- };
-
- let block_start;
- let block_end;
- {
- let position = self.fragment.style().logical_position();
- block_start =
- MaybeAuto::from_style(position.block_start, containing_block_block_size);
- block_end = MaybeAuto::from_style(position.block_end, containing_block_block_size);
- }
-
- let available_block_size =
- containing_block_block_size - self.fragment.border_padding.block_start_end();
- 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();
- // 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.
- let block_size_used_val = self.fragment.border_box.size.block -
- self.fragment.border_padding.block_start_end();
- solution = Some(
- BSizeConstraintSolution::solve_vertical_constraints_abs_replaced(
- block_size_used_val,
- margin_block_start,
- margin_block_end,
- block_start,
- block_end,
- content_block_size,
- available_block_size,
- ),
- )
- } else {
- let mut candidate_block_size_iterator =
- CandidateBSizeIterator::new(&self.fragment, Some(containing_block_block_size));
-
- // Can't use `for` because we assign to
- // `candidate_block_size_iterator.candidate_value`.
- while let Some(block_size_used_val) = candidate_block_size_iterator.next() {
- solution = Some(
- BSizeConstraintSolution::solve_vertical_constraints_abs_nonreplaced(
- block_size_used_val,
- margin_block_start,
- margin_block_end,
- block_start,
- block_end,
- content_block_size,
- available_block_size,
- ),
- );
-
- candidate_block_size_iterator.candidate_value = solution.unwrap().block_size;
- }
- }
- }
-
- let solution = solution.unwrap();
- self.fragment.margin.block_start = solution.margin_block_start;
- self.fragment.margin.block_end = solution.margin_block_end;
- self.fragment.border_box.start.b = Au(0);
-
- if !self
- .base
- .flags
- .contains(FlowFlags::BLOCK_POSITION_IS_STATIC)
- {
- self.base.position.start.b = solution.block_start + self.fragment.margin.block_start
- }
-
- let block_size = solution.block_size + self.fragment.border_padding.block_start_end();
-
- self.fragment.border_box.size.block = block_size;
- self.base.position.size.block = block_size;
-
- self.base
- .restyle_damage
- .remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
- self.fragment
- .restyle_damage
- .remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
- }
-
- /// Compute inline size based using the `block_container_inline_size` set by the parent flow.
- ///
- /// This is run in the `AssignISizes` traversal.
- fn propagate_and_compute_used_inline_size(&mut self, shared_context: &SharedStyleContext) {
- let containing_block_inline_size = self.base.block_container_inline_size;
- self.compute_used_inline_size(shared_context, containing_block_inline_size);
- if self.base.flags.is_float() {
- self.float.as_mut().unwrap().containing_inline_size = containing_block_inline_size
- }
- }
-
- /// Assigns the computed inline-start content edge and inline-size to all the children of this
- /// block flow. The given `callback`, if supplied, will be called once per child; it is
- /// currently used to push down column sizes for tables.
- ///
- /// `#[inline(always)]` because this is called only from block or table inline-size assignment
- /// and the code for block layout is significantly simpler.
- #[inline(always)]
- pub fn propagate_assigned_inline_size_to_children<F>(
- &mut self,
- shared_context: &SharedStyleContext,
- inline_start_content_edge: Au,
- inline_end_content_edge: Au,
- content_inline_size: Au,
- mut callback: F,
- ) where
- F: FnMut(&mut dyn Flow, usize, Au, WritingMode, &mut Au, &mut Au),
- {
- let flags = self.base.flags.clone();
-
- let opaque_self = OpaqueFlow::from_flow(self);
-
- // Calculate non-auto block size to pass to children.
- let box_border = match self.fragment.style().get_position().box_sizing {
- BoxSizing::BorderBox => self.fragment.border_padding.block_start_end(),
- BoxSizing::ContentBox => Au(0),
- };
- let parent_container_size = self.explicit_block_containing_size(shared_context);
- // https://drafts.csswg.org/css-ui-3/#box-sizing
- let mut explicit_content_size = self.explicit_block_size(parent_container_size).map(|x| {
- if x < box_border {
- Au(0)
- } else {
- x - box_border
- }
- });
- if self.is_root() {
- explicit_content_size = max(parent_container_size, explicit_content_size);
- }
- // Calculate containing block inline size.
- let containing_block_size = if flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
- self.containing_block_size(&shared_context.viewport_size(), opaque_self)
- .inline
- } else {
- content_inline_size
- };
- // FIXME (mbrubeck): Get correct mode for absolute containing block
- let containing_block_mode = self.base.writing_mode;
-
- let mut inline_start_margin_edge = inline_start_content_edge;
- let mut inline_end_margin_edge = inline_end_content_edge;
-
- let mut iterator = self.base.child_iter_mut().enumerate().peekable();
- while let Some((i, kid)) = iterator.next() {
- kid.mut_base().block_container_explicit_block_size = explicit_content_size;
-
- // The inline-start margin edge of the child flow is at our inline-start content edge,
- // and its inline-size is our content inline-size.
- let kid_mode = kid.base().writing_mode;
- {
- // Don't assign positions to children unless they're going to be reflowed.
- // Otherwise, the position we assign might be incorrect and never fixed up. (Issue
- // #13704.)
- //
- // For instance, floats have their true inline position calculated in
- // `assign_block_size()`, which won't do anything unless `REFLOW` is set. So, if a
- // float child does not have `REFLOW` set, we must be careful to avoid touching its
- // inline position, as no logic will run afterward to set its true value.
- let kid_base = kid.mut_base();
- let reflow_damage = if kid_base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- ServoRestyleDamage::REFLOW_OUT_OF_FLOW
- } else {
- ServoRestyleDamage::REFLOW
- };
- if kid_base
- .flags
- .contains(FlowFlags::INLINE_POSITION_IS_STATIC) &&
- kid_base.restyle_damage.contains(reflow_damage)
- {
- kid_base.position.start.i =
- if kid_mode.is_bidi_ltr() == containing_block_mode.is_bidi_ltr() {
- inline_start_content_edge
- } else {
- // The kid's inline 'start' is at the parent's 'end'
- inline_end_content_edge
- };
- }
- kid_base.block_container_inline_size = content_inline_size;
- kid_base.block_container_writing_mode = containing_block_mode;
- }
-
- // Call the callback to propagate extra inline size information down to the child. This
- // is currently used for tables.
- callback(
- kid,
- i,
- content_inline_size,
- containing_block_mode,
- &mut inline_start_margin_edge,
- &mut inline_end_margin_edge,
- );
-
- // Per CSS 2.1 § 16.3.1, text alignment propagates to all children in flow.
- //
- // TODO(#2265, pcwalton): Do this in the cascade instead.
- let containing_block_text_align = self.fragment.style().get_inherited_text().text_align;
- kid.mut_base()
- .flags
- .set_text_align(containing_block_text_align);
-
- // Handle `text-indent` on behalf of any inline children that we have. This is
- // necessary because any percentages are relative to the containing block, which only
- // we know.
- if kid.is_inline_flow() {
- kid.as_mut_inline().first_line_indentation = self
- .fragment
- .style()
- .get_inherited_text()
- .text_indent
- .to_used_value(containing_block_size);
- }
- }
- }
-
- /// Determines the type of formatting context this is. See the definition of
- /// `FormattingContextType`.
- pub fn formatting_context_type(&self) -> FormattingContextType {
- if self.is_inline_flex_item() || self.is_block_flex_item() {
- return FormattingContextType::Other;
- }
- let style = self.fragment.style();
- if style.get_box().float != Float::None {
- return FormattingContextType::Other;
- }
- match style.get_box().display {
- Display::TableCell |
- Display::TableCaption |
- Display::TableRowGroup |
- Display::Table |
- Display::InlineBlock |
- Display::Flex => FormattingContextType::Other,
- _ if style.get_box().overflow_x != StyleOverflow::Visible ||
- style.get_box().overflow_y != StyleOverflow::Visible ||
- style.is_multicol() =>
- {
- FormattingContextType::Block
- },
- _ => FormattingContextType::None,
- }
- }
-
- /// Per CSS 2.1 § 9.5, block formatting contexts' inline widths and positions are affected by
- /// the presence of floats. This is the part of the assign-heights traversal that computes
- /// the final inline position and width for such flows.
- ///
- /// Note that this is part of the assign-block-sizes traversal, not the assign-inline-sizes
- /// traversal as one might expect. That is because, in general, float placement cannot occur
- /// until heights are assigned. To work around this unfortunate circular dependency, by the
- /// time we get here we have already estimated the width of the block formatting context based
- /// on the floats we could see at the time of inline-size assignment. The job of this function,
- /// therefore, is not only to assign the final size but also to perform the layout again for
- /// this block formatting context if our speculation was wrong.
- fn assign_inline_position_for_formatting_context(
- &mut self,
- layout_context: &LayoutContext,
- content_box: LogicalRect<Au>,
- ) {
- debug_assert_ne!(self.formatting_context_type(), FormattingContextType::None);
-
- if !self
- .base
- .restyle_damage
- .intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW)
- {
- return;
- }
-
- // We do this first to avoid recomputing our inline size when we propagate it.
- self.base
- .restyle_damage
- .remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
- self.fragment
- .restyle_damage
- .remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
-
- // The code below would completely wreck the layout if run on a flex item, however:
- // * Flex items are always the children of flex containers.
- // * Flex containers only contain flex items.
- // * Floats cannot intrude into flex containers.
- // * Floats cannot escape flex items.
- // * Flex items cannot also be floats.
- // Therefore, a flex item cannot be impacted by a float.
- // See also: https://www.w3.org/TR/css-flexbox-1/#flex-containers
- if !self.base.might_have_floats_in() {
- return;
- }
-
- // If you remove the might_have_floats_in conditional, this will go off.
- debug_assert!(!self.is_inline_flex_item());
-
- // Compute the available space for us, based on the actual floats.
- let rect = self.base.floats.available_rect(
- Au(0),
- self.fragment.border_box.size.block,
- content_box.size.inline,
- );
- let available_inline_size = if let Some(rect) = rect {
- // Offset our position by whatever displacement is needed to not impact the floats.
- // Also, account for margins sliding behind floats.
- let inline_offset = if self.fragment.margin.inline_start < rect.start.i {
- // Do not do anything for negative margins; those are handled separately.
- rect.start.i - max(Au(0), self.fragment.margin.inline_start)
- } else {
- Au(0)
- };
- self.base.position.start.i = content_box.start.i + inline_offset;
- // Handle the end margin sliding behind the float.
- let end = content_box.size.inline - rect.start.i - rect.size.inline;
- let inline_end_offset = if self.fragment.margin.inline_end < end {
- end - max(Au(0), self.fragment.margin.inline_end)
- } else {
- Au(0)
- };
- content_box.size.inline - inline_offset - inline_end_offset
- } else {
- content_box.size.inline
- } - self.fragment.margin.inline_start_end();
- let max_inline_size = self
- .fragment
- .style()
- .max_inline_size()
- .to_used_value(self.base.block_container_inline_size)
- .unwrap_or(MAX_AU);
- let min_inline_size = self
- .fragment
- .style()
- .min_inline_size()
- .to_used_value(self.base.block_container_inline_size)
- .unwrap_or(Au(0));
- let specified_inline_size = self.fragment.style().content_inline_size();
- let container_size = self.base.block_container_inline_size;
- let inline_size = match specified_inline_size.to_used_value(container_size) {
- Some(size) => match self.fragment.style().get_position().box_sizing {
- BoxSizing::BorderBox => size,
- BoxSizing::ContentBox => size + self.fragment.border_padding.inline_start_end(),
- },
- None => max(min_inline_size, min(available_inline_size, max_inline_size)),
- };
- self.base.position.size.inline = inline_size + self.fragment.margin.inline_start_end();
-
- // If float speculation failed, fixup our layout, and re-layout all the children.
- if self.fragment.margin_box_inline_size() != self.base.position.size.inline {
- debug!("assign_inline_position_for_formatting_context: float speculation failed");
- // Fix-up our own layout.
- // We can't just traverse_flow_tree_preorder ourself, because that would re-run
- // float speculation, instead of acting on the actual results.
- self.fragment.border_box.size.inline = inline_size;
- // Assign final-final inline sizes on all our children.
- self.assign_inline_sizes(layout_context);
- // Re-run layout on our children.
- for child in self.base.child_iter_mut() {
- sequential::reflow(child, layout_context, RelayoutMode::Force);
- }
- // Assign our final-final block size.
- self.assign_block_size(layout_context);
- }
-
- debug_assert_eq!(
- self.fragment.margin_box_inline_size(),
- self.base.position.size.inline
- );
- }
-
- fn is_inline_block_or_inline_flex(&self) -> bool {
- self.fragment.style().get_box().display == Display::InlineBlock ||
- self.fragment.style().get_box().display == Display::InlineFlex
- }
-
- /// Computes the content portion (only) of the intrinsic inline sizes of this flow. This is
- /// used for calculating shrink-to-fit width. Assumes that intrinsic sizes have already been
- /// computed for this flow.
- fn content_intrinsic_inline_sizes(&self) -> IntrinsicISizes {
- let (border_padding, margin) = self.fragment.surrounding_intrinsic_inline_size();
- IntrinsicISizes {
- minimum_inline_size: self.base.intrinsic_inline_sizes.minimum_inline_size -
- border_padding -
- margin,
- preferred_inline_size: self.base.intrinsic_inline_sizes.preferred_inline_size -
- border_padding -
- margin,
- }
- }
-
- /// Computes intrinsic inline sizes for a block.
- pub fn bubble_inline_sizes_for_block(&mut self, consult_children: bool) {
- let _scope = layout_debug_scope!("block::bubble_inline_sizes {:x}", self.base.debug_id());
-
- let mut flags = self.base.flags;
- if self.definitely_has_zero_block_size() {
- // This is kind of a hack for Acid2. But it's a harmless one, because (a) this behavior
- // is unspecified; (b) it matches the behavior one would intuitively expect, since
- // floats don't flow around blocks that take up no space in the block direction.
- flags.remove(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
- } else if self.fragment.is_text_or_replaced() {
- flags.insert(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
- } else {
- flags.remove(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
- for kid in self.base.children.iter() {
- if kid
- .base()
- .flags
- .contains(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS)
- {
- flags.insert(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
- break;
- }
- }
- }
-
- // Find the maximum inline-size from children.
- //
- // See: https://lists.w3.org/Archives/Public/www-style/2014Nov/0085.html
- //
- // FIXME(pcwalton): This doesn't exactly follow that algorithm at the moment.
- // FIXME(pcwalton): This should consider all float descendants, not just children.
- let mut computation = self.fragment.compute_intrinsic_inline_sizes();
- let (mut left_float_width, mut right_float_width) = (Au(0), Au(0));
- let (mut left_float_width_accumulator, mut right_float_width_accumulator) = (Au(0), Au(0));
- let mut preferred_inline_size_of_children_without_text_or_replaced_fragments = Au(0);
- for kid in self.base.child_iter_mut() {
- if kid
- .base()
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) ||
- !consult_children
- {
- continue;
- }
-
- let child_base = kid.mut_base();
- let float_kind = child_base.flags.float_kind();
- computation.content_intrinsic_sizes.minimum_inline_size = max(
- computation.content_intrinsic_sizes.minimum_inline_size,
- child_base.intrinsic_inline_sizes.minimum_inline_size,
- );
-
- if child_base.flags.contains(FlowFlags::CLEARS_LEFT) {
- left_float_width = max(left_float_width, left_float_width_accumulator);
- left_float_width_accumulator = Au(0)
- }
- if child_base.flags.contains(FlowFlags::CLEARS_RIGHT) {
- right_float_width = max(right_float_width, right_float_width_accumulator);
- right_float_width_accumulator = Au(0)
- }
-
- match (
- float_kind,
- child_base
- .flags
- .contains(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS),
- ) {
- (Float::None, true) => {
- computation.content_intrinsic_sizes.preferred_inline_size = max(
- computation.content_intrinsic_sizes.preferred_inline_size,
- child_base.intrinsic_inline_sizes.preferred_inline_size,
- );
- },
- (Float::None, false) => {
- preferred_inline_size_of_children_without_text_or_replaced_fragments = max(
- preferred_inline_size_of_children_without_text_or_replaced_fragments,
- child_base.intrinsic_inline_sizes.preferred_inline_size,
- )
- },
- (Float::Left, _) => {
- left_float_width_accumulator = left_float_width_accumulator +
- child_base.intrinsic_inline_sizes.preferred_inline_size;
- },
- (Float::Right, _) => {
- right_float_width_accumulator = right_float_width_accumulator +
- child_base.intrinsic_inline_sizes.preferred_inline_size;
- },
- }
- }
-
- left_float_width = max(left_float_width, left_float_width_accumulator);
- right_float_width = max(right_float_width, right_float_width_accumulator);
-
- computation.content_intrinsic_sizes.preferred_inline_size =
- computation.content_intrinsic_sizes.preferred_inline_size +
- left_float_width +
- right_float_width;
- computation.content_intrinsic_sizes.preferred_inline_size = max(
- computation.content_intrinsic_sizes.preferred_inline_size,
- preferred_inline_size_of_children_without_text_or_replaced_fragments,
- );
-
- self.base.intrinsic_inline_sizes = computation.finish();
- self.base.flags = flags
- }
-
- pub fn overflow_style_may_require_clip_scroll_node(&self) -> bool {
- match (
- self.fragment.style().get_box().overflow_x,
- self.fragment.style().get_box().overflow_y,
- ) {
- (StyleOverflow::Auto, _) |
- (StyleOverflow::Scroll, _) |
- (StyleOverflow::Hidden, _) |
- (_, StyleOverflow::Auto) |
- (_, StyleOverflow::Scroll) |
- (_, StyleOverflow::Hidden) => true,
- (_, _) => false,
- }
- }
-
- pub fn compute_inline_sizes(&mut self, shared_context: &SharedStyleContext) {
- if !self
- .base
- .restyle_damage
- .intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW)
- {
- return;
- }
-
- debug!(
- "assign_inline_sizes({}): assigning inline_size for flow",
- if self.base.flags.is_float() {
- "float"
- } else {
- "block"
- }
- );
-
- self.base.floats = Floats::new(self.base.writing_mode);
-
- self.initialize_container_size_for_root(shared_context);
-
- // Our inline-size was set to the inline-size of the containing block by the flow's parent.
- // Now compute the real value.
- self.propagate_and_compute_used_inline_size(shared_context);
-
- self.guess_inline_size_for_block_formatting_context_if_necessary()
- }
-
- /// If this is the root flow, initialize values that would normally be set by the parent.
- ///
- /// Should be called during `assign_inline_sizes` for flows that may be the root.
- pub fn initialize_container_size_for_root(&mut self, shared_context: &SharedStyleContext) {
- if self.is_root() {
- debug!("Setting root position");
- self.base.position.start = LogicalPoint::zero(self.base.writing_mode);
- self.base.block_container_inline_size =
- LogicalSize::from_physical(self.base.writing_mode, shared_context.viewport_size())
- .inline;
- self.base.block_container_writing_mode = self.base.writing_mode;
- }
- }
-
- fn guess_inline_size_for_block_formatting_context_if_necessary(&mut self) {
- // We don't need to guess anything unless this is a block formatting context.
- if self.formatting_context_type() != FormattingContextType::Block {
- return;
- }
-
- // If `max-width` is set, then don't perform this speculation. We guess that the
- // page set `max-width` in order to avoid hitting floats. The search box on Google
- // SERPs falls into this category.
- if self.fragment.style.max_inline_size() != MaxSize::None {
- return;
- }
-
- // At this point, we know we can't precisely compute the inline-size of this block now,
- // because floats might affect it. Speculate that its inline-size is equal to the
- // inline-size computed above minus the inline-size of the previous left and/or right
- // floats.
- let speculated_left_float_size = if self.fragment.margin.inline_start >= Au(0) &&
- self.base.speculated_float_placement_in.left > self.fragment.margin.inline_start
- {
- self.base.speculated_float_placement_in.left - self.fragment.margin.inline_start
- } else {
- Au(0)
- };
- let speculated_right_float_size = if self.fragment.margin.inline_end >= Au(0) &&
- self.base.speculated_float_placement_in.right > self.fragment.margin.inline_end
- {
- self.base.speculated_float_placement_in.right - self.fragment.margin.inline_end
- } else {
- Au(0)
- };
- self.fragment.border_box.size.inline = self.fragment.border_box.size.inline -
- speculated_left_float_size -
- speculated_right_float_size
- }
-
- fn definitely_has_zero_block_size(&self) -> bool {
- if !self
- .fragment
- .style
- .content_block_size()
- .is_definitely_zero()
- {
- return false;
- }
- let border_width = self.fragment.border_width();
- if border_width.block_start != Au(0) || border_width.block_end != Au(0) {
- return false;
- }
- let padding = self.fragment.style.logical_padding();
- padding.block_start.is_definitely_zero() && padding.block_end.is_definitely_zero()
- }
-
- pub fn is_inline_flex_item(&self) -> bool {
- self.fragment
- .flags
- .contains(FragmentFlags::IS_INLINE_FLEX_ITEM)
- }
-
- pub fn is_block_flex_item(&self) -> bool {
- self.fragment
- .flags
- .contains(FragmentFlags::IS_BLOCK_FLEX_ITEM)
- }
-
- pub fn mark_scrolling_overflow(&mut self, has_scrolling_overflow: bool) {
- if has_scrolling_overflow {
- self.flags.insert(BlockFlowFlags::HAS_SCROLLING_OVERFLOW);
- } else {
- self.flags.remove(BlockFlowFlags::HAS_SCROLLING_OVERFLOW);
- }
- }
-
- pub fn has_scrolling_overflow(&self) -> bool {
- self.flags.contains(BlockFlowFlags::HAS_SCROLLING_OVERFLOW)
- }
-
- // Return offset from original position because of `position: sticky`.
- pub fn sticky_position(&self) -> SideOffsets2D<MaybeAuto> {
- let containing_block_size = &self
- .base
- .early_absolute_position_info
- .relative_containing_block_size;
- let writing_mode = self
- .base
- .early_absolute_position_info
- .relative_containing_block_mode;
- let offsets = self.fragment.style().logical_position();
- let as_margins = LogicalMargin::new(
- writing_mode,
- MaybeAuto::from_style(offsets.block_start, containing_block_size.inline),
- MaybeAuto::from_style(offsets.inline_end, containing_block_size.inline),
- MaybeAuto::from_style(offsets.block_end, containing_block_size.inline),
- MaybeAuto::from_style(offsets.inline_start, containing_block_size.inline),
- );
- as_margins.to_physical(writing_mode)
- }
-
- pub fn background_border_section(&self) -> DisplayListSection {
- if self.base.flags.is_float() {
- DisplayListSection::BackgroundAndBorders
- } else if self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- if self.fragment.establishes_stacking_context() {
- DisplayListSection::BackgroundAndBorders
- } else {
- DisplayListSection::BlockBackgroundsAndBorders
- }
- } else {
- DisplayListSection::BlockBackgroundsAndBorders
- }
- }
-}
-
-impl Flow for BlockFlow {
- fn class(&self) -> FlowClass {
- FlowClass::Block
- }
-
- fn as_mut_block(&mut self) -> &mut BlockFlow {
- self
- }
-
- fn as_block(&self) -> &BlockFlow {
- self
- }
-
- /// Pass 1 of reflow: computes minimum and preferred inline-sizes.
- ///
- /// Recursively (bottom-up) determine the flow's minimum and preferred inline-sizes. When
- /// called on this flow, all child flows have had their minimum and preferred inline-sizes set.
- /// This function must decide minimum/preferred inline-sizes based on its children's
- /// inline-sizes and the dimensions of any fragments it is responsible for flowing.
- fn bubble_inline_sizes(&mut self) {
- // If this block has a fixed width, just use that for the minimum and preferred width,
- // rather than bubbling up children inline width.
- // FIXME(emilio): This should probably be writing-mode-aware.
- let consult_children = match self.fragment.style().get_position().width {
- Size::Auto => true,
- Size::LengthPercentage(ref lp) => lp.maybe_to_used_value(None).is_none(),
- };
- self.bubble_inline_sizes_for_block(consult_children);
- self.fragment
- .restyle_damage
- .remove(ServoRestyleDamage::BUBBLE_ISIZES);
- }
-
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
- /// When called on this context, the context has had its inline-size set by the parent context.
- ///
- /// Dual fragments consume some inline-size first, and the remainder is assigned to all child
- /// (block) contexts.
- fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
- let _scope = layout_debug_scope!("block::assign_inline_sizes {:x}", self.base.debug_id());
-
- let shared_context = layout_context.shared_context();
- self.compute_inline_sizes(shared_context);
-
- // Move in from the inline-start border edge.
- let inline_start_content_edge =
- self.fragment.border_box.start.i + self.fragment.border_padding.inline_start;
-
- let padding_and_borders = self.fragment.border_padding.inline_start_end();
-
- // Distance from the inline-end margin edge to the inline-end content edge.
- let inline_end_content_edge =
- self.fragment.margin.inline_end + self.fragment.border_padding.inline_end;
-
- let content_inline_size = self.fragment.border_box.size.inline - padding_and_borders;
-
- self.propagate_assigned_inline_size_to_children(
- shared_context,
- inline_start_content_edge,
- inline_end_content_edge,
- content_inline_size,
- |_, _, _, _, _, _| {},
- );
- }
-
- fn place_float_if_applicable<'a>(&mut self) {
- if self.base.flags.is_float() {
- self.place_float();
- }
- }
-
- fn assign_block_size_for_inorder_child_if_necessary(
- &mut self,
- layout_context: &LayoutContext,
- parent_thread_id: u8,
- content_box: LogicalRect<Au>,
- ) -> bool {
- if self.base.flags.is_float() {
- return false;
- }
-
- let is_formatting_context = self.formatting_context_type() != FormattingContextType::None;
- if !self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) &&
- is_formatting_context
- {
- self.assign_inline_position_for_formatting_context(layout_context, content_box);
- }
-
- if (self as &dyn Flow).floats_might_flow_through() {
- self.base.thread_id = parent_thread_id;
- if self
- .base
- .restyle_damage
- .intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW)
- {
- self.assign_block_size(layout_context);
- // Don't remove the restyle damage; `assign_block_size` decides whether that is
- // appropriate (which in the case of e.g. absolutely-positioned flows, it is not).
- }
- return true;
- }
-
- if is_formatting_context {
- // If this is a formatting context and definitely did not have floats in, then we must
- // translate the floats past us.
- let writing_mode = self.base.floats.writing_mode;
- let delta = self.base.position.size.block;
- self.base
- .floats
- .translate(LogicalSize::new(writing_mode, Au(0), -delta));
- return true;
- }
-
- false
- }
-
- fn assign_block_size(&mut self, ctx: &LayoutContext) {
- let remaining = Flow::fragment(self, ctx, None);
- debug_assert!(remaining.is_none());
- }
-
- fn fragment(
- &mut self,
- layout_context: &LayoutContext,
- fragmentation_context: Option<FragmentationContext>,
- ) -> Option<Arc<dyn Flow>> {
- 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.
- self.fragment.assign_replaced_block_size_if_necessary();
- if !self
- .base
- .flags
- .contains(FlowFlags::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(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
- self.fragment
- .restyle_damage
- .remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
- }
- None
- } else if self.is_root() ||
- self.formatting_context_type() != FormattingContextType::None ||
- self.base.flags.contains(FlowFlags::MARGINS_CANNOT_COLLAPSE)
- {
- // Root element margins should never be collapsed according to CSS § 8.3.1.
- debug!(
- "assign_block_size: assigning block_size for root flow {:?}",
- self.base().debug_id()
- );
- self.assign_block_size_block_base(
- layout_context,
- fragmentation_context,
- MarginsMayCollapseFlag::MarginsMayNotCollapse,
- )
- } else {
- debug!(
- "assign_block_size: assigning block_size for block {:?}",
- self.base().debug_id()
- );
- self.assign_block_size_block_base(
- layout_context,
- fragmentation_context,
- MarginsMayCollapseFlag::MarginsMayCollapse,
- )
- }
- }
-
- fn compute_stacking_relative_position(&mut self, _layout_context: &LayoutContext) {
- // FIXME (mbrubeck): Get the real container size, taking the container writing mode into
- // account. Must handle vertical writing modes.
- let container_size = Size2D::new(self.base.block_container_inline_size, Au(0));
-
- if self.is_root() {
- self.base.clip = Rect::max_rect();
- }
-
- if self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- let position_start = self
- .base
- .position
- .start
- .to_physical(self.base.writing_mode, container_size);
-
- // Compute our position relative to the nearest ancestor stacking context. This will be
- // passed down later as part of containing block details for absolute descendants.
- let absolute_stacking_relative_position = if self.is_fixed() {
- // The viewport is initially at (0, 0).
- position_start
- } else {
- // Absolute position of the containing block + position of absolute
- // flow w.r.t. the containing block.
- self.base
- .late_absolute_position_info
- .stacking_relative_position_of_absolute_containing_block +
- position_start.to_vector()
- };
-
- if !self.base.writing_mode.is_vertical() {
- if !self
- .base
- .flags
- .contains(FlowFlags::INLINE_POSITION_IS_STATIC)
- {
- self.base.stacking_relative_position.x = absolute_stacking_relative_position.x
- }
- if !self
- .base
- .flags
- .contains(FlowFlags::BLOCK_POSITION_IS_STATIC)
- {
- self.base.stacking_relative_position.y = absolute_stacking_relative_position.y
- }
- } else {
- if !self
- .base
- .flags
- .contains(FlowFlags::INLINE_POSITION_IS_STATIC)
- {
- self.base.stacking_relative_position.y = absolute_stacking_relative_position.y
- }
- if !self
- .base
- .flags
- .contains(FlowFlags::BLOCK_POSITION_IS_STATIC)
- {
- self.base.stacking_relative_position.x = absolute_stacking_relative_position.x
- }
- }
- }
-
- // For relatively-positioned descendants, the containing block formed by a block is just
- // the content box. The containing block for absolutely-positioned descendants, on the
- // other hand, is established in other circumstances (see `is_absolute_containing_block').
- let relative_offset = self.fragment.relative_position(
- &self
- .base
- .early_absolute_position_info
- .relative_containing_block_size,
- );
- if self.is_absolute_containing_block() {
- let border_box_origin =
- (self.fragment.border_box - self.fragment.style.logical_border_width()).start;
- self.base
- .late_absolute_position_info
- .stacking_relative_position_of_absolute_containing_block =
- self.base.stacking_relative_position.to_point() +
- (border_box_origin + relative_offset)
- .to_physical(self.base.writing_mode, container_size)
- .to_vector()
- }
-
- // Compute absolute position info for children.
- let stacking_relative_position_of_absolute_containing_block_for_children =
- if self.fragment.establishes_stacking_context() {
- let logical_border_width = self.fragment.style().logical_border_width();
- let position = LogicalPoint::new(
- self.base.writing_mode,
- logical_border_width.inline_start,
- logical_border_width.block_start,
- );
- let position = position.to_physical(self.base.writing_mode, container_size);
-
- // Some blocks establish a stacking context, but not a containing block for
- // absolutely positioned elements. An example of this might be a block that has
- // `position: static` and `opacity` set. In these cases, absolutely-positioned
- // children will not be positioned relative to us but will instead be positioned
- // relative to our containing block.
- if self.is_absolute_containing_block() {
- position
- } else {
- position - self.base.stacking_relative_position
- }
- } else {
- self.base
- .late_absolute_position_info
- .stacking_relative_position_of_absolute_containing_block
- };
- let late_absolute_position_info_for_children = LateAbsolutePositionInfo {
- stacking_relative_position_of_absolute_containing_block:
- stacking_relative_position_of_absolute_containing_block_for_children,
- };
- let container_size_for_children =
- self.base.position.size.to_physical(self.base.writing_mode);
-
- // Compute the origin and clipping rectangle for children.
- let relative_offset = relative_offset
- .to_physical(self.base.writing_mode)
- .to_vector();
- let is_stacking_context = self.fragment.establishes_stacking_context();
- let origin_for_children = if is_stacking_context {
- // We establish a stacking context, so the position of our children is vertically
- // correct, but has to be adjusted to accommodate horizontal margins. (Note the
- // calculation involving `position` below and recall that inline-direction flow
- // positions are relative to the edges of the margin box.)
- //
- // FIXME(pcwalton): Is this vertical-writing-direction-safe?
- let margin = self.fragment.margin.to_physical(self.base.writing_mode);
- Point2D::new(-margin.left, Au(0))
- } else {
- self.base.stacking_relative_position.to_point() + relative_offset
- };
-
- // Process children.
- for kid in self.base.child_iter_mut() {
- if kid
- .base()
- .flags
- .contains(FlowFlags::INLINE_POSITION_IS_STATIC) ||
- kid.base()
- .flags
- .contains(FlowFlags::BLOCK_POSITION_IS_STATIC)
- {
- let kid_base = kid.mut_base();
- let physical_position = kid_base
- .position
- .to_physical(kid_base.writing_mode, container_size_for_children);
-
- // Set the inline and block positions as necessary.
- if !kid_base.writing_mode.is_vertical() {
- if kid_base
- .flags
- .contains(FlowFlags::INLINE_POSITION_IS_STATIC)
- {
- kid_base.stacking_relative_position.x =
- origin_for_children.x + physical_position.origin.x
- }
- if kid_base.flags.contains(FlowFlags::BLOCK_POSITION_IS_STATIC) {
- kid_base.stacking_relative_position.y =
- origin_for_children.y + physical_position.origin.y
- }
- } else {
- if kid_base
- .flags
- .contains(FlowFlags::INLINE_POSITION_IS_STATIC)
- {
- kid_base.stacking_relative_position.y =
- origin_for_children.y + physical_position.origin.y
- }
- if kid_base.flags.contains(FlowFlags::BLOCK_POSITION_IS_STATIC) {
- kid_base.stacking_relative_position.x =
- origin_for_children.x + physical_position.origin.x
- }
- }
- }
-
- kid.mut_base().late_absolute_position_info = late_absolute_position_info_for_children;
- }
- }
-
- fn mark_as_root(&mut self) {
- self.flags.insert(BlockFlowFlags::IS_ROOT)
- }
-
- fn is_root(&self) -> bool {
- self.flags.contains(BlockFlowFlags::IS_ROOT)
- }
-
- /// The 'position' property of this flow.
- fn positioning(&self) -> Position {
- self.fragment.style.get_box().position
- }
-
- /// Return the dimensions of the containing block generated by this flow for absolutely-
- /// positioned descendants. For block flows, this is the padding box.
- fn generated_containing_block_size(&self, _: OpaqueFlow) -> LogicalSize<Au> {
- (self.fragment.border_box - self.fragment.style().logical_border_width()).size
- }
-
- /// Returns true if this flow contains fragments that are roots of an absolute flow tree.
- fn contains_roots_of_absolute_flow_tree(&self) -> bool {
- self.contains_relatively_positioned_fragments() ||
- self.is_root() ||
- self.fragment.has_filter_transform_or_perspective()
- }
-
- /// Returns true if this is an absolute containing block.
- fn is_absolute_containing_block(&self) -> bool {
- self.contains_positioned_fragments() || self.fragment.has_filter_transform_or_perspective()
- }
-
- fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
- if self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) &&
- self.fragment.style().logical_position().inline_start == LengthPercentageOrAuto::Auto &&
- self.fragment.style().logical_position().inline_end == LengthPercentageOrAuto::Auto
- {
- self.base.position.start.i = inline_position
- }
- }
-
- fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
- if self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) &&
- self.fragment.style().logical_position().block_start == LengthPercentageOrAuto::Auto &&
- self.fragment.style().logical_position().block_end == LengthPercentageOrAuto::Auto
- {
- self.base.position.start.b = block_position
- }
- }
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
- self.collect_stacking_contexts_for_block(state, StackingContextCollectionFlags::empty());
- }
-
- fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
- self.build_display_list_for_block(state, BorderPaintingMode::Separate);
- }
-
- fn repair_style(&mut self, new_style: &crate::ServoArc<ComputedValues>) {
- self.fragment.repair_style(new_style)
- }
-
- fn compute_overflow(&self) -> Overflow {
- let flow_size = self.base.position.size.to_physical(self.base.writing_mode);
- let overflow = self.fragment.compute_overflow(
- &flow_size,
- &self
- .base
- .early_absolute_position_info
- .relative_containing_block_size,
- );
- overflow
- }
-
- fn iterate_through_fragment_border_boxes(
- &self,
- iterator: &mut dyn FragmentBorderBoxIterator,
- level: i32,
- stacking_context_position: &Point2D<Au>,
- ) {
- if !iterator.should_process(&self.fragment) {
- return;
- }
-
- iterator.process(
- &self.fragment,
- level,
- &self
- .fragment
- .stacking_relative_border_box(
- &self.base.stacking_relative_position,
- &self
- .base
- .early_absolute_position_info
- .relative_containing_block_size,
- self.base
- .early_absolute_position_info
- .relative_containing_block_mode,
- CoordinateSystem::Own,
- )
- .translate(&stacking_context_position.to_vector()),
- );
- }
-
- fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
- (*mutator)(&mut self.fragment)
- }
-
- fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
- print_tree.add_item(format!("↑↑ Fragment for block:{:?}", self.fragment));
- }
-}
-
-impl fmt::Debug for BlockFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "{:?}({:x}) {:?}",
- self.class(),
- self.base.debug_id(),
- self.base
- )
- }
-}
-
-/// The inputs for the inline-sizes-and-margins constraint equation.
-#[derive(Clone, Copy, Debug)]
-pub struct ISizeConstraintInput {
- pub computed_inline_size: MaybeAuto,
- pub inline_start_margin: MaybeAuto,
- pub inline_end_margin: MaybeAuto,
- pub inline_start: MaybeAuto,
- pub inline_end: MaybeAuto,
- pub text_align: TextAlign,
- pub available_inline_size: Au,
-}
-
-impl ISizeConstraintInput {
- pub fn new(
- computed_inline_size: MaybeAuto,
- inline_start_margin: MaybeAuto,
- inline_end_margin: MaybeAuto,
- inline_start: MaybeAuto,
- inline_end: MaybeAuto,
- text_align: TextAlign,
- available_inline_size: Au,
- ) -> ISizeConstraintInput {
- ISizeConstraintInput {
- computed_inline_size: computed_inline_size,
- inline_start_margin: inline_start_margin,
- inline_end_margin: inline_end_margin,
- inline_start: inline_start,
- inline_end: inline_end,
- text_align: text_align,
- available_inline_size: available_inline_size,
- }
- }
-}
-
-/// The solutions for the inline-size-and-margins constraint equation.
-#[derive(Clone, Copy, Debug)]
-pub struct ISizeConstraintSolution {
- pub inline_start: Au,
- pub inline_size: Au,
- pub margin_inline_start: Au,
- pub margin_inline_end: Au,
-}
-
-impl ISizeConstraintSolution {
- pub fn new(
- inline_size: Au,
- margin_inline_start: Au,
- margin_inline_end: Au,
- ) -> ISizeConstraintSolution {
- ISizeConstraintSolution {
- inline_start: Au(0),
- inline_size: inline_size,
- margin_inline_start: margin_inline_start,
- margin_inline_end: margin_inline_end,
- }
- }
-
- fn for_absolute_flow(
- inline_start: Au,
- inline_size: Au,
- margin_inline_start: Au,
- margin_inline_end: Au,
- ) -> ISizeConstraintSolution {
- ISizeConstraintSolution {
- inline_start: inline_start,
- inline_size: inline_size,
- margin_inline_start: margin_inline_start,
- margin_inline_end: margin_inline_end,
- }
- }
-}
-
-// Trait to encapsulate the ISize and Margin calculation.
-//
-// CSS Section 10.3
-pub trait ISizeAndMarginsComputer {
- /// Instructs the fragment to compute its border and padding.
- fn compute_border_and_padding(&self, block: &mut BlockFlow, containing_block_inline_size: Au) {
- block
- .fragment
- .compute_border_and_padding(containing_block_inline_size);
- }
-
- /// Compute the inputs for the ISize constraint equation.
- ///
- /// This is called only once to compute the initial inputs. For calculations involving
- /// minimum and maximum inline-size, we don't need to recompute these.
- fn compute_inline_size_constraint_inputs(
- &self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- shared_context: &SharedStyleContext,
- ) -> ISizeConstraintInput {
- let containing_block_inline_size =
- self.containing_block_inline_size(block, parent_flow_inline_size, shared_context);
-
- block
- .fragment
- .compute_block_direction_margins(containing_block_inline_size);
- block
- .fragment
- .compute_inline_direction_margins(containing_block_inline_size);
- self.compute_border_and_padding(block, containing_block_inline_size);
-
- let mut computed_inline_size =
- self.initial_computed_inline_size(block, parent_flow_inline_size, shared_context);
- let style = block.fragment.style();
- match (computed_inline_size, style.get_position().box_sizing) {
- (MaybeAuto::Specified(size), BoxSizing::BorderBox) => {
- computed_inline_size =
- MaybeAuto::Specified(size - block.fragment.border_padding.inline_start_end())
- },
- (MaybeAuto::Auto, BoxSizing::BorderBox) | (_, BoxSizing::ContentBox) => {},
- }
-
- let margin = style.logical_margin();
- let position = style.logical_position();
-
- let available_inline_size =
- containing_block_inline_size - block.fragment.border_padding.inline_start_end();
- ISizeConstraintInput::new(
- computed_inline_size,
- MaybeAuto::from_style(margin.inline_start, containing_block_inline_size),
- MaybeAuto::from_style(margin.inline_end, containing_block_inline_size),
- MaybeAuto::from_style(position.inline_start, containing_block_inline_size),
- MaybeAuto::from_style(position.inline_end, containing_block_inline_size),
- style.get_inherited_text().text_align,
- available_inline_size,
- )
- }
-
- /// Set the used values for inline-size and margins from the relevant constraint equation.
- /// This is called only once.
- ///
- /// Set:
- /// * Used values for content inline-size, inline-start margin, and inline-end margin for this
- /// flow's box;
- /// * Inline-start coordinate of this flow's box;
- /// * Inline-start coordinate of the flow with respect to its containing block (if this is an
- /// absolute flow).
- fn set_inline_size_constraint_solutions(
- &self,
- block: &mut BlockFlow,
- solution: ISizeConstraintSolution,
- ) {
- let inline_size;
- let extra_inline_size_from_margin;
- {
- let block_mode = block.base.writing_mode;
-
- // FIXME (mbrubeck): Get correct containing block for positioned blocks?
- let container_mode = block.base.block_container_writing_mode;
- let container_size = block.base.block_container_inline_size;
-
- let fragment = block.fragment();
- fragment.margin.inline_start = solution.margin_inline_start;
- fragment.margin.inline_end = solution.margin_inline_end;
-
- // The associated fragment has the border box of this flow.
- inline_size = solution.inline_size + fragment.border_padding.inline_start_end();
- fragment.border_box.size.inline = inline_size;
-
- // Start border edge.
- // FIXME (mbrubeck): Handle vertical writing modes.
- fragment.border_box.start.i =
- if container_mode.is_bidi_ltr() == block_mode.is_bidi_ltr() {
- fragment.margin.inline_start
- } else {
- // The parent's "start" direction is the child's "end" direction.
- container_size - inline_size - fragment.margin.inline_end
- };
-
- // To calculate the total size of this block, we also need to account for any
- // additional size contribution from positive margins. Negative margins means the block
- // isn't made larger at all by the margin.
- extra_inline_size_from_margin =
- max(Au(0), fragment.margin.inline_start) + max(Au(0), fragment.margin.inline_end);
- }
-
- // We also resize the block itself, to ensure that overflow is not calculated
- // as the inline-size of our parent. We might be smaller and we might be larger if we
- // overflow.
- block.mut_base().position.size.inline = inline_size + extra_inline_size_from_margin;
- }
-
- /// Set the inline coordinate of the given flow if it is absolutely positioned.
- fn set_inline_position_of_flow_if_necessary(
- &self,
- _: &mut BlockFlow,
- _: ISizeConstraintSolution,
- ) {
- }
-
- /// Solve the inline-size and margins constraints for this block flow.
- fn solve_inline_size_constraints(
- &self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution;
-
- fn initial_computed_inline_size(
- &self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- shared_context: &SharedStyleContext,
- ) -> MaybeAuto {
- MaybeAuto::from_option(
- block
- .fragment()
- .style()
- .content_inline_size()
- .to_used_value(self.containing_block_inline_size(
- block,
- parent_flow_inline_size,
- shared_context,
- )),
- )
- }
-
- fn containing_block_inline_size(
- &self,
- _: &mut BlockFlow,
- parent_flow_inline_size: Au,
- _: &SharedStyleContext,
- ) -> Au {
- parent_flow_inline_size
- }
-
- /// Compute the used value of inline-size, taking care of min-inline-size and max-inline-size.
- ///
- /// CSS Section 10.4: Minimum and Maximum inline-sizes
- fn compute_used_inline_size(
- &self,
- block: &mut BlockFlow,
- shared_context: &SharedStyleContext,
- parent_flow_inline_size: Au,
- ) {
- let mut input = self.compute_inline_size_constraint_inputs(
- block,
- parent_flow_inline_size,
- shared_context,
- );
-
- let containing_block_inline_size =
- self.containing_block_inline_size(block, parent_flow_inline_size, shared_context);
-
- let mut solution = self.solve_inline_size_constraints(block, &input);
-
- // If the tentative used inline-size is greater than 'max-inline-size', inline-size should
- // be recalculated, but this time using the computed value of 'max-inline-size' as the
- // computed value for 'inline-size'.
- match block
- .fragment()
- .style()
- .max_inline_size()
- .to_used_value(containing_block_inline_size)
- {
- Some(max_inline_size) if max_inline_size < solution.inline_size => {
- input.computed_inline_size = MaybeAuto::Specified(max_inline_size);
- solution = self.solve_inline_size_constraints(block, &input);
- },
- _ => {},
- }
-
- // If the resulting inline-size is smaller than 'min-inline-size', inline-size should be
- // recalculated, but this time using the value of 'min-inline-size' as the computed value
- // for 'inline-size'.
- let computed_min_inline_size = block
- .fragment()
- .style()
- .min_inline_size()
- .to_used_value(containing_block_inline_size)
- .unwrap_or(Au(0));
- if computed_min_inline_size > solution.inline_size {
- input.computed_inline_size = MaybeAuto::Specified(computed_min_inline_size);
- solution = self.solve_inline_size_constraints(block, &input);
- }
-
- self.set_inline_size_constraint_solutions(block, solution);
- self.set_inline_position_of_flow_if_necessary(block, solution);
- }
-
- /// Computes inline-start and inline-end margins and inline-size.
- ///
- /// This is used by both replaced and non-replaced Blocks.
- ///
- /// CSS 2.1 Section 10.3.3.
- /// Constraint Equation: margin-inline-start + margin-inline-end + inline-size =
- /// available_inline-size
- /// where available_inline-size = CB inline-size - (horizontal border + padding)
- fn solve_block_inline_size_constraints(
- &self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- let (computed_inline_size, inline_start_margin, inline_end_margin, available_inline_size) = (
- input.computed_inline_size,
- input.inline_start_margin,
- input.inline_end_margin,
- input.available_inline_size,
- );
-
- // Check for direction of parent flow (NOT Containing Block)
- let block_mode = block.base.writing_mode;
- let container_mode = block.base.block_container_writing_mode;
- let block_align = block.base.flags.text_align();
-
- // FIXME (mbrubeck): Handle vertical writing modes.
- let parent_has_same_direction = container_mode.is_bidi_ltr() == block_mode.is_bidi_ltr();
-
- // If inline-size is not 'auto', and inline-size + margins > available_inline-size, all
- // 'auto' margins are treated as 0.
- let (inline_start_margin, inline_end_margin) = match computed_inline_size {
- MaybeAuto::Auto => (inline_start_margin, inline_end_margin),
- MaybeAuto::Specified(inline_size) => {
- let inline_start = inline_start_margin.specified_or_zero();
- let inline_end = inline_end_margin.specified_or_zero();
-
- if (inline_start + inline_end + inline_size) > available_inline_size {
- (
- MaybeAuto::Specified(inline_start),
- MaybeAuto::Specified(inline_end),
- )
- } else {
- (inline_start_margin, inline_end_margin)
- }
- },
- };
-
- // Invariant: inline-start_margin + inline-size + inline-end_margin ==
- // available_inline-size
- let (inline_start_margin, inline_size, inline_end_margin) =
- match (inline_start_margin, computed_inline_size, inline_end_margin) {
- // If all have a computed value other than 'auto', the system is over-constrained.
- (
- MaybeAuto::Specified(margin_start),
- MaybeAuto::Specified(inline_size),
- MaybeAuto::Specified(margin_end),
- ) => {
- // servo_left, servo_right, and servo_center are used to implement
- // the "align descendants" rule in HTML5 § 14.2.
- if block_align == TextAlign::ServoCenter {
- // Ignore any existing margins, and make the inline-start and
- // inline-end margins equal.
- let margin = (available_inline_size - inline_size).scale_by(0.5);
- (margin, inline_size, margin)
- } else {
- let ignore_end_margin = match block_align {
- TextAlign::ServoLeft => block_mode.is_bidi_ltr(),
- TextAlign::ServoRight => !block_mode.is_bidi_ltr(),
- _ => parent_has_same_direction,
- };
- if ignore_end_margin {
- (
- margin_start,
- inline_size,
- available_inline_size - (margin_start + inline_size),
- )
- } else {
- (
- available_inline_size - (margin_end + inline_size),
- inline_size,
- margin_end,
- )
- }
- }
- },
- // If exactly one value is 'auto', solve for it
- (
- MaybeAuto::Auto,
- MaybeAuto::Specified(inline_size),
- MaybeAuto::Specified(margin_end),
- ) => (
- available_inline_size - (inline_size + margin_end),
- inline_size,
- margin_end,
- ),
- (
- MaybeAuto::Specified(margin_start),
- MaybeAuto::Auto,
- MaybeAuto::Specified(margin_end),
- ) => (
- margin_start,
- available_inline_size - (margin_start + margin_end),
- margin_end,
- ),
- (
- MaybeAuto::Specified(margin_start),
- MaybeAuto::Specified(inline_size),
- MaybeAuto::Auto,
- ) => (
- margin_start,
- inline_size,
- available_inline_size - (margin_start + inline_size),
- ),
-
- // If inline-size is set to 'auto', any other 'auto' value becomes '0',
- // and inline-size is solved for
- (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Specified(margin_end)) => {
- (Au(0), available_inline_size - margin_end, margin_end)
- },
- (MaybeAuto::Specified(margin_start), MaybeAuto::Auto, MaybeAuto::Auto) => {
- (margin_start, available_inline_size - margin_start, Au(0))
- },
- (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Auto) => {
- (Au(0), available_inline_size, Au(0))
- },
-
- // If inline-start and inline-end margins are auto, they become equal
- (MaybeAuto::Auto, MaybeAuto::Specified(inline_size), MaybeAuto::Auto) => {
- let margin = (available_inline_size - inline_size).scale_by(0.5);
- (margin, inline_size, margin)
- },
- };
-
- ISizeConstraintSolution::new(inline_size, inline_start_margin, inline_end_margin)
- }
-}
-
-/// The different types of Blocks.
-///
-/// They mainly differ in the way inline-size and block-sizes and margins are calculated
-/// for them.
-pub struct AbsoluteNonReplaced;
-pub struct AbsoluteReplaced;
-pub struct BlockNonReplaced;
-pub struct BlockReplaced;
-pub struct FloatNonReplaced;
-pub struct FloatReplaced;
-pub struct InlineBlockNonReplaced;
-pub struct InlineBlockReplaced;
-pub struct InlineFlexItem;
-
-impl ISizeAndMarginsComputer for AbsoluteNonReplaced {
- /// Solve the horizontal constraint equation for absolute non-replaced elements.
- ///
- /// CSS Section 10.3.7
- /// Constraint equation:
- /// inline-start + inline-end + inline-size + margin-inline-start + margin-inline-end
- /// = absolute containing block inline-size - (horizontal padding and border)
- /// [aka available inline-size]
- ///
- /// Return the solution for the equation.
- fn solve_inline_size_constraints(
- &self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- let &ISizeConstraintInput {
- computed_inline_size,
- inline_start_margin,
- inline_end_margin,
- inline_start,
- inline_end,
- available_inline_size,
- ..
- } = input;
-
- // Check for direction of parent flow (NOT Containing Block)
- let block_mode = block.base.writing_mode;
- let container_mode = block.base.block_container_writing_mode;
-
- // FIXME (mbrubeck): Handle vertical writing modes.
- let parent_has_same_direction = container_mode.is_bidi_ltr() == block_mode.is_bidi_ltr();
-
- let (inline_start, inline_size, margin_inline_start, margin_inline_end) =
- match (inline_start, inline_end, computed_inline_size) {
- (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Auto) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- // Now it is the same situation as inline-start Specified and inline-end
- // and inline-size Auto.
-
- // Set inline-end to zero to calculate inline-size.
- let inline_size = block.get_shrink_to_fit_inline_size(
- available_inline_size - (margin_start + margin_end),
- );
- (Au(0), inline_size, margin_start, margin_end)
- },
- (
- MaybeAuto::Specified(inline_start),
- MaybeAuto::Specified(inline_end),
- MaybeAuto::Specified(inline_size),
- ) => {
- match (inline_start_margin, inline_end_margin) {
- (MaybeAuto::Auto, MaybeAuto::Auto) => {
- let total_margin_val =
- available_inline_size - inline_start - inline_end - inline_size;
- if total_margin_val < Au(0) {
- if parent_has_same_direction {
- // margin-inline-start becomes 0
- (inline_start, inline_size, Au(0), total_margin_val)
- } else {
- // margin-inline-end becomes 0, because it's toward the parent's
- // inline-start edge.
- (inline_start, inline_size, total_margin_val, Au(0))
- }
- } else {
- // Equal margins
- (
- inline_start,
- inline_size,
- total_margin_val.scale_by(0.5),
- total_margin_val.scale_by(0.5),
- )
- }
- },
- (MaybeAuto::Specified(margin_start), MaybeAuto::Auto) => {
- let sum = inline_start + inline_end + inline_size + margin_start;
- (
- inline_start,
- inline_size,
- margin_start,
- available_inline_size - sum,
- )
- },
- (MaybeAuto::Auto, MaybeAuto::Specified(margin_end)) => {
- let sum = inline_start + inline_end + inline_size + margin_end;
- (
- inline_start,
- inline_size,
- available_inline_size - sum,
- margin_end,
- )
- },
- (MaybeAuto::Specified(margin_start), MaybeAuto::Specified(margin_end)) => {
- // Values are over-constrained.
- let sum = inline_start + inline_size + margin_start + margin_end;
- if parent_has_same_direction {
- // Ignore value for 'inline-end'
- (inline_start, inline_size, margin_start, margin_end)
- } else {
- // Ignore value for 'inline-start'
- (
- available_inline_size - sum,
- inline_size,
- margin_start,
- margin_end,
- )
- }
- },
- }
- },
- // For the rest of the cases, auto values for margin are set to 0
-
- // If only one is Auto, solve for it
- (
- MaybeAuto::Auto,
- MaybeAuto::Specified(inline_end),
- MaybeAuto::Specified(inline_size),
- ) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- let sum = inline_end + inline_size + margin_start + margin_end;
- (
- available_inline_size - sum,
- inline_size,
- margin_start,
- margin_end,
- )
- },
- (
- MaybeAuto::Specified(inline_start),
- MaybeAuto::Auto,
- MaybeAuto::Specified(inline_size),
- ) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- (inline_start, inline_size, margin_start, margin_end)
- },
- (
- MaybeAuto::Specified(inline_start),
- MaybeAuto::Specified(inline_end),
- MaybeAuto::Auto,
- ) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- let sum = inline_start + inline_end + margin_start + margin_end;
- (
- inline_start,
- available_inline_size - sum,
- margin_start,
- margin_end,
- )
- },
-
- // If inline-size is auto, then inline-size is shrink-to-fit. Solve for the
- // non-auto value.
- (MaybeAuto::Specified(inline_start), MaybeAuto::Auto, MaybeAuto::Auto) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- // Set inline-end to zero to calculate inline-size
- let inline_size = block.get_shrink_to_fit_inline_size(
- available_inline_size - (margin_start + margin_end),
- );
- (inline_start, inline_size, margin_start, margin_end)
- },
- (MaybeAuto::Auto, MaybeAuto::Specified(inline_end), MaybeAuto::Auto) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- // Set inline-start to zero to calculate inline-size
- let inline_size = block.get_shrink_to_fit_inline_size(
- available_inline_size - (margin_start + margin_end),
- );
- let sum = inline_end + inline_size + margin_start + margin_end;
- (
- available_inline_size - sum,
- inline_size,
- margin_start,
- margin_end,
- )
- },
-
- (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Specified(inline_size)) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- // Setting 'inline-start' to static position because direction is 'ltr'.
- // TODO: Handle 'rtl' when it is implemented.
- (Au(0), inline_size, margin_start, margin_end)
- },
- };
- ISizeConstraintSolution::for_absolute_flow(
- inline_start,
- inline_size,
- margin_inline_start,
- margin_inline_end,
- )
- }
-
- fn containing_block_inline_size(
- &self,
- block: &mut BlockFlow,
- _: Au,
- shared_context: &SharedStyleContext,
- ) -> Au {
- let opaque_block = OpaqueFlow::from_flow(block);
- block
- .containing_block_size(&shared_context.viewport_size(), opaque_block)
- .inline
- }
-
- fn set_inline_position_of_flow_if_necessary(
- &self,
- block: &mut BlockFlow,
- solution: ISizeConstraintSolution,
- ) {
- // Set the inline position of the absolute flow wrt to its containing block.
- if !block
- .base
- .flags
- .contains(FlowFlags::INLINE_POSITION_IS_STATIC)
- {
- block.base.position.start.i = solution.inline_start;
- }
- }
-}
-
-impl ISizeAndMarginsComputer for AbsoluteReplaced {
- /// Solve the horizontal constraint equation for absolute replaced elements.
- ///
- /// CSS Section 10.3.8
- /// Constraint equation:
- /// inline-start + inline-end + inline-size + margin-inline-start + margin-inline-end
- /// = absolute containing block inline-size - (horizontal padding and border)
- /// [aka available_inline-size]
- ///
- /// Return the solution for the equation.
- fn solve_inline_size_constraints(
- &self,
- _: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- let &ISizeConstraintInput {
- computed_inline_size,
- inline_start_margin,
- inline_end_margin,
- inline_start,
- inline_end,
- available_inline_size,
- ..
- } = input;
- // TODO: Check for direction of static-position Containing Block (aka
- // parent flow, _not_ the actual Containing Block) when right-to-left
- // is implemented
- // Assume direction is 'ltr' for now
- // TODO: Handle all the cases for 'rtl' direction.
-
- let inline_size = match computed_inline_size {
- MaybeAuto::Specified(w) => w,
- _ => panic!(
- "{} {}",
- "The used value for inline_size for absolute replaced flow",
- "should have already been calculated by now."
- ),
- };
-
- let (inline_start, inline_size, margin_inline_start, margin_inline_end) =
- match (inline_start, inline_end) {
- (MaybeAuto::Auto, MaybeAuto::Auto) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- (Au(0), inline_size, margin_start, margin_end)
- },
- // If only one is Auto, solve for it
- (MaybeAuto::Auto, MaybeAuto::Specified(inline_end)) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- let sum = inline_end + inline_size + margin_start + margin_end;
- (
- available_inline_size - sum,
- inline_size,
- margin_start,
- margin_end,
- )
- },
- (MaybeAuto::Specified(inline_start), MaybeAuto::Auto) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- (inline_start, inline_size, margin_start, margin_end)
- },
- (MaybeAuto::Specified(inline_start), MaybeAuto::Specified(inline_end)) => {
- match (inline_start_margin, inline_end_margin) {
- (MaybeAuto::Auto, MaybeAuto::Auto) => {
- let total_margin_val =
- available_inline_size - inline_start - inline_end - inline_size;
- if total_margin_val < Au(0) {
- // margin-inline-start becomes 0 because direction is 'ltr'.
- (inline_start, inline_size, Au(0), total_margin_val)
- } else {
- // Equal margins
- (
- inline_start,
- inline_size,
- total_margin_val.scale_by(0.5),
- total_margin_val.scale_by(0.5),
- )
- }
- },
- (MaybeAuto::Specified(margin_start), MaybeAuto::Auto) => {
- let sum = inline_start + inline_end + inline_size + margin_start;
- (
- inline_start,
- inline_size,
- margin_start,
- available_inline_size - sum,
- )
- },
- (MaybeAuto::Auto, MaybeAuto::Specified(margin_end)) => {
- let sum = inline_start + inline_end + inline_size + margin_end;
- (
- inline_start,
- inline_size,
- available_inline_size - sum,
- margin_end,
- )
- },
- (MaybeAuto::Specified(margin_start), MaybeAuto::Specified(margin_end)) => {
- // Values are over-constrained.
- // Ignore value for 'inline-end' cos direction is 'ltr'.
- (inline_start, inline_size, margin_start, margin_end)
- },
- }
- },
- };
- ISizeConstraintSolution::for_absolute_flow(
- inline_start,
- inline_size,
- margin_inline_start,
- margin_inline_end,
- )
- }
-
- /// Calculate used value of inline-size just like we do for inline replaced elements.
- fn initial_computed_inline_size(
- &self,
- block: &mut BlockFlow,
- _: Au,
- shared_context: &SharedStyleContext,
- ) -> MaybeAuto {
- let opaque_block = OpaqueFlow::from_flow(block);
- let containing_block_inline_size = block
- .containing_block_size(&shared_context.viewport_size(), opaque_block)
- .inline;
- let container_block_size = block.explicit_block_containing_size(shared_context);
- let fragment = block.fragment();
- 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_box().size.inline)
- }
-
- fn containing_block_inline_size(
- &self,
- block: &mut BlockFlow,
- _: Au,
- shared_context: &SharedStyleContext,
- ) -> Au {
- let opaque_block = OpaqueFlow::from_flow(block);
- block
- .containing_block_size(&shared_context.viewport_size(), opaque_block)
- .inline
- }
-
- fn set_inline_position_of_flow_if_necessary(
- &self,
- block: &mut BlockFlow,
- solution: ISizeConstraintSolution,
- ) {
- // Set the x-coordinate of the absolute flow wrt to its containing block.
- block.base.position.start.i = solution.inline_start;
- }
-}
-
-impl ISizeAndMarginsComputer for BlockNonReplaced {
- /// Compute inline-start and inline-end margins and inline-size.
- fn solve_inline_size_constraints(
- &self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- self.solve_block_inline_size_constraints(block, input)
- }
-}
-
-impl ISizeAndMarginsComputer for BlockReplaced {
- /// Compute inline-start and inline-end margins and inline-size.
- ///
- /// ISize has already been calculated. We now calculate the margins just
- /// like for non-replaced blocks.
- fn solve_inline_size_constraints(
- &self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- match input.computed_inline_size {
- MaybeAuto::Specified(_) => {},
- MaybeAuto::Auto => {
- panic!("BlockReplaced: inline_size should have been computed by now")
- },
- };
- self.solve_block_inline_size_constraints(block, input)
- }
-
- /// Calculate used value of inline-size just like we do for inline replaced elements.
- fn initial_computed_inline_size(
- &self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- shared_context: &SharedStyleContext,
- ) -> MaybeAuto {
- let container_block_size = block.explicit_block_containing_size(shared_context);
- let fragment = block.fragment();
- 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_box().size.inline)
- }
-}
-
-impl ISizeAndMarginsComputer for FloatNonReplaced {
- /// CSS Section 10.3.5
- ///
- /// If inline-size is computed as 'auto', the used value is the 'shrink-to-fit' inline-size.
- fn solve_inline_size_constraints(
- &self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- let (computed_inline_size, inline_start_margin, inline_end_margin, available_inline_size) = (
- input.computed_inline_size,
- input.inline_start_margin,
- input.inline_end_margin,
- input.available_inline_size,
- );
- let margin_inline_start = inline_start_margin.specified_or_zero();
- let margin_inline_end = inline_end_margin.specified_or_zero();
- let available_inline_size_float =
- available_inline_size - margin_inline_start - margin_inline_end;
- let shrink_to_fit = block.get_shrink_to_fit_inline_size(available_inline_size_float);
- let inline_size = computed_inline_size.specified_or_default(shrink_to_fit);
- debug!(
- "assign_inline_sizes_float -- inline_size: {:?}",
- inline_size
- );
- ISizeConstraintSolution::new(inline_size, margin_inline_start, margin_inline_end)
- }
-}
-
-impl ISizeAndMarginsComputer for FloatReplaced {
- /// CSS Section 10.3.5
- ///
- /// If inline-size is computed as 'auto', the used value is the 'shrink-to-fit' inline-size.
- fn solve_inline_size_constraints(
- &self,
- _: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- let (computed_inline_size, inline_start_margin, inline_end_margin) = (
- input.computed_inline_size,
- input.inline_start_margin,
- input.inline_end_margin,
- );
- let margin_inline_start = inline_start_margin.specified_or_zero();
- let margin_inline_end = inline_end_margin.specified_or_zero();
- let inline_size = match computed_inline_size {
- MaybeAuto::Specified(w) => w,
- MaybeAuto::Auto => {
- panic!("FloatReplaced: inline_size should have been computed by now")
- },
- };
- debug!(
- "assign_inline_sizes_float -- inline_size: {:?}",
- inline_size
- );
- ISizeConstraintSolution::new(inline_size, margin_inline_start, margin_inline_end)
- }
-
- /// Calculate used value of inline-size just like we do for inline replaced elements.
- fn initial_computed_inline_size(
- &self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- shared_context: &SharedStyleContext,
- ) -> MaybeAuto {
- let container_block_size = block.explicit_block_containing_size(shared_context);
- let fragment = block.fragment();
- 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_box().size.inline)
- }
-}
-
-impl ISizeAndMarginsComputer for InlineBlockNonReplaced {
- /// Compute inline-start and inline-end margins and inline-size.
- fn solve_inline_size_constraints(
- &self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- let (computed_inline_size, inline_start_margin, inline_end_margin, available_inline_size) = (
- input.computed_inline_size,
- input.inline_start_margin,
- input.inline_end_margin,
- input.available_inline_size,
- );
-
- // For inline-blocks, `auto` margins compute to 0.
- let inline_start_margin = inline_start_margin.specified_or_zero();
- let inline_end_margin = inline_end_margin.specified_or_zero();
-
- // If inline-size is set to 'auto', and this is an inline block, use the
- // shrink to fit algorithm (see CSS 2.1 § 10.3.9)
- let inline_size = match computed_inline_size {
- MaybeAuto::Auto => block.get_shrink_to_fit_inline_size(
- available_inline_size - (inline_start_margin + inline_end_margin),
- ),
- MaybeAuto::Specified(inline_size) => inline_size,
- };
-
- ISizeConstraintSolution::new(inline_size, inline_start_margin, inline_end_margin)
- }
-}
-
-impl ISizeAndMarginsComputer for InlineBlockReplaced {
- /// Compute inline-start and inline-end margins and inline-size.
- ///
- /// ISize has already been calculated. We now calculate the margins just
- /// like for non-replaced blocks.
- fn solve_inline_size_constraints(
- &self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- debug_assert!(match input.computed_inline_size {
- MaybeAuto::Specified(_) => true,
- MaybeAuto::Auto => false,
- });
-
- let (computed_inline_size, inline_start_margin, inline_end_margin, available_inline_size) = (
- input.computed_inline_size,
- input.inline_start_margin,
- input.inline_end_margin,
- input.available_inline_size,
- );
-
- // For inline-blocks, `auto` margins compute to 0.
- let inline_start_margin = inline_start_margin.specified_or_zero();
- let inline_end_margin = inline_end_margin.specified_or_zero();
-
- // If inline-size is set to 'auto', and this is an inline block, use the
- // shrink to fit algorithm (see CSS 2.1 § 10.3.9)
- let inline_size = match computed_inline_size {
- MaybeAuto::Auto => block.get_shrink_to_fit_inline_size(
- available_inline_size - (inline_start_margin + inline_end_margin),
- ),
- MaybeAuto::Specified(inline_size) => inline_size,
- };
-
- ISizeConstraintSolution::new(inline_size, inline_start_margin, inline_end_margin)
- }
-
- /// Calculate used value of inline-size just like we do for inline replaced elements.
- fn initial_computed_inline_size(
- &self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- shared_context: &SharedStyleContext,
- ) -> MaybeAuto {
- let container_block_size = block.explicit_block_containing_size(shared_context);
- let fragment = block.fragment();
- 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_box().size.inline)
- }
-}
-
-impl ISizeAndMarginsComputer for InlineFlexItem {
- // Replace the default method directly to prevent recalculating and setting margins again
- // which has already been set by its parent.
- fn compute_used_inline_size(
- &self,
- block: &mut BlockFlow,
- shared_context: &SharedStyleContext,
- parent_flow_inline_size: Au,
- ) {
- let container_block_size = block.explicit_block_containing_size(shared_context);
- block.fragment.assign_replaced_inline_size_if_necessary(
- parent_flow_inline_size,
- container_block_size,
- );
- }
-
- // The used inline size and margins are set by parent flex flow, do nothing here.
- fn solve_inline_size_constraints(
- &self,
- block: &mut BlockFlow,
- _: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- let fragment = block.fragment();
- ISizeConstraintSolution::new(
- fragment.border_box.size.inline,
- fragment.margin.inline_start,
- fragment.margin.inline_end,
- )
- }
-}
diff --git a/components/layout_2020/construct.rs b/components/layout_2020/construct.rs
deleted file mode 100644
index 072413995b2..00000000000
--- a/components/layout_2020/construct.rs
+++ /dev/null
@@ -1,2443 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Creates flows and fragments from a DOM tree via a bottom-up, incremental traversal of the DOM.
-//!
-//! Each step of the traversal considers the node and existing flow, if there is one. If a node is
-//! not dirty and an existing flow exists, then the traversal reuses that flow. Otherwise, it
-//! proceeds to construct either a flow or a `ConstructionItem`. A construction item is a piece of
-//! intermediate data that goes with a DOM node and hasn't found its "home" yet-maybe it's a box,
-//! maybe it's an absolute or fixed position thing that hasn't found its containing block yet.
-//! Construction items bubble up the tree from children to parents until they find their homes.
-
-use crate::block::BlockFlow;
-use crate::context::{with_thread_local_font_context, LayoutContext};
-use crate::data::{LayoutData, LayoutDataFlags};
-use crate::display_list::items::OpaqueNode;
-use crate::flex::FlexFlow;
-use crate::floats::FloatKind;
-use crate::flow::{AbsoluteDescendants, Flow, FlowClass, GetBaseFlow, ImmutableFlowUtils};
-use crate::flow::{FlowFlags, MutableFlowUtils, MutableOwnedFlowUtils};
-use crate::flow_ref::FlowRef;
-use crate::fragment::{
- CanvasFragmentInfo, Fragment, FragmentFlags, GeneratedContentInfo, IframeFragmentInfo,
-};
-use crate::fragment::{
- ImageFragmentInfo, InlineAbsoluteFragmentInfo, InlineAbsoluteHypotheticalFragmentInfo,
-};
-use crate::fragment::{
- InlineBlockFragmentInfo, MediaFragmentInfo, SpecificFragmentInfo, SvgFragmentInfo,
-};
-use crate::fragment::{
- TableColumnFragmentInfo, UnscannedTextFragmentInfo, WhitespaceStrippingResult,
-};
-use crate::inline::{InlineFlow, InlineFragmentNodeFlags, InlineFragmentNodeInfo};
-use crate::linked_list::prepend_from;
-use crate::list_item::{ListItemFlow, ListStyleTypeContent};
-use crate::multicol::{MulticolColumnFlow, MulticolFlow};
-use crate::parallel;
-use crate::table::TableFlow;
-use crate::table_caption::TableCaptionFlow;
-use crate::table_cell::TableCellFlow;
-use crate::table_colgroup::TableColGroupFlow;
-use crate::table_row::TableRowFlow;
-use crate::table_rowgroup::TableRowGroupFlow;
-use crate::table_wrapper::TableWrapperFlow;
-use crate::text::TextRunScanner;
-use crate::traversal::PostorderNodeMutTraversal;
-use crate::wrapper::{LayoutNodeLayoutData, TextContent, ThreadSafeLayoutNodeHelpers};
-use crate::ServoArc;
-use script_layout_interface::wrapper_traits::{
- PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
-};
-use script_layout_interface::{LayoutElementType, LayoutNodeType};
-use servo_config::opts;
-use servo_url::ServoUrl;
-use std::collections::LinkedList;
-use std::marker::PhantomData;
-use std::mem;
-use std::sync::atomic::Ordering;
-use std::sync::Arc;
-use style::computed_values::caption_side::T as CaptionSide;
-use style::computed_values::display::T as Display;
-use style::computed_values::empty_cells::T as EmptyCells;
-use style::computed_values::float::T as Float;
-use style::computed_values::list_style_position::T as ListStylePosition;
-use style::computed_values::position::T as Position;
-use style::context::SharedStyleContext;
-use style::dom::TElement;
-use style::logical_geometry::Direction;
-use style::properties::ComputedValues;
-use style::selector_parser::{PseudoElement, RestyleDamage};
-use style::servo::restyle_damage::ServoRestyleDamage;
-use style::values::generics::counters::ContentItem;
-use style::values::generics::url::UrlOrNone as ImageUrlOrNone;
-
-/// The results of flow construction for a DOM node.
-#[derive(Clone)]
-pub enum ConstructionResult {
- /// This node contributes nothing at all (`display: none`). Alternately, this is what newly
- /// created nodes have their `ConstructionResult` set to.
- None,
-
- /// This node contributed a flow at the proper position in the tree.
- /// Nothing more needs to be done for this node. It has bubbled up fixed
- /// and absolute descendant flows that have a containing block above it.
- Flow(FlowRef, AbsoluteDescendants),
-
- /// This node contributed some object or objects that will be needed to construct a proper flow
- /// later up the tree, but these objects have not yet found their home.
- ConstructionItem(ConstructionItem),
-}
-
-impl ConstructionResult {
- pub fn get(&mut self) -> ConstructionResult {
- // FIXME(pcwalton): Stop doing this with inline fragments. Cloning fragments is very
- // inefficient!
- (*self).clone()
- }
-
- pub fn debug_id(&self) -> usize {
- match *self {
- ConstructionResult::None => 0,
- ConstructionResult::ConstructionItem(_) => 0,
- ConstructionResult::Flow(ref flow_ref, _) => flow_ref.base().debug_id(),
- }
- }
-}
-
-/// Represents the output of flow construction for a DOM node that has not yet resulted in a
-/// complete flow. Construction items bubble up the tree until they find a `Flow` to be attached
-/// to.
-#[derive(Clone)]
-pub enum ConstructionItem {
- /// Inline fragments and associated {ib} splits that have not yet found flows.
- InlineFragments(InlineFragmentsConstructionResult),
- /// Potentially ignorable whitespace.
- ///
- /// FIXME(emilio): How could whitespace have any PseudoElementType other
- /// than Normal?
- Whitespace(
- OpaqueNode,
- PseudoElementType,
- ServoArc<ComputedValues>,
- RestyleDamage,
- ),
- /// TableColumn Fragment
- TableColumnFragment(Fragment),
-}
-
-/// Represents inline fragments and {ib} splits that are bubbling up from an inline.
-#[derive(Clone)]
-pub struct InlineFragmentsConstructionResult {
- /// Any {ib} splits that we're bubbling up.
- pub splits: LinkedList<InlineBlockSplit>,
-
- /// Any fragments that succeed the {ib} splits.
- pub fragments: IntermediateInlineFragments,
-}
-
-/// Represents an {ib} split that has not yet found the containing block that it belongs to. This
-/// is somewhat tricky. An example may be helpful. For this DOM fragment:
-///
-/// ```html
-/// <span>
-/// A
-/// <div>B</div>
-/// C
-/// </span>
-/// ```
-///
-/// The resulting `ConstructionItem` for the outer `span` will be:
-///
-/// ```rust,ignore
-/// ConstructionItem::InlineFragments(
-/// InlineFragmentsConstructionResult {
-/// splits: linked_list![
-/// InlineBlockSplit {
-/// predecessors: IntermediateInlineFragments {
-/// fragments: linked_list![A],
-/// absolute_descendents: AbsoluteDescendents {
-/// descendant_links: vec![]
-/// },
-/// },
-/// flow: B,
-/// }
-/// ],
-/// fragments: linked_list![C],
-/// },
-/// )
-/// ```
-#[derive(Clone)]
-pub struct InlineBlockSplit {
- /// The inline fragments that precede the flow.
- pub predecessors: IntermediateInlineFragments,
-
- /// The flow that caused this {ib} split.
- pub flow: FlowRef,
-}
-
-impl InlineBlockSplit {
- /// Flushes the given accumulator to the new split and makes a new accumulator to hold any
- /// subsequent fragments.
- fn new<ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>(
- fragment_accumulator: &mut InlineFragmentsAccumulator,
- node: &ConcreteThreadSafeLayoutNode,
- style_context: &SharedStyleContext,
- flow: FlowRef,
- ) -> InlineBlockSplit {
- fragment_accumulator.enclosing_node.as_mut().expect(
- "enclosing_node is None; Are {ib} splits being generated outside of an inline node?"
- ).flags.remove(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT);
-
- let split = InlineBlockSplit {
- predecessors: mem::replace(
- fragment_accumulator,
- InlineFragmentsAccumulator::from_inline_node(node, style_context),
- )
- .to_intermediate_inline_fragments::<ConcreteThreadSafeLayoutNode>(style_context),
- flow: flow,
- };
-
- fragment_accumulator
- .enclosing_node
- .as_mut()
- .unwrap()
- .flags
- .remove(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT);
-
- split
- }
-}
-
-/// Holds inline fragments and absolute descendants.
-#[derive(Clone)]
-pub struct IntermediateInlineFragments {
- /// The list of fragments.
- pub fragments: LinkedList<Fragment>,
-
- /// The list of absolute descendants of those inline fragments.
- pub absolute_descendants: AbsoluteDescendants,
-}
-
-impl IntermediateInlineFragments {
- fn new() -> IntermediateInlineFragments {
- IntermediateInlineFragments {
- fragments: LinkedList::new(),
- absolute_descendants: AbsoluteDescendants::new(),
- }
- }
-
- fn is_empty(&self) -> bool {
- self.fragments.is_empty() && self.absolute_descendants.is_empty()
- }
-
- fn push_all(&mut self, mut other: IntermediateInlineFragments) {
- self.fragments.append(&mut other.fragments);
- self.absolute_descendants
- .push_descendants(other.absolute_descendants);
- }
-}
-
-/// Holds inline fragments that we're gathering for children of an inline node.
-struct InlineFragmentsAccumulator {
- /// The list of fragments.
- fragments: IntermediateInlineFragments,
-
- /// Information about the inline box directly enclosing the fragments being gathered, if any.
- ///
- /// `inline::InlineFragmentNodeInfo` also stores flags indicating whether a fragment is the
- /// first and/or last of the corresponding inline box. This `InlineFragmentsAccumulator` may
- /// represent only one side of an {ib} split, so we store these flags as if it represented only
- /// one fragment. `to_intermediate_inline_fragments` later splits this hypothetical fragment
- /// into pieces, leaving the `FIRST_FRAGMENT_OF_ELEMENT` and `LAST_FRAGMENT_OF_ELEMENT` flags,
- /// if present, on the first and last fragments of the output.
- enclosing_node: Option<InlineFragmentNodeInfo>,
-
- /// Restyle damage to use for fragments created in this node.
- restyle_damage: RestyleDamage,
-
- /// Bidi control characters to insert before and after these fragments.
- bidi_control_chars: Option<(&'static str, &'static str)>,
-}
-
-impl InlineFragmentsAccumulator {
- fn new() -> InlineFragmentsAccumulator {
- InlineFragmentsAccumulator {
- fragments: IntermediateInlineFragments::new(),
- enclosing_node: None,
- bidi_control_chars: None,
- restyle_damage: RestyleDamage::empty(),
- }
- }
-
- fn from_inline_node<N>(
- node: &N,
- style_context: &SharedStyleContext,
- ) -> InlineFragmentsAccumulator
- where
- N: ThreadSafeLayoutNode,
- {
- InlineFragmentsAccumulator {
- fragments: IntermediateInlineFragments::new(),
- enclosing_node: Some(InlineFragmentNodeInfo {
- address: node.opaque(),
- pseudo: node.get_pseudo_element_type(),
- style: node.style(style_context),
- selected_style: node.selected_style(),
- flags: InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT |
- InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT,
- }),
- bidi_control_chars: None,
- restyle_damage: node.restyle_damage(),
- }
- }
-
- fn push(&mut self, fragment: Fragment) {
- self.fragments.fragments.push_back(fragment)
- }
-
- fn push_all(&mut self, mut fragments: IntermediateInlineFragments) {
- self.fragments.fragments.append(&mut fragments.fragments);
- self.fragments
- .absolute_descendants
- .push_descendants(fragments.absolute_descendants);
- }
-
- fn to_intermediate_inline_fragments<N>(
- self,
- context: &SharedStyleContext,
- ) -> IntermediateInlineFragments
- where
- N: ThreadSafeLayoutNode,
- {
- let InlineFragmentsAccumulator {
- mut fragments,
- enclosing_node,
- bidi_control_chars,
- restyle_damage,
- } = self;
- if let Some(mut enclosing_node) = enclosing_node {
- let fragment_count = fragments.fragments.len();
- for (index, fragment) in fragments.fragments.iter_mut().enumerate() {
- let mut enclosing_node = enclosing_node.clone();
- if index != 0 {
- enclosing_node
- .flags
- .remove(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT)
- }
- if index != fragment_count - 1 {
- enclosing_node
- .flags
- .remove(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT)
- }
- fragment.add_inline_context_style(enclosing_node);
- }
-
- // Control characters are later discarded in transform_text, so they don't affect the
- // is_first/is_last styles above.
- enclosing_node.flags.remove(
- InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT |
- InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT,
- );
-
- if let Some((start, end)) = bidi_control_chars {
- fragments
- .fragments
- .push_front(control_chars_to_fragment::<N::ConcreteElement>(
- &enclosing_node,
- context,
- start,
- restyle_damage,
- ));
- fragments
- .fragments
- .push_back(control_chars_to_fragment::<N::ConcreteElement>(
- &enclosing_node,
- context,
- end,
- restyle_damage,
- ));
- }
- }
- fragments
- }
-}
-
-/// An object that knows how to create flows.
-pub struct FlowConstructor<'a, N: ThreadSafeLayoutNode> {
- /// The layout context.
- pub layout_context: &'a LayoutContext<'a>,
- /// Satisfy the compiler about the unused parameters, which we use to improve the ergonomics of
- /// the ensuing impl {} by removing the need to parameterize all the methods individually.
- phantom2: PhantomData<N>,
-}
-
-impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
- FlowConstructor<'a, ConcreteThreadSafeLayoutNode>
-{
- /// Creates a new flow constructor.
- pub fn new(layout_context: &'a LayoutContext<'a>) -> Self {
- FlowConstructor {
- layout_context: layout_context,
- phantom2: PhantomData,
- }
- }
-
- #[inline]
- fn style_context(&self) -> &SharedStyleContext {
- self.layout_context.shared_context()
- }
-
- #[inline]
- fn set_flow_construction_result(
- &self,
- node: &ConcreteThreadSafeLayoutNode,
- result: ConstructionResult,
- ) {
- node.set_flow_construction_result(result);
- }
-
- /// Builds the fragment for the given block or subclass thereof.
- fn build_fragment_for_block(&self, node: &ConcreteThreadSafeLayoutNode) -> Fragment {
- let specific_fragment_info = match node.type_id() {
- Some(LayoutNodeType::Element(LayoutElementType::HTMLIFrameElement)) => {
- SpecificFragmentInfo::Iframe(IframeFragmentInfo::new(node))
- },
- Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) => {
- let image_info = Box::new(ImageFragmentInfo::new(
- node.image_url(),
- node.image_density(),
- node,
- &self.layout_context,
- ));
- SpecificFragmentInfo::Image(image_info)
- },
- Some(LayoutNodeType::Element(LayoutElementType::HTMLMediaElement)) => {
- let data = node.media_data().unwrap();
- SpecificFragmentInfo::Media(Box::new(MediaFragmentInfo::new(data)))
- },
- Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => {
- let elem = node.as_element().unwrap();
- let type_and_data = (
- elem.get_attr(&ns!(), &local_name!("type")),
- elem.get_attr(&ns!(), &local_name!("data")),
- );
- let object_data = match type_and_data {
- (None, Some(uri)) if is_image_data(uri) => ServoUrl::parse(uri).ok(),
- _ => None,
- };
- let image_info = Box::new(ImageFragmentInfo::new(
- object_data,
- None,
- node,
- &self.layout_context,
- ));
- SpecificFragmentInfo::Image(image_info)
- },
- Some(LayoutNodeType::Element(LayoutElementType::HTMLTableElement)) => {
- SpecificFragmentInfo::TableWrapper
- },
- Some(LayoutNodeType::Element(LayoutElementType::HTMLTableColElement)) => {
- SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node))
- },
- Some(LayoutNodeType::Element(LayoutElementType::HTMLTableCellElement)) => {
- SpecificFragmentInfo::TableCell
- },
- Some(LayoutNodeType::Element(LayoutElementType::HTMLTableRowElement)) |
- Some(LayoutNodeType::Element(LayoutElementType::HTMLTableSectionElement)) => {
- SpecificFragmentInfo::TableRow
- },
- Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) => {
- let data = node.canvas_data().unwrap();
- SpecificFragmentInfo::Canvas(Box::new(CanvasFragmentInfo::new(data)))
- },
- Some(LayoutNodeType::Element(LayoutElementType::SVGSVGElement)) => {
- let data = node.svg_data().unwrap();
- SpecificFragmentInfo::Svg(Box::new(SvgFragmentInfo::new(data)))
- },
- _ => {
- // This includes pseudo-elements.
- SpecificFragmentInfo::Generic
- },
- };
-
- Fragment::new(node, specific_fragment_info, self.layout_context)
- }
-
- /// Creates an inline flow from a set of inline fragments, then adds it as a child of the given
- /// flow or pushes it onto the given flow list.
- ///
- /// `#[inline(always)]` because this is performance critical and LLVM will not inline it
- /// otherwise.
- #[inline(always)]
- fn flush_inline_fragments_to_flow(
- &mut self,
- fragment_accumulator: InlineFragmentsAccumulator,
- flow: &mut FlowRef,
- absolute_descendants: &mut AbsoluteDescendants,
- legalizer: &mut Legalizer,
- node: &ConcreteThreadSafeLayoutNode,
- ) {
- let mut fragments = fragment_accumulator
- .to_intermediate_inline_fragments::<ConcreteThreadSafeLayoutNode>(self.style_context());
- if fragments.is_empty() {
- return;
- };
-
- strip_ignorable_whitespace_from_start(&mut fragments.fragments);
- strip_ignorable_whitespace_from_end(&mut fragments.fragments);
- if fragments.fragments.is_empty() {
- absolute_descendants.push_descendants(fragments.absolute_descendants);
- return;
- }
-
- // Build a list of all the inline-block fragments before fragments is moved.
- let mut inline_block_flows = vec![];
- for fragment in &fragments.fragments {
- match fragment.specific {
- SpecificFragmentInfo::InlineBlock(ref info) => {
- inline_block_flows.push(info.flow_ref.clone())
- },
- SpecificFragmentInfo::InlineAbsoluteHypothetical(ref info) => {
- inline_block_flows.push(info.flow_ref.clone())
- },
- SpecificFragmentInfo::InlineAbsolute(ref info) => {
- inline_block_flows.push(info.flow_ref.clone())
- },
- _ => {},
- }
- }
-
- // We must scan for runs before computing minimum ascent and descent because scanning
- // for runs might collapse so much whitespace away that only hypothetical fragments
- // remain. In that case the inline flow will compute its ascent and descent to be zero.
- let scanned_fragments =
- with_thread_local_font_context(self.layout_context, |font_context| {
- TextRunScanner::new().scan_for_runs(
- font_context,
- mem::replace(&mut fragments.fragments, LinkedList::new()),
- )
- });
- let mut inline_flow_ref = FlowRef::new(Arc::new(InlineFlow::from_fragments(
- scanned_fragments,
- node.style(self.style_context()).writing_mode,
- )));
-
- // Add all the inline-block fragments as children of the inline flow.
- for inline_block_flow in &inline_block_flows {
- inline_flow_ref.add_new_child(inline_block_flow.clone());
- }
-
- // Set up absolute descendants as necessary.
- //
- // The inline flow itself may need to become the containing block for absolute descendants
- // in order to handle cases like:
- //
- // <div>
- // <span style="position: relative">
- // <span style="position: absolute; ..."></span>
- // </span>
- // </div>
- //
- // See the comment above `flow::AbsoluteDescendantInfo` for more information.
- inline_flow_ref.take_applicable_absolute_descendants(&mut fragments.absolute_descendants);
- absolute_descendants.push_descendants(fragments.absolute_descendants);
-
- {
- // FIXME(#6503): Use Arc::get_mut().unwrap() here.
- let inline_flow = FlowRef::deref_mut(&mut inline_flow_ref).as_mut_inline();
- inline_flow.minimum_line_metrics =
- with_thread_local_font_context(self.layout_context, |font_context| {
- inline_flow
- .minimum_line_metrics(font_context, &node.style(self.style_context()))
- });
- }
-
- inline_flow_ref.finish();
- legalizer.add_child::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
- self.style_context(),
- flow,
- inline_flow_ref,
- )
- }
-
- fn build_block_flow_using_construction_result_of_child(
- &mut self,
- flow: &mut FlowRef,
- node: &ConcreteThreadSafeLayoutNode,
- kid: ConcreteThreadSafeLayoutNode,
- inline_fragment_accumulator: &mut InlineFragmentsAccumulator,
- abs_descendants: &mut AbsoluteDescendants,
- legalizer: &mut Legalizer,
- ) {
- match kid.get_construction_result() {
- ConstructionResult::None => {},
- ConstructionResult::Flow(kid_flow, kid_abs_descendants) => {
- // If kid_flow is TableCaptionFlow, kid_flow should be added under
- // TableWrapperFlow.
- if flow.is_table() && kid_flow.is_table_caption() {
- let construction_result =
- ConstructionResult::Flow(kid_flow, AbsoluteDescendants::new());
- self.set_flow_construction_result(&kid, construction_result)
- } else {
- if !kid_flow
- .base()
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- // Flush any inline fragments that we were gathering up. This allows us to
- // handle {ib} splits.
- let old_inline_fragment_accumulator = mem::replace(
- inline_fragment_accumulator,
- InlineFragmentsAccumulator::new(),
- );
- self.flush_inline_fragments_to_flow(
- old_inline_fragment_accumulator,
- flow,
- abs_descendants,
- legalizer,
- node,
- );
- }
- legalizer.add_child::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
- self.style_context(),
- flow,
- kid_flow,
- )
- }
- abs_descendants.push_descendants(kid_abs_descendants);
- },
- ConstructionResult::ConstructionItem(ConstructionItem::InlineFragments(
- InlineFragmentsConstructionResult {
- splits,
- fragments: successor_fragments,
- },
- )) => {
- // Add any {ib} splits.
- for split in splits {
- // Pull apart the {ib} split object and push its predecessor fragments
- // onto the list.
- let InlineBlockSplit {
- predecessors,
- flow: kid_flow,
- } = split;
- inline_fragment_accumulator.push_all(predecessors);
-
- // Flush any inline fragments that we were gathering up.
- debug!(
- "flushing {} inline box(es) to flow A",
- inline_fragment_accumulator.fragments.fragments.len()
- );
- let old_inline_fragment_accumulator = mem::replace(
- inline_fragment_accumulator,
- InlineFragmentsAccumulator::new(),
- );
- let absolute_descendants =
- &mut inline_fragment_accumulator.fragments.absolute_descendants;
- self.flush_inline_fragments_to_flow(
- old_inline_fragment_accumulator,
- flow,
- absolute_descendants,
- legalizer,
- node,
- );
-
- // Push the flow generated by the {ib} split onto our list of flows.
- legalizer.add_child::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
- self.style_context(),
- flow,
- kid_flow,
- )
- }
-
- // Add the fragments to the list we're maintaining.
- inline_fragment_accumulator.push_all(successor_fragments);
- },
- ConstructionResult::ConstructionItem(ConstructionItem::Whitespace(
- whitespace_node,
- whitespace_pseudo,
- whitespace_style,
- whitespace_damage,
- )) => {
- // Add whitespace results. They will be stripped out later on when
- // between block elements, and retained when between inline elements.
- let fragment_info = SpecificFragmentInfo::UnscannedText(Box::new(
- UnscannedTextFragmentInfo::new(Box::<str>::from(" "), None),
- ));
- let fragment = Fragment::from_opaque_node_and_style(
- whitespace_node,
- whitespace_pseudo,
- whitespace_style,
- node.selected_style(),
- whitespace_damage,
- fragment_info,
- );
- inline_fragment_accumulator
- .fragments
- .fragments
- .push_back(fragment);
- },
- ConstructionResult::ConstructionItem(ConstructionItem::TableColumnFragment(_)) => {
- // TODO: Implement anonymous table objects for missing parents
- // CSS 2.1 § 17.2.1, step 3-2
- },
- }
- }
-
- /// Constructs a block flow, beginning with the given `initial_fragments` if present and then
- /// appending the construction results of children to the child list of the block flow. {ib}
- /// splits and absolutely-positioned descendants are handled correctly.
- fn build_flow_for_block_starting_with_fragments(
- &mut self,
- mut flow: FlowRef,
- node: &ConcreteThreadSafeLayoutNode,
- initial_fragments: IntermediateInlineFragments,
- ) -> ConstructionResult {
- // Gather up fragments for the inline flows we might need to create.
- let mut inline_fragment_accumulator = InlineFragmentsAccumulator::new();
-
- inline_fragment_accumulator
- .fragments
- .push_all(initial_fragments);
-
- // List of absolute descendants, in tree order.
- let mut abs_descendants = AbsoluteDescendants::new();
- let mut legalizer = Legalizer::new();
- let is_media_element_with_widget = node.type_id() ==
- Some(LayoutNodeType::Element(LayoutElementType::HTMLMediaElement)) &&
- node.as_element().unwrap().is_shadow_host();
- if !node.is_replaced_content() || is_media_element_with_widget {
- for kid in node.children() {
- if kid.get_pseudo_element_type() != PseudoElementType::Normal {
- if node.is_replaced_content() {
- // Replaced elements don't have pseudo-elements per spec.
- continue;
- }
- self.process(&kid);
- }
-
- self.build_block_flow_using_construction_result_of_child(
- &mut flow,
- node,
- kid,
- &mut inline_fragment_accumulator,
- &mut abs_descendants,
- &mut legalizer,
- );
- }
- }
-
- // Perform a final flush of any inline fragments that we were gathering up to handle {ib}
- // splits, after stripping ignorable whitespace.
- self.flush_inline_fragments_to_flow(
- inline_fragment_accumulator,
- &mut flow,
- &mut abs_descendants,
- &mut legalizer,
- node,
- );
-
- // The flow is done.
- legalizer.finish(&mut flow);
- flow.finish();
-
- // Set up the absolute descendants.
- if flow.is_absolute_containing_block() {
- // This is the containing block for all the absolute descendants.
- flow.set_absolute_descendants(abs_descendants);
-
- abs_descendants = AbsoluteDescendants::new();
- if flow
- .base()
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- // This is now the only absolute flow in the subtree which hasn't yet
- // reached its CB.
- abs_descendants.push(flow.clone());
- }
- }
- ConstructionResult::Flow(flow, abs_descendants)
- }
-
- /// Constructs a flow for the given block node and its children. This method creates an
- /// initial fragment as appropriate and then dispatches to
- /// `build_flow_for_block_starting_with_fragments`. Currently the following kinds of flows get
- /// initial content:
- ///
- /// * Generated content gets the initial content specified by the `content` attribute of the
- /// CSS.
- /// * `<input>` and `<textarea>` elements get their content.
- ///
- /// FIXME(pcwalton): It is not clear to me that there isn't a cleaner way to handle
- /// `<textarea>`.
- fn build_flow_for_block_like(
- &mut self,
- flow: FlowRef,
- node: &ConcreteThreadSafeLayoutNode,
- ) -> ConstructionResult {
- let mut fragments = IntermediateInlineFragments::new();
- let node_is_input_or_text_area = node.type_id() ==
- Some(LayoutNodeType::Element(LayoutElementType::HTMLInputElement)) ||
- node.type_id() ==
- Some(LayoutNodeType::Element(
- LayoutElementType::HTMLTextAreaElement,
- ));
- if node.get_pseudo_element_type().is_replaced_content() || node_is_input_or_text_area {
- // A TextArea's text contents are displayed through the input text
- // box, so don't construct them.
- if node.type_id() ==
- Some(LayoutNodeType::Element(
- LayoutElementType::HTMLTextAreaElement,
- ))
- {
- for kid in node.children() {
- self.set_flow_construction_result(&kid, ConstructionResult::None)
- }
- }
-
- let context = self.style_context();
- let mut style = node.style(context);
- style = context
- .stylist
- .style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
- &context.guards,
- &PseudoElement::ServoText,
- &style,
- );
- if node_is_input_or_text_area {
- style = context
- .stylist
- .style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
- &context.guards,
- &PseudoElement::ServoInputText,
- &style,
- )
- }
-
- self.create_fragments_for_node_text_content(&mut fragments, node, &style)
- }
- self.build_flow_for_block_starting_with_fragments(flow, node, fragments)
- }
-
- /// Pushes fragments appropriate for the content of the given node onto the given list.
- fn create_fragments_for_node_text_content(
- &self,
- fragments: &mut IntermediateInlineFragments,
- node: &ConcreteThreadSafeLayoutNode,
- style: &ServoArc<ComputedValues>,
- ) {
- // Fast path: If there is no text content, return immediately.
- let text_content = node.text_content();
- if text_content.is_empty() {
- return;
- }
-
- let style = (*style).clone();
- let selected_style = node.selected_style();
-
- match text_content {
- TextContent::Text(string) => {
- let info = Box::new(UnscannedTextFragmentInfo::new(string, node.selection()));
- let specific_fragment_info = SpecificFragmentInfo::UnscannedText(info);
- fragments
- .fragments
- .push_back(Fragment::from_opaque_node_and_style(
- node.opaque(),
- node.get_pseudo_element_type(),
- style,
- selected_style,
- node.restyle_damage(),
- specific_fragment_info,
- ))
- },
- TextContent::GeneratedContent(content_items) => {
- for content_item in content_items.into_iter() {
- let specific_fragment_info = match content_item {
- ContentItem::String(string) => {
- let info = Box::new(UnscannedTextFragmentInfo::new(string, None));
- SpecificFragmentInfo::UnscannedText(info)
- },
- content_item => {
- let content_item =
- Box::new(GeneratedContentInfo::ContentItem(content_item));
- SpecificFragmentInfo::GeneratedContent(content_item)
- },
- };
- fragments
- .fragments
- .push_back(Fragment::from_opaque_node_and_style(
- node.opaque(),
- node.get_pseudo_element_type(),
- style.clone(),
- selected_style.clone(),
- node.restyle_damage(),
- specific_fragment_info,
- ))
- }
- },
- }
- }
-
- /// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly
- /// other `BlockFlow`s or `InlineFlow`s underneath it, depending on whether {ib} splits needed
- /// to happen.
- fn build_flow_for_block(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- float_kind: Option<FloatKind>,
- ) -> ConstructionResult {
- if node.style(self.style_context()).is_multicol() {
- return self.build_flow_for_multicol(node, float_kind);
- }
-
- let fragment = self.build_fragment_for_block(node);
- let flow = FlowRef::new(Arc::new(BlockFlow::from_fragment_and_float_kind(
- fragment, float_kind,
- )));
- self.build_flow_for_block_like(flow, node)
- }
-
- /// Bubbles up {ib} splits.
- fn accumulate_inline_block_splits(
- &mut self,
- splits: LinkedList<InlineBlockSplit>,
- node: &ConcreteThreadSafeLayoutNode,
- fragment_accumulator: &mut InlineFragmentsAccumulator,
- opt_inline_block_splits: &mut LinkedList<InlineBlockSplit>,
- ) {
- for split in splits {
- let InlineBlockSplit {
- predecessors,
- flow: kid_flow,
- } = split;
- fragment_accumulator.push_all(predecessors);
-
- opt_inline_block_splits.push_back(InlineBlockSplit::new(
- fragment_accumulator,
- node,
- self.style_context(),
- kid_flow,
- ));
- }
- }
-
- /// Concatenates the fragments of kids, adding in our own borders/padding/margins if necessary.
- /// Returns the `InlineFragmentsConstructionResult`, if any. There will be no
- /// `InlineFragmentsConstructionResult` if this node consisted entirely of ignorable
- /// whitespace.
- fn build_fragments_for_nonreplaced_inline_content(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- ) -> ConstructionResult {
- let mut opt_inline_block_splits: LinkedList<InlineBlockSplit> = LinkedList::new();
- let mut fragment_accumulator =
- InlineFragmentsAccumulator::from_inline_node(node, self.style_context());
- fragment_accumulator.bidi_control_chars =
- bidi_control_chars(&node.style(self.style_context()));
-
- let mut abs_descendants = AbsoluteDescendants::new();
-
- // Concatenate all the fragments of our kids, creating {ib} splits as necessary.
- let mut is_empty = true;
- for kid in node.children() {
- is_empty = false;
- if kid.get_pseudo_element_type() != PseudoElementType::Normal {
- self.process(&kid);
- }
- match kid.get_construction_result() {
- ConstructionResult::None => {},
- ConstructionResult::Flow(flow, kid_abs_descendants) => {
- if !flow
- .base()
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- opt_inline_block_splits.push_back(InlineBlockSplit::new(
- &mut fragment_accumulator,
- node,
- self.style_context(),
- flow,
- ));
- abs_descendants.push_descendants(kid_abs_descendants);
- } else {
- // Push the absolutely-positioned kid as an inline containing block.
- let kid_node = flow.as_block().fragment.node;
- let kid_pseudo = flow.as_block().fragment.pseudo.clone();
- let kid_style = flow.as_block().fragment.style.clone();
- let kid_selected_style = flow.as_block().fragment.selected_style.clone();
- let kid_restyle_damage = flow.as_block().fragment.restyle_damage;
- let fragment_info = SpecificFragmentInfo::InlineAbsolute(
- InlineAbsoluteFragmentInfo::new(flow),
- );
- fragment_accumulator.push(Fragment::from_opaque_node_and_style(
- kid_node,
- kid_pseudo,
- kid_style,
- kid_selected_style,
- kid_restyle_damage,
- fragment_info,
- ));
- fragment_accumulator
- .fragments
- .absolute_descendants
- .push_descendants(kid_abs_descendants);
- }
- },
- ConstructionResult::ConstructionItem(ConstructionItem::InlineFragments(
- InlineFragmentsConstructionResult {
- splits,
- fragments: successors,
- },
- )) => {
- // Bubble up {ib} splits.
- self.accumulate_inline_block_splits(
- splits,
- node,
- &mut fragment_accumulator,
- &mut opt_inline_block_splits,
- );
-
- // Push residual fragments.
- fragment_accumulator.push_all(successors);
- },
- ConstructionResult::ConstructionItem(ConstructionItem::Whitespace(
- whitespace_node,
- whitespace_pseudo,
- whitespace_style,
- whitespace_damage,
- )) => {
- // Instantiate the whitespace fragment.
- let fragment_info = SpecificFragmentInfo::UnscannedText(Box::new(
- UnscannedTextFragmentInfo::new(Box::<str>::from(" "), None),
- ));
- let fragment = Fragment::from_opaque_node_and_style(
- whitespace_node,
- whitespace_pseudo,
- whitespace_style,
- node.selected_style(),
- whitespace_damage,
- fragment_info,
- );
- fragment_accumulator.fragments.fragments.push_back(fragment)
- },
- ConstructionResult::ConstructionItem(ConstructionItem::TableColumnFragment(_)) => {
- // TODO: Implement anonymous table objects for missing parents
- // CSS 2.1 § 17.2.1, step 3-2
- },
- }
- }
-
- let node_style = node.style(self.style_context());
- if is_empty && has_padding_or_border(&node_style) {
- // An empty inline box needs at least one fragment to draw its background and borders.
- let info = SpecificFragmentInfo::UnscannedText(Box::new(
- UnscannedTextFragmentInfo::new(Box::<str>::from(""), None),
- ));
- let fragment = Fragment::from_opaque_node_and_style(
- node.opaque(),
- node.get_pseudo_element_type(),
- node_style.clone(),
- node.selected_style(),
- node.restyle_damage(),
- info,
- );
- fragment_accumulator.fragments.fragments.push_back(fragment)
- }
-
- // Finally, make a new construction result.
- if opt_inline_block_splits.len() > 0 ||
- !fragment_accumulator.fragments.is_empty() ||
- abs_descendants.len() > 0
- {
- fragment_accumulator
- .fragments
- .absolute_descendants
- .push_descendants(abs_descendants);
-
- // If the node is positioned, then it's the containing block for all absolutely-
- // positioned descendants.
- if node_style.get_box().position != Position::Static {
- fragment_accumulator
- .fragments
- .absolute_descendants
- .mark_as_having_reached_containing_block();
- }
-
- let construction_item =
- ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
- splits: opt_inline_block_splits,
- fragments: fragment_accumulator
- .to_intermediate_inline_fragments::<ConcreteThreadSafeLayoutNode>(
- self.style_context(),
- ),
- });
- ConstructionResult::ConstructionItem(construction_item)
- } else {
- ConstructionResult::None
- }
- }
-
- /// Creates an `InlineFragmentsConstructionResult` for replaced content. Replaced content
- /// doesn't render its children, so this just nukes a child's fragments and creates a
- /// `Fragment`.
- fn build_fragments_for_replaced_inline_content(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- ) -> ConstructionResult {
- for kid in node.children() {
- self.set_flow_construction_result(&kid, ConstructionResult::None)
- }
-
- let context = self.style_context();
- let style = node.style(context);
- // If this node is ignorable whitespace, bail out now.
- if node.is_ignorable_whitespace(context) {
- return ConstructionResult::ConstructionItem(ConstructionItem::Whitespace(
- node.opaque(),
- node.get_pseudo_element_type(),
- context
- .stylist
- .style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
- &context.guards,
- &PseudoElement::ServoText,
- &style,
- ),
- node.restyle_damage(),
- ));
- }
-
- // If this is generated content, then we need to initialize the accumulator with the
- // fragment corresponding to that content. Otherwise, just initialize with the ordinary
- // fragment that needs to be generated for this inline node.
- let mut fragments = IntermediateInlineFragments::new();
- match (node.get_pseudo_element_type(), node.type_id()) {
- (_, Some(LayoutNodeType::Text)) => {
- let text_style = context
- .stylist
- .style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
- &context.guards,
- &PseudoElement::ServoText,
- &style,
- );
- self.create_fragments_for_node_text_content(&mut fragments, node, &text_style)
- },
- (PseudoElementType::Normal, _) => {
- fragments
- .fragments
- .push_back(self.build_fragment_for_block(node));
- },
- (_, _) => self.create_fragments_for_node_text_content(&mut fragments, node, &style),
- }
-
- let construction_item =
- ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
- splits: LinkedList::new(),
- fragments: fragments,
- });
- ConstructionResult::ConstructionItem(construction_item)
- }
-
- /// Build the fragment for an inline-block or inline-flex, based on the `display` flag
- fn build_fragment_for_inline_block_or_inline_flex(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- display: Display,
- ) -> ConstructionResult {
- let block_flow_result = match display {
- Display::InlineBlock => self.build_flow_for_block(node, None),
- Display::InlineFlex => self.build_flow_for_flex(node, None),
- _ => panic!("The flag should be inline-block or inline-flex"),
- };
- let (block_flow, abs_descendants) = match block_flow_result {
- ConstructionResult::Flow(block_flow, abs_descendants) => (block_flow, abs_descendants),
- _ => unreachable!(),
- };
-
- let context = self.style_context();
- let style = node.style(context);
- let style = context
- .stylist
- .style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
- &context.guards,
- &PseudoElement::ServoInlineBlockWrapper,
- &style,
- );
- let fragment_info =
- SpecificFragmentInfo::InlineBlock(InlineBlockFragmentInfo::new(block_flow));
- let fragment = Fragment::from_opaque_node_and_style(
- node.opaque(),
- node.get_pseudo_element_type(),
- style,
- node.selected_style(),
- node.restyle_damage(),
- fragment_info,
- );
-
- let mut fragment_accumulator = InlineFragmentsAccumulator::new();
- fragment_accumulator.fragments.fragments.push_back(fragment);
- fragment_accumulator
- .fragments
- .absolute_descendants
- .push_descendants(abs_descendants);
-
- let construction_item =
- ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
- splits: LinkedList::new(),
- fragments: fragment_accumulator
- .to_intermediate_inline_fragments::<ConcreteThreadSafeLayoutNode>(context),
- });
- ConstructionResult::ConstructionItem(construction_item)
- }
-
- /// This is an annoying case, because the computed `display` value is `block`, but the
- /// hypothetical box is inline.
- fn build_fragment_for_absolutely_positioned_inline(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- ) -> ConstructionResult {
- let block_flow_result = self.build_flow_for_block(node, None);
- let (block_flow, abs_descendants) = match block_flow_result {
- ConstructionResult::Flow(block_flow, abs_descendants) => (block_flow, abs_descendants),
- _ => unreachable!(),
- };
-
- let fragment_info = SpecificFragmentInfo::InlineAbsoluteHypothetical(
- InlineAbsoluteHypotheticalFragmentInfo::new(block_flow),
- );
- let style_context = self.style_context();
- let style = node.style(style_context);
- let style = style_context
- .stylist
- .style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
- &style_context.guards,
- &PseudoElement::ServoInlineAbsolute,
- &style,
- );
- let fragment = Fragment::from_opaque_node_and_style(
- node.opaque(),
- PseudoElementType::Normal,
- style,
- node.selected_style(),
- node.restyle_damage(),
- fragment_info,
- );
-
- let mut fragment_accumulator =
- InlineFragmentsAccumulator::from_inline_node(node, self.style_context());
- fragment_accumulator.fragments.fragments.push_back(fragment);
- fragment_accumulator
- .fragments
- .absolute_descendants
- .push_descendants(abs_descendants);
-
- let construction_item =
- ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
- splits: LinkedList::new(),
- fragments: fragment_accumulator
- .to_intermediate_inline_fragments::<ConcreteThreadSafeLayoutNode>(
- style_context,
- ),
- });
- ConstructionResult::ConstructionItem(construction_item)
- }
-
- /// Builds one or more fragments for a node with `display: inline`. This yields an
- /// `InlineFragmentsConstructionResult`.
- fn build_fragments_for_inline(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- ) -> ConstructionResult {
- // Is this node replaced content?
- if !node.is_replaced_content() {
- // Go to a path that concatenates our kids' fragments.
- self.build_fragments_for_nonreplaced_inline_content(node)
- } else {
- // Otherwise, just nuke our kids' fragments, create our fragment if any, and be done
- // with it.
- self.build_fragments_for_replaced_inline_content(node)
- }
- }
-
- /// Places any table captions found under the given table wrapper, if the value of their
- /// `caption-side` property is equal to the given `side`.
- fn place_table_caption_under_table_wrapper_on_side(
- &mut self,
- table_wrapper_flow: &mut FlowRef,
- node: &ConcreteThreadSafeLayoutNode,
- side: CaptionSide,
- ) {
- // Only flows that are table captions are matched here.
- for kid in node.children() {
- match kid.get_construction_result() {
- ConstructionResult::Flow(kid_flow, _) => {
- if kid_flow.is_table_caption() &&
- kid_flow
- .as_block()
- .fragment
- .style()
- .get_inherited_table()
- .caption_side ==
- side
- {
- table_wrapper_flow.add_new_child(kid_flow);
- }
- },
- ConstructionResult::None | ConstructionResult::ConstructionItem(_) => {},
- }
- }
- }
-
- /// Builds a flow for a node with `column-count` or `column-width` non-`auto`.
- /// This yields a `MulticolFlow` with a single `MulticolColumnFlow` underneath it.
- fn build_flow_for_multicol(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- float_kind: Option<FloatKind>,
- ) -> ConstructionResult {
- let fragment = Fragment::new(node, SpecificFragmentInfo::Multicol, self.layout_context);
- let mut flow = FlowRef::new(Arc::new(MulticolFlow::from_fragment(fragment, float_kind)));
-
- let column_fragment = Fragment::new(
- node,
- SpecificFragmentInfo::MulticolColumn,
- self.layout_context,
- );
- let column_flow =
- FlowRef::new(Arc::new(MulticolColumnFlow::from_fragment(column_fragment)));
-
- // First populate the column flow with its children.
- let construction_result = self.build_flow_for_block_like(column_flow, node);
-
- let mut abs_descendants = AbsoluteDescendants::new();
-
- if let ConstructionResult::Flow(column_flow, column_abs_descendants) = construction_result {
- flow.add_new_child(column_flow);
- abs_descendants.push_descendants(column_abs_descendants);
- }
-
- // The flow is done.
- flow.finish();
- if flow.is_absolute_containing_block() {
- // This is the containing block for all the absolute descendants.
- flow.set_absolute_descendants(abs_descendants);
-
- abs_descendants = AbsoluteDescendants::new();
-
- if flow
- .base()
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- // This is now the only absolute flow in the subtree which hasn't yet
- // reached its containing block.
- abs_descendants.push(flow.clone());
- }
- }
-
- ConstructionResult::Flow(flow, abs_descendants)
- }
-
- /// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with
- /// possibly other `TableCaptionFlow`s or `TableFlow`s underneath it.
- fn build_flow_for_table(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- float_value: Float,
- ) -> ConstructionResult {
- let mut legalizer = Legalizer::new();
-
- let table_style;
- let wrapper_style;
- {
- let context = self.style_context();
- table_style = node.style(context);
- wrapper_style = context
- .stylist
- .style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
- &context.guards,
- &PseudoElement::ServoTableWrapper,
- &table_style,
- );
- }
- let wrapper_fragment = Fragment::from_opaque_node_and_style(
- node.opaque(),
- PseudoElementType::Normal,
- wrapper_style,
- node.selected_style(),
- node.restyle_damage(),
- SpecificFragmentInfo::TableWrapper,
- );
- let wrapper_float_kind = FloatKind::from_property(float_value);
- let mut wrapper_flow = FlowRef::new(Arc::new(
- TableWrapperFlow::from_fragment_and_float_kind(wrapper_fragment, wrapper_float_kind),
- ));
-
- let table_fragment = Fragment::new(node, SpecificFragmentInfo::Table, self.layout_context);
- let table_flow = FlowRef::new(Arc::new(TableFlow::from_fragment(table_fragment)));
-
- // First populate the table flow with its children.
- let construction_result = self.build_flow_for_block_like(table_flow, node);
-
- let mut abs_descendants = AbsoluteDescendants::new();
-
- // The order of the caption and the table are not necessarily the same order as in the DOM
- // tree. All caption blocks are placed before or after the table flow, depending on the
- // value of `caption-side`.
- self.place_table_caption_under_table_wrapper_on_side(
- &mut wrapper_flow,
- node,
- CaptionSide::Top,
- );
-
- if let ConstructionResult::Flow(table_flow, table_abs_descendants) = construction_result {
- legalizer.add_child::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
- self.style_context(),
- &mut wrapper_flow,
- table_flow,
- );
- abs_descendants.push_descendants(table_abs_descendants);
- }
-
- // If the value of `caption-side` is `bottom`, place it now.
- self.place_table_caption_under_table_wrapper_on_side(
- &mut wrapper_flow,
- node,
- CaptionSide::Bottom,
- );
-
- // The flow is done.
- legalizer.finish(&mut wrapper_flow);
- wrapper_flow.finish();
-
- if wrapper_flow.is_absolute_containing_block() {
- // This is the containing block for all the absolute descendants.
- wrapper_flow.set_absolute_descendants(abs_descendants);
-
- abs_descendants = AbsoluteDescendants::new();
-
- if wrapper_flow
- .base()
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- // This is now the only absolute flow in the subtree which hasn't yet
- // reached its containing block.
- abs_descendants.push(wrapper_flow.clone());
- }
- }
-
- ConstructionResult::Flow(wrapper_flow, abs_descendants)
- }
-
- /// Builds a flow for a node with `display: table-caption`. This yields a `TableCaptionFlow`
- /// with possibly other `BlockFlow`s or `InlineFlow`s underneath it.
- fn build_flow_for_table_caption(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- ) -> ConstructionResult {
- let fragment = self.build_fragment_for_block(node);
- let flow = FlowRef::new(Arc::new(TableCaptionFlow::from_fragment(fragment)));
- self.build_flow_for_block_like(flow, node)
- }
-
- /// Builds a flow for a node with `display: table-row-group`. This yields a `TableRowGroupFlow`
- /// with possibly other `TableRowFlow`s underneath it.
- fn build_flow_for_table_rowgroup(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- ) -> ConstructionResult {
- let fragment = Fragment::new(node, SpecificFragmentInfo::TableRow, self.layout_context);
- let flow = FlowRef::new(Arc::new(TableRowGroupFlow::from_fragment(fragment)));
- self.build_flow_for_block_like(flow, node)
- }
-
- /// Builds a flow for a node with `display: table-row`. This yields a `TableRowFlow` with
- /// possibly other `TableCellFlow`s underneath it.
- fn build_flow_for_table_row(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- ) -> ConstructionResult {
- let fragment = Fragment::new(node, SpecificFragmentInfo::TableRow, self.layout_context);
- let flow = FlowRef::new(Arc::new(TableRowFlow::from_fragment(fragment)));
- self.build_flow_for_block_like(flow, node)
- }
-
- /// Builds a flow for a node with `display: table-cell`. This yields a `TableCellFlow` with
- /// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
- fn build_flow_for_table_cell(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- ) -> ConstructionResult {
- let fragment = Fragment::new(node, SpecificFragmentInfo::TableCell, self.layout_context);
-
- // Determine if the table cell should be hidden. Per CSS 2.1 § 17.6.1.1, this will be true
- // if the cell has any in-flow elements (even empty ones!) and has `empty-cells` set to
- // `hide`.
- let hide = node
- .style(self.style_context())
- .get_inherited_table()
- .empty_cells ==
- EmptyCells::Hide &&
- node.children().all(|kid| {
- let position = kid.style(self.style_context()).get_box().position;
- !kid.is_content() || position == Position::Absolute || position == Position::Fixed
- });
-
- let flow = FlowRef::new(Arc::new(
- TableCellFlow::from_node_fragment_and_visibility_flag(node, fragment, !hide),
- ));
- self.build_flow_for_block_like(flow, node)
- }
-
- /// Builds a flow for a node with `display: list-item`. This yields a `ListItemFlow` with
- /// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
- fn build_flow_for_list_item(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- flotation: Float,
- ) -> ConstructionResult {
- let flotation = FloatKind::from_property(flotation);
- let marker_fragments = match node.style(self.style_context()).get_list().list_style_image {
- ImageUrlOrNone::Url(ref url_value) => {
- let image_info = Box::new(ImageFragmentInfo::new(
- url_value.url().map(|u| u.clone()),
- None,
- node,
- &self.layout_context,
- ));
- vec![Fragment::new(
- node,
- SpecificFragmentInfo::Image(image_info),
- self.layout_context,
- )]
- },
- ImageUrlOrNone::None => match ListStyleTypeContent::from_list_style_type(
- node.style(self.style_context()).get_list().list_style_type,
- ) {
- ListStyleTypeContent::None => Vec::new(),
- ListStyleTypeContent::StaticText(ch) => {
- let text = format!("{}\u{a0}", ch);
- let mut unscanned_marker_fragments = LinkedList::new();
- unscanned_marker_fragments.push_back(Fragment::new(
- node,
- SpecificFragmentInfo::UnscannedText(Box::new(
- UnscannedTextFragmentInfo::new(Box::<str>::from(text), None),
- )),
- self.layout_context,
- ));
- let marker_fragments =
- with_thread_local_font_context(self.layout_context, |mut font_context| {
- TextRunScanner::new()
- .scan_for_runs(&mut font_context, unscanned_marker_fragments)
- });
- marker_fragments.fragments
- },
- ListStyleTypeContent::GeneratedContent(info) => vec![Fragment::new(
- node,
- SpecificFragmentInfo::GeneratedContent(info),
- self.layout_context,
- )],
- },
- };
-
- // If the list marker is outside, it becomes the special "outside fragment" that list item
- // flows have. If it's inside, it's just a plain old fragment. Note that this means that
- // we adopt Gecko's behavior rather than WebKit's when the marker causes an {ib} split,
- // which has caused some malaise (Bugzilla #36854) but CSS 2.1 § 12.5.1 lets me do it, so
- // there.
- let mut initial_fragments = IntermediateInlineFragments::new();
- let main_fragment = self.build_fragment_for_block(node);
- let flow = match node
- .style(self.style_context())
- .get_list()
- .list_style_position
- {
- ListStylePosition::Outside => Arc::new(ListItemFlow::from_fragments_and_flotation(
- main_fragment,
- marker_fragments,
- flotation,
- )),
- ListStylePosition::Inside => {
- for marker_fragment in marker_fragments {
- initial_fragments.fragments.push_back(marker_fragment)
- }
- Arc::new(ListItemFlow::from_fragments_and_flotation(
- main_fragment,
- vec![],
- flotation,
- ))
- },
- };
-
- self.build_flow_for_block_starting_with_fragments(
- FlowRef::new(flow),
- node,
- initial_fragments,
- )
- }
-
- /// Creates a fragment for a node with `display: table-column`.
- fn build_fragments_for_table_column(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- ) -> ConstructionResult {
- // CSS 2.1 § 17.2.1. Treat all child fragments of a `table-column` as `display: none`.
- for kid in node.children() {
- self.set_flow_construction_result(&kid, ConstructionResult::None)
- }
-
- let specific = SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node));
- let construction_item = ConstructionItem::TableColumnFragment(Fragment::new(
- node,
- specific,
- self.layout_context,
- ));
- ConstructionResult::ConstructionItem(construction_item)
- }
-
- /// Builds a flow for a node with `display: table-column-group`.
- /// This yields a `TableColGroupFlow`.
- fn build_flow_for_table_colgroup(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- ) -> ConstructionResult {
- let fragment = Fragment::new(
- node,
- SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node)),
- self.layout_context,
- );
- let mut col_fragments = vec![];
- for kid in node.children() {
- // CSS 2.1 § 17.2.1. Treat all non-column child fragments of `table-column-group`
- // as `display: none`.
- if let ConstructionResult::ConstructionItem(ConstructionItem::TableColumnFragment(
- fragment,
- )) = kid.get_construction_result()
- {
- col_fragments.push(fragment)
- }
- }
- if col_fragments.is_empty() {
- debug!("add SpecificFragmentInfo::TableColumn for empty colgroup");
- let specific = SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node));
- col_fragments.push(Fragment::new(node, specific, self.layout_context));
- }
- let mut flow = FlowRef::new(Arc::new(TableColGroupFlow::from_fragments(
- fragment,
- col_fragments,
- )));
- flow.finish();
-
- ConstructionResult::Flow(flow, AbsoluteDescendants::new())
- }
-
- /// Builds a flow for a node with 'display: flex'.
- fn build_flow_for_flex(
- &mut self,
- node: &ConcreteThreadSafeLayoutNode,
- float_kind: Option<FloatKind>,
- ) -> ConstructionResult {
- let fragment = self.build_fragment_for_block(node);
- let flow = FlowRef::new(Arc::new(FlexFlow::from_fragment(fragment, float_kind)));
- self.build_flow_for_block_like(flow, node)
- }
-
- /// Attempts to perform incremental repair to account for recent changes to this node. This
- /// can fail and return false, indicating that flows will need to be reconstructed.
- ///
- /// TODO(pcwalton): Add some more fast paths, like toggling `display: none`, adding block kids
- /// to block parents with no {ib} splits, adding out-of-flow kids, etc.
- pub fn repair_if_possible(&mut self, node: &ConcreteThreadSafeLayoutNode) -> bool {
- // We can skip reconstructing the flow if we don't have to reconstruct and none of our kids
- // did either.
- //
- // We visit the kids first and reset their HAS_NEWLY_CONSTRUCTED_FLOW flags after checking
- // them. NOTE: Make sure not to bail out early before resetting all the flags!
- let mut need_to_reconstruct = false;
-
- // If the node has display: none, it's possible that we haven't even
- // styled the children once, so we need to bailout early here.
- if node.style(self.style_context()).get_box().clone_display() == Display::None {
- return false;
- }
-
- for kid in node.children() {
- if kid
- .flags()
- .contains(LayoutDataFlags::HAS_NEWLY_CONSTRUCTED_FLOW)
- {
- kid.remove_flags(LayoutDataFlags::HAS_NEWLY_CONSTRUCTED_FLOW);
- need_to_reconstruct = true
- }
- }
-
- if need_to_reconstruct {
- return false;
- }
-
- if node
- .restyle_damage()
- .contains(ServoRestyleDamage::RECONSTRUCT_FLOW)
- {
- return false;
- }
-
- let mut set_has_newly_constructed_flow_flag = false;
- let result = {
- let style = node.style(self.style_context());
-
- if style.can_be_fragmented() || style.is_multicol() {
- return false;
- }
-
- let damage = node.restyle_damage();
- let mut data = node.mutate_layout_data().unwrap();
-
- match *node.construction_result_mut(&mut *data) {
- ConstructionResult::None => true,
- ConstructionResult::Flow(ref mut flow, _) => {
- // The node's flow is of the same type and has the same set of children and can
- // therefore be repaired by simply propagating damage and style to the flow.
- if !flow.is_block_flow() {
- return false;
- }
-
- let flow = FlowRef::deref_mut(flow);
- flow.mut_base().restyle_damage.insert(damage);
- flow.repair_style_and_bubble_inline_sizes(&style);
- true
- },
- ConstructionResult::ConstructionItem(ConstructionItem::InlineFragments(
- ref mut inline_fragments_construction_result,
- )) => {
- if !inline_fragments_construction_result.splits.is_empty() {
- return false;
- }
-
- for fragment in inline_fragments_construction_result
- .fragments
- .fragments
- .iter_mut()
- {
- // Only mutate the styles of fragments that represent the dirty node (including
- // pseudo-element).
- if fragment.node != node.opaque() {
- continue;
- }
- if fragment.pseudo != node.get_pseudo_element_type() {
- continue;
- }
-
- match fragment.specific {
- SpecificFragmentInfo::InlineBlock(ref mut inline_block_fragment) => {
- let flow_ref =
- FlowRef::deref_mut(&mut inline_block_fragment.flow_ref);
- flow_ref.mut_base().restyle_damage.insert(damage);
- // FIXME(pcwalton): Fragment restyle damage too?
- flow_ref.repair_style_and_bubble_inline_sizes(&style);
- },
- SpecificFragmentInfo::InlineAbsoluteHypothetical(
- ref mut inline_absolute_hypothetical_fragment,
- ) => {
- let flow_ref = FlowRef::deref_mut(
- &mut inline_absolute_hypothetical_fragment.flow_ref,
- );
- flow_ref.mut_base().restyle_damage.insert(damage);
- // FIXME(pcwalton): Fragment restyle damage too?
- flow_ref.repair_style_and_bubble_inline_sizes(&style);
- },
- SpecificFragmentInfo::InlineAbsolute(
- ref mut inline_absolute_fragment,
- ) => {
- let flow_ref =
- FlowRef::deref_mut(&mut inline_absolute_fragment.flow_ref);
- flow_ref.mut_base().restyle_damage.insert(damage);
- // FIXME(pcwalton): Fragment restyle damage too?
- flow_ref.repair_style_and_bubble_inline_sizes(&style);
- },
- SpecificFragmentInfo::ScannedText(_) => {
- // Text fragments in ConstructionResult haven't been scanned yet
- unreachable!()
- },
- SpecificFragmentInfo::GeneratedContent(_) |
- SpecificFragmentInfo::UnscannedText(_) => {
- // We can't repair this unscanned text; we need to update the
- // scanned text fragments.
- //
- // TODO: Add code to find and repair the ScannedText fragments?
- return false;
- },
- _ => {
- fragment.repair_style(&style);
- set_has_newly_constructed_flow_flag = true;
- },
- }
- }
- true
- },
- ConstructionResult::ConstructionItem(_) => false,
- }
- };
- if set_has_newly_constructed_flow_flag {
- node.insert_flags(LayoutDataFlags::HAS_NEWLY_CONSTRUCTED_FLOW);
- }
- return result;
- }
-}
-
-impl<'a, ConcreteThreadSafeLayoutNode> PostorderNodeMutTraversal<ConcreteThreadSafeLayoutNode>
- for FlowConstructor<'a, ConcreteThreadSafeLayoutNode>
-where
- ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode,
-{
- // Construct Flow based on 'display', 'position', and 'float' values.
- //
- // CSS 2.1 Section 9.7
- //
- // TODO: This should actually consult the table in that section to get the
- // final computed value for 'display'.
- fn process(&mut self, node: &ConcreteThreadSafeLayoutNode) {
- node.insert_flags(LayoutDataFlags::HAS_NEWLY_CONSTRUCTED_FLOW);
-
- let style = node.style(self.style_context());
-
- // Bail out if this node is display: none. The style system guarantees
- // that we don't arrive here for children of those.
- if style.get_box().display.is_none() {
- self.set_flow_construction_result(node, ConstructionResult::None);
- return;
- }
-
- // Get the `display` property for this node, and determine whether this node is floated.
- let (display, float, positioning) = match node.type_id() {
- None => {
- // Pseudo-element.
- (
- style.get_box().display,
- style.get_box().float,
- style.get_box().position,
- )
- },
- Some(LayoutNodeType::Element(_)) => {
- let original_display = style.get_box().original_display;
- // FIXME(emilio, #19771): This munged_display business is pretty
- // wrong. After we fix this we should be able to unify the
- // pseudo-element path too.
- let munged_display = match original_display {
- Display::Inline | Display::InlineBlock => original_display,
- _ => style.get_box().display,
- };
- (
- munged_display,
- style.get_box().float,
- style.get_box().position,
- )
- },
- Some(LayoutNodeType::Text) => (Display::Inline, Float::None, Position::Static),
- };
-
- debug!(
- "building flow for node: {:?} {:?} {:?} {:?}",
- display,
- float,
- positioning,
- node.type_id()
- );
-
- // Switch on display and floatedness.
- match (display, float, positioning) {
- // `display: none` contributes no flow construction result.
- (Display::None, _, _) => {
- self.set_flow_construction_result(node, ConstructionResult::None);
- },
-
- // Table items contribute table flow construction results.
- (Display::Table, float_value, _) => {
- let construction_result = self.build_flow_for_table(node, float_value);
- self.set_flow_construction_result(node, construction_result)
- },
-
- // Absolutely positioned elements will have computed value of
- // `float` as 'none' and `display` as per the table.
- // Only match here for block items. If an item is absolutely
- // positioned, but inline we shouldn't try to construct a block
- // flow here - instead, let it match the inline case
- // below.
- (Display::Block, _, Position::Absolute) | (Display::Block, _, Position::Fixed) => {
- let construction_result = self.build_flow_for_block(node, None);
- self.set_flow_construction_result(node, construction_result)
- },
-
- // List items contribute their own special flows.
- (Display::ListItem, float_value, _) => {
- let construction_result = self.build_flow_for_list_item(node, float_value);
- self.set_flow_construction_result(node, construction_result)
- },
-
- // Inline items that are absolutely-positioned contribute inline fragment construction
- // results with a hypothetical fragment.
- (Display::Inline, _, Position::Absolute) |
- (Display::InlineBlock, _, Position::Absolute) => {
- let construction_result =
- self.build_fragment_for_absolutely_positioned_inline(node);
- self.set_flow_construction_result(node, construction_result)
- },
-
- // Inline items contribute inline fragment construction results.
- //
- // FIXME(pcwalton, #3307): This is not sufficient to handle floated generated content.
- (Display::Inline, Float::None, _) => {
- let construction_result = self.build_fragments_for_inline(node);
- self.set_flow_construction_result(node, construction_result)
- },
-
- // Inline-block items contribute inline fragment construction results.
- (Display::InlineBlock, Float::None, _) => {
- let construction_result =
- self.build_fragment_for_inline_block_or_inline_flex(node, Display::InlineBlock);
- self.set_flow_construction_result(node, construction_result)
- },
-
- // Table items contribute table flow construction results.
- (Display::TableCaption, _, _) => {
- let construction_result = self.build_flow_for_table_caption(node);
- self.set_flow_construction_result(node, construction_result)
- },
-
- // Table items contribute table flow construction results.
- (Display::TableColumnGroup, _, _) => {
- let construction_result = self.build_flow_for_table_colgroup(node);
- self.set_flow_construction_result(node, construction_result)
- },
-
- // Table items contribute table flow construction results.
- (Display::TableColumn, _, _) => {
- let construction_result = self.build_fragments_for_table_column(node);
- self.set_flow_construction_result(node, construction_result)
- },
-
- // Table items contribute table flow construction results.
- (Display::TableRowGroup, _, _) |
- (Display::TableHeaderGroup, _, _) |
- (Display::TableFooterGroup, _, _) => {
- let construction_result = self.build_flow_for_table_rowgroup(node);
- self.set_flow_construction_result(node, construction_result)
- },
-
- // Table items contribute table flow construction results.
- (Display::TableRow, _, _) => {
- let construction_result = self.build_flow_for_table_row(node);
- self.set_flow_construction_result(node, construction_result)
- },
-
- // Table items contribute table flow construction results.
- (Display::TableCell, _, _) => {
- let construction_result = self.build_flow_for_table_cell(node);
- self.set_flow_construction_result(node, construction_result)
- },
-
- // Flex items contribute flex flow construction results.
- (Display::Flex, float_value, _) => {
- let float_kind = FloatKind::from_property(float_value);
- let construction_result = self.build_flow_for_flex(node, float_kind);
- self.set_flow_construction_result(node, construction_result)
- },
-
- (Display::InlineFlex, _, _) => {
- let construction_result =
- self.build_fragment_for_inline_block_or_inline_flex(node, Display::InlineFlex);
- self.set_flow_construction_result(node, construction_result)
- },
-
- // Block flows that are not floated contribute block flow construction results.
- //
- // TODO(pcwalton): Make this only trigger for blocks and handle the other `display`
- // properties separately.
- (_, float_value, _) => {
- let float_kind = FloatKind::from_property(float_value);
- let construction_result = self.build_flow_for_block(node, float_kind);
- self.set_flow_construction_result(node, construction_result)
- },
- }
- }
-}
-
-/// A utility trait with some useful methods for node queries.
-trait NodeUtils {
- /// Returns true if this node doesn't render its kids and false otherwise.
- fn is_replaced_content(&self) -> bool;
-
- fn construction_result_mut(self, layout_data: &mut LayoutData) -> &mut ConstructionResult;
-
- /// Sets the construction result of a flow.
- fn set_flow_construction_result(self, result: ConstructionResult);
-
- /// Returns the construction result for this node.
- fn get_construction_result(self) -> ConstructionResult;
-}
-
-impl<ConcreteThreadSafeLayoutNode> NodeUtils for ConcreteThreadSafeLayoutNode
-where
- ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode,
-{
- fn is_replaced_content(&self) -> bool {
- match self.type_id() {
- Some(LayoutNodeType::Text) |
- Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) |
- Some(LayoutNodeType::Element(LayoutElementType::HTMLMediaElement)) |
- Some(LayoutNodeType::Element(LayoutElementType::HTMLIFrameElement)) |
- Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) |
- Some(LayoutNodeType::Element(LayoutElementType::SVGSVGElement)) => true,
- Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => {
- let elem = self.as_element().unwrap();
- let type_and_data = (
- elem.get_attr(&ns!(), &local_name!("type")),
- elem.get_attr(&ns!(), &local_name!("data")),
- );
- match type_and_data {
- (None, Some(uri)) => is_image_data(uri),
- _ => false,
- }
- },
- Some(LayoutNodeType::Element(_)) => false,
- None => self.get_pseudo_element_type().is_replaced_content(),
- }
- }
-
- fn construction_result_mut(self, data: &mut LayoutData) -> &mut ConstructionResult {
- match self.get_pseudo_element_type() {
- PseudoElementType::Before => &mut data.before_flow_construction_result,
- PseudoElementType::After => &mut data.after_flow_construction_result,
- PseudoElementType::DetailsSummary => &mut data.details_summary_flow_construction_result,
- PseudoElementType::DetailsContent => &mut data.details_content_flow_construction_result,
- PseudoElementType::Normal => &mut data.flow_construction_result,
- }
- }
-
- #[inline(always)]
- fn set_flow_construction_result(self, result: ConstructionResult) {
- let mut layout_data = self.mutate_layout_data().unwrap();
- let dst = self.construction_result_mut(&mut *layout_data);
- *dst = result;
- }
-
- #[inline(always)]
- fn get_construction_result(self) -> ConstructionResult {
- let mut layout_data = self.mutate_layout_data().unwrap();
- self.construction_result_mut(&mut *layout_data).get()
- }
-}
-
-impl FlowRef {
- /// Adds a new flow as a child of this flow. Fails if this flow is marked as a leaf.
- fn add_new_child(&mut self, mut new_child: FlowRef) {
- {
- let kid_base = FlowRef::deref_mut(&mut new_child).mut_base();
- kid_base.parallel.parent = parallel::mut_owned_flow_to_unsafe_flow(self);
- }
-
- let base = FlowRef::deref_mut(self).mut_base();
- base.children.push_back(new_child);
- let _ = base.parallel.children_count.fetch_add(1, Ordering::Relaxed);
- }
-
- /// Finishes a flow. Once a flow is finished, no more child flows or fragments may be added to
- /// it. This will normally run the bubble-inline-sizes (minimum and preferred -- i.e. intrinsic
- /// -- inline-size) calculation, unless the global `bubble_inline-sizes_separately` flag is on.
- ///
- /// All flows must be finished at some point, or they will not have their intrinsic inline-sizes
- /// properly computed. (This is not, however, a memory safety problem.)
- fn finish(&mut self) {
- if !opts::get().bubble_inline_sizes_separately {
- FlowRef::deref_mut(self).bubble_inline_sizes();
- FlowRef::deref_mut(self)
- .mut_base()
- .restyle_damage
- .remove(ServoRestyleDamage::BUBBLE_ISIZES);
- }
- }
-}
-
-/// Strips ignorable whitespace from the start of a list of fragments.
-pub fn strip_ignorable_whitespace_from_start(this: &mut LinkedList<Fragment>) {
- if this.is_empty() {
- return; // Fast path.
- }
-
- let mut leading_fragments_consisting_of_solely_bidi_control_characters = LinkedList::new();
- while !this.is_empty() {
- match this
- .front_mut()
- .as_mut()
- .unwrap()
- .strip_leading_whitespace_if_necessary()
- {
- WhitespaceStrippingResult::RetainFragment => break,
- WhitespaceStrippingResult::FragmentContainedOnlyBidiControlCharacters => {
- leading_fragments_consisting_of_solely_bidi_control_characters
- .push_back(this.pop_front().unwrap())
- },
- WhitespaceStrippingResult::FragmentContainedOnlyWhitespace => {
- let removed_fragment = this.pop_front().unwrap();
- if let Some(ref mut remaining_fragment) = this.front_mut() {
- remaining_fragment.meld_with_prev_inline_fragment(&removed_fragment);
- }
- },
- }
- }
- prepend_from(
- this,
- &mut leading_fragments_consisting_of_solely_bidi_control_characters,
- );
-}
-
-/// Strips ignorable whitespace from the end of a list of fragments.
-pub fn strip_ignorable_whitespace_from_end(this: &mut LinkedList<Fragment>) {
- if this.is_empty() {
- return;
- }
-
- let mut trailing_fragments_consisting_of_solely_bidi_control_characters = LinkedList::new();
- while !this.is_empty() {
- match this
- .back_mut()
- .as_mut()
- .unwrap()
- .strip_trailing_whitespace_if_necessary()
- {
- WhitespaceStrippingResult::RetainFragment => break,
- WhitespaceStrippingResult::FragmentContainedOnlyBidiControlCharacters => {
- trailing_fragments_consisting_of_solely_bidi_control_characters
- .push_front(this.pop_back().unwrap())
- },
- WhitespaceStrippingResult::FragmentContainedOnlyWhitespace => {
- let removed_fragment = this.pop_back().unwrap();
- if let Some(ref mut remaining_fragment) = this.back_mut() {
- remaining_fragment.meld_with_next_inline_fragment(&removed_fragment);
- }
- },
- }
- }
- this.append(&mut trailing_fragments_consisting_of_solely_bidi_control_characters);
-}
-
-/// If the 'unicode-bidi' property has a value other than 'normal', return the bidi control codes
-/// to inject before and after the text content of the element.
-fn bidi_control_chars(style: &ServoArc<ComputedValues>) -> Option<(&'static str, &'static str)> {
- use style::computed_values::direction::T::*;
- use style::computed_values::unicode_bidi::T::*;
-
- let unicode_bidi = style.get_text().unicode_bidi;
- let direction = style.get_inherited_box().direction;
-
- // See the table in http://dev.w3.org/csswg/css-writing-modes/#unicode-bidi
- match (unicode_bidi, direction) {
- (Normal, _) => None,
- (Embed, Ltr) => Some(("\u{202A}", "\u{202C}")),
- (Embed, Rtl) => Some(("\u{202B}", "\u{202C}")),
- (Isolate, Ltr) => Some(("\u{2066}", "\u{2069}")),
- (Isolate, Rtl) => Some(("\u{2067}", "\u{2069}")),
- (BidiOverride, Ltr) => Some(("\u{202D}", "\u{202C}")),
- (BidiOverride, Rtl) => Some(("\u{202E}", "\u{202C}")),
- (IsolateOverride, Ltr) => Some(("\u{2068}\u{202D}", "\u{202C}\u{2069}")),
- (IsolateOverride, Rtl) => Some(("\u{2068}\u{202E}", "\u{202C}\u{2069}")),
- (Plaintext, _) => Some(("\u{2068}", "\u{2069}")),
- }
-}
-
-fn control_chars_to_fragment<E>(
- node: &InlineFragmentNodeInfo,
- context: &SharedStyleContext,
- text: &str,
- restyle_damage: RestyleDamage,
-) -> Fragment
-where
- E: TElement,
-{
- let info = SpecificFragmentInfo::UnscannedText(Box::new(UnscannedTextFragmentInfo::new(
- Box::<str>::from(text),
- None,
- )));
- let text_style = context.stylist.style_for_anonymous::<E>(
- &context.guards,
- &PseudoElement::ServoText,
- &node.style,
- );
-
- Fragment::from_opaque_node_and_style(
- node.address,
- node.pseudo,
- text_style,
- node.selected_style.clone(),
- restyle_damage,
- info,
- )
-}
-
-/// Returns true if this node has non-zero padding or border.
-fn has_padding_or_border(values: &ComputedValues) -> bool {
- let padding = values.get_padding();
- let border = values.get_border();
-
- !padding.padding_top.is_definitely_zero() ||
- !padding.padding_right.is_definitely_zero() ||
- !padding.padding_bottom.is_definitely_zero() ||
- !padding.padding_left.is_definitely_zero() ||
- border.border_top_width.px() != 0. ||
- border.border_right_width.px() != 0. ||
- border.border_bottom_width.px() != 0. ||
- border.border_left_width.px() != 0.
-}
-
-/// Maintains a stack of anonymous boxes needed to ensure that the flow tree is *legal*. The tree
-/// is legal if it follows the rules in CSS 2.1 § 17.2.1.
-///
-/// As an example, the legalizer makes sure that table row flows contain only table cells. If the
-/// flow constructor attempts to place, say, a block flow directly underneath the table row, the
-/// legalizer generates an anonymous table cell in between to hold the block.
-///
-/// Generally, the flow constructor should use `Legalizer::add_child()` instead of calling
-/// `Flow::add_new_child()` directly. This ensures that the flow tree remains legal at all times
-/// and centralizes the anonymous flow generation logic in one place.
-struct Legalizer {
- /// A stack of anonymous flows that have yet to be finalized (i.e. that still could acquire new
- /// children).
- stack: Vec<FlowRef>,
-}
-
-impl Legalizer {
- /// Creates a new legalizer.
- fn new() -> Legalizer {
- Legalizer { stack: vec![] }
- }
-
- /// Makes the `child` flow a new child of `parent`. Anonymous flows are automatically inserted
- /// to keep the tree legal.
- fn add_child<E>(
- &mut self,
- context: &SharedStyleContext,
- parent: &mut FlowRef,
- mut child: FlowRef,
- ) where
- E: TElement,
- {
- while !self.stack.is_empty() {
- if self.try_to_add_child::<E>(context, parent, &mut child) {
- return;
- }
- self.flush_top_of_stack(parent)
- }
-
- while !self.try_to_add_child::<E>(context, parent, &mut child) {
- self.push_next_anonymous_flow::<E>(context, parent)
- }
- }
-
- /// Flushes all flows we've been gathering up.
- fn finish(mut self, parent: &mut FlowRef) {
- while !self.stack.is_empty() {
- self.flush_top_of_stack(parent)
- }
- }
-
- /// Attempts to make `child` a child of `parent`. On success, this returns true. If this would
- /// make the tree illegal, this method does nothing and returns false.
- ///
- /// This method attempts to create anonymous blocks in between `parent` and `child` if and only
- /// if those blocks will only ever have `child` as their sole child. At present, this is only
- /// true for anonymous block children of flex flows.
- fn try_to_add_child<E>(
- &mut self,
- context: &SharedStyleContext,
- parent: &mut FlowRef,
- child: &mut FlowRef,
- ) -> bool
- where
- E: TElement,
- {
- let parent = self.stack.last_mut().unwrap_or(parent);
- let (parent_class, child_class) = (parent.class(), child.class());
- match (parent_class, child_class) {
- (FlowClass::TableWrapper, FlowClass::Table) |
- (FlowClass::Table, FlowClass::TableColGroup) |
- (FlowClass::Table, FlowClass::TableRowGroup) |
- (FlowClass::Table, FlowClass::TableRow) |
- (FlowClass::Table, FlowClass::TableCaption) |
- (FlowClass::TableRowGroup, FlowClass::TableRow) |
- (FlowClass::TableRow, FlowClass::TableCell) => {
- parent.add_new_child((*child).clone());
- true
- },
-
- (FlowClass::TableWrapper, _) |
- (FlowClass::Table, _) |
- (FlowClass::TableRowGroup, _) |
- (FlowClass::TableRow, _) |
- (_, FlowClass::Table) |
- (_, FlowClass::TableColGroup) |
- (_, FlowClass::TableRowGroup) |
- (_, FlowClass::TableRow) |
- (_, FlowClass::TableCaption) |
- (_, FlowClass::TableCell) => false,
-
- (FlowClass::Flex, FlowClass::Inline) => {
- FlowRef::deref_mut(child)
- .mut_base()
- .flags
- .insert(FlowFlags::MARGINS_CANNOT_COLLAPSE);
- let mut block_wrapper = Legalizer::create_anonymous_flow::<E, _>(
- context,
- parent,
- &[PseudoElement::ServoAnonymousBlock],
- SpecificFragmentInfo::Generic,
- BlockFlow::from_fragment,
- );
-
- {
- let flag = if parent.as_flex().main_mode() == Direction::Inline {
- FragmentFlags::IS_INLINE_FLEX_ITEM
- } else {
- FragmentFlags::IS_BLOCK_FLEX_ITEM
- };
- let block = FlowRef::deref_mut(&mut block_wrapper).as_mut_block();
- block.base.flags.insert(FlowFlags::MARGINS_CANNOT_COLLAPSE);
- block.fragment.flags.insert(flag);
- }
- block_wrapper.add_new_child((*child).clone());
- block_wrapper.finish();
- parent.add_new_child(block_wrapper);
- true
- },
-
- (FlowClass::Flex, _) => {
- {
- let flag = if parent.as_flex().main_mode() == Direction::Inline {
- FragmentFlags::IS_INLINE_FLEX_ITEM
- } else {
- FragmentFlags::IS_BLOCK_FLEX_ITEM
- };
- let block = FlowRef::deref_mut(child).as_mut_block();
- block.base.flags.insert(FlowFlags::MARGINS_CANNOT_COLLAPSE);
- block.fragment.flags.insert(flag);
- }
- parent.add_new_child((*child).clone());
- true
- },
-
- _ => {
- parent.add_new_child((*child).clone());
- true
- },
- }
- }
-
- /// Finalizes the flow on the top of the stack.
- fn flush_top_of_stack(&mut self, parent: &mut FlowRef) {
- let mut child = self.stack.pop().expect("flush_top_of_stack(): stack empty");
- child.finish();
- self.stack.last_mut().unwrap_or(parent).add_new_child(child)
- }
-
- /// Adds the anonymous flow that would be necessary to make an illegal child of `parent` legal
- /// to the stack.
- fn push_next_anonymous_flow<E>(&mut self, context: &SharedStyleContext, parent: &FlowRef)
- where
- E: TElement,
- {
- let parent_class = self.stack.last().unwrap_or(parent).class();
- match parent_class {
- FlowClass::TableRow => self.push_new_anonymous_flow::<E, _>(
- context,
- parent,
- &[PseudoElement::ServoAnonymousTableCell],
- SpecificFragmentInfo::TableCell,
- TableCellFlow::from_fragment,
- ),
- FlowClass::Table | FlowClass::TableRowGroup => self.push_new_anonymous_flow::<E, _>(
- context,
- parent,
- &[PseudoElement::ServoAnonymousTableRow],
- SpecificFragmentInfo::TableRow,
- TableRowFlow::from_fragment,
- ),
- FlowClass::TableWrapper => self.push_new_anonymous_flow::<E, _>(
- context,
- parent,
- &[PseudoElement::ServoAnonymousTable],
- SpecificFragmentInfo::Table,
- TableFlow::from_fragment,
- ),
- _ => self.push_new_anonymous_flow::<E, _>(
- context,
- parent,
- &[
- PseudoElement::ServoTableWrapper,
- PseudoElement::ServoAnonymousTableWrapper,
- ],
- SpecificFragmentInfo::TableWrapper,
- TableWrapperFlow::from_fragment,
- ),
- }
- }
-
- /// Creates an anonymous flow and pushes it onto the stack.
- fn push_new_anonymous_flow<E, F>(
- &mut self,
- context: &SharedStyleContext,
- reference: &FlowRef,
- pseudos: &[PseudoElement],
- specific_fragment_info: SpecificFragmentInfo,
- constructor: fn(Fragment) -> F,
- ) where
- E: TElement,
- F: Flow,
- {
- let new_flow = Self::create_anonymous_flow::<E, _>(
- context,
- reference,
- pseudos,
- specific_fragment_info,
- constructor,
- );
- self.stack.push(new_flow)
- }
-
- /// Creates a new anonymous flow. The new flow is identical to `reference` except with all
- /// styles applying to every pseudo-element in `pseudos` applied.
- ///
- /// This method invokes the supplied constructor function on the given specific fragment info
- /// in order to actually generate the flow.
- fn create_anonymous_flow<E, F>(
- context: &SharedStyleContext,
- reference: &FlowRef,
- pseudos: &[PseudoElement],
- specific_fragment_info: SpecificFragmentInfo,
- constructor: fn(Fragment) -> F,
- ) -> FlowRef
- where
- E: TElement,
- F: Flow,
- {
- let reference_block = reference.as_block();
- let mut new_style = reference_block.fragment.style.clone();
- for pseudo in pseudos {
- new_style =
- context
- .stylist
- .style_for_anonymous::<E>(&context.guards, pseudo, &new_style);
- }
- let fragment = reference_block
- .fragment
- .create_similar_anonymous_fragment(new_style, specific_fragment_info);
- FlowRef::new(Arc::new(constructor(fragment)))
- }
-}
-
-pub fn is_image_data(uri: &str) -> bool {
- static TYPES: &'static [&'static str] =
- &["data:image/png", "data:image/gif", "data:image/jpeg"];
- TYPES.iter().any(|&type_| uri.starts_with(type_))
-}
diff --git a/components/layout_2020/context.rs b/components/layout_2020/context.rs
index 015cc9ac40d..72398d85a80 100644
--- a/components/layout_2020/context.rs
+++ b/components/layout_2020/context.rs
@@ -2,100 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-//! Data needed by the layout thread.
-
-use crate::display_list::items::{OpaqueNode, WebRenderImageInfo};
-use crate::opaque_node::OpaqueNodeMethods;
-use fnv::FnvHasher;
-use gfx::font_cache_thread::FontCacheThread;
-use gfx::font_context::FontContext;
-use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use msg::constellation_msg::PipelineId;
-use net_traits::image_cache::{CanRequestImages, ImageCache, ImageState};
-use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
-use parking_lot::RwLock;
-use script_layout_interface::{PendingImage, PendingImageState};
-use script_traits::Painter;
-use script_traits::UntrustedNodeAddress;
-use servo_atoms::Atom;
-use servo_url::ServoUrl;
-use std::cell::{RefCell, RefMut};
-use std::collections::HashMap;
-use std::hash::BuildHasherDefault;
-use std::sync::{Arc, Mutex};
-use std::thread;
-use style::context::RegisteredSpeculativePainter;
use style::context::SharedStyleContext;
-pub type LayoutFontContext = FontContext<FontCacheThread>;
-
-thread_local!(static FONT_CONTEXT_KEY: RefCell<Option<LayoutFontContext>> = RefCell::new(None));
-
-pub fn with_thread_local_font_context<F, R>(layout_context: &LayoutContext, f: F) -> R
-where
- F: FnOnce(&mut LayoutFontContext) -> R,
-{
- FONT_CONTEXT_KEY.with(|k| {
- let mut font_context = k.borrow_mut();
- if font_context.is_none() {
- let font_cache_thread = layout_context.font_cache_thread.lock().unwrap().clone();
- *font_context = Some(FontContext::new(font_cache_thread));
- }
- f(&mut RefMut::map(font_context, |x| x.as_mut().unwrap()))
- })
-}
-
-pub fn malloc_size_of_persistent_local_context(ops: &mut MallocSizeOfOps) -> usize {
- FONT_CONTEXT_KEY.with(|r| {
- if let Some(ref context) = *r.borrow() {
- context.size_of(ops)
- } else {
- 0
- }
- })
-}
-
-/// Layout information shared among all workers. This must be thread-safe.
pub struct LayoutContext<'a> {
- /// The pipeline id of this LayoutContext.
pub id: PipelineId,
-
- /// Bits shared by the layout and style system.
pub style_context: SharedStyleContext<'a>,
-
- /// Reference to the script thread image cache.
- pub image_cache: Arc<dyn ImageCache>,
-
- /// Interface to the font cache thread.
- pub font_cache_thread: Mutex<FontCacheThread>,
-
- /// A cache of WebRender image info.
- pub webrender_image_cache: Arc<
- RwLock<
- HashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo, BuildHasherDefault<FnvHasher>>,
- >,
- >,
-
- /// Paint worklets
- pub registered_painters: &'a dyn RegisteredPainters,
-
- /// A list of in-progress image loads to be shared with the script thread.
- /// A None value means that this layout was not initiated by the script thread.
- pub pending_images: Option<Mutex<Vec<PendingImage>>>,
-
- /// A list of nodes that have just initiated a CSS transition.
- /// A None value means that this layout was not initiated by the script thread.
- pub newly_transitioning_nodes: Option<Mutex<Vec<UntrustedNodeAddress>>>,
-}
-
-impl<'a> Drop for LayoutContext<'a> {
- fn drop(&mut self) {
- if !thread::panicking() {
- if let Some(ref pending_images) = self.pending_images {
- assert!(pending_images.lock().unwrap().is_empty());
- }
- }
- }
}
impl<'a> LayoutContext<'a> {
@@ -103,99 +15,4 @@ impl<'a> LayoutContext<'a> {
pub fn shared_context(&self) -> &SharedStyleContext {
&self.style_context
}
-
- pub fn get_or_request_image_or_meta(
- &self,
- node: OpaqueNode,
- url: ServoUrl,
- use_placeholder: UsePlaceholder,
- ) -> Option<ImageOrMetadataAvailable> {
- //XXXjdm For cases where we do not request an image, we still need to
- // ensure the node gets another script-initiated reflow or it
- // won't be requested at all.
- let can_request = if self.pending_images.is_some() {
- CanRequestImages::Yes
- } else {
- CanRequestImages::No
- };
-
- // See if the image is already available
- let result =
- self.image_cache
- .find_image_or_metadata(url.clone(), use_placeholder, can_request);
- match result {
- Ok(image_or_metadata) => Some(image_or_metadata),
- // Image failed to load, so just return nothing
- Err(ImageState::LoadError) => None,
- // Not yet requested - request image or metadata from the cache
- Err(ImageState::NotRequested(id)) => {
- let image = PendingImage {
- state: PendingImageState::Unrequested(url),
- node: node.to_untrusted_node_address(),
- id: id,
- };
- self.pending_images
- .as_ref()
- .unwrap()
- .lock()
- .unwrap()
- .push(image);
- None
- },
- // Image has been requested, is still pending. Return no image for this paint loop.
- // When the image loads it will trigger a reflow and/or repaint.
- Err(ImageState::Pending(id)) => {
- //XXXjdm if self.pending_images is not available, we should make sure that
- // this node gets marked dirty again so it gets a script-initiated
- // reflow that deals with this properly.
- if let Some(ref pending_images) = self.pending_images {
- let image = PendingImage {
- state: PendingImageState::PendingResponse,
- node: node.to_untrusted_node_address(),
- id: id,
- };
- pending_images.lock().unwrap().push(image);
- }
- None
- },
- }
- }
-
- pub fn get_webrender_image_for_url(
- &self,
- node: OpaqueNode,
- url: ServoUrl,
- use_placeholder: UsePlaceholder,
- ) -> Option<WebRenderImageInfo> {
- if let Some(existing_webrender_image) = self
- .webrender_image_cache
- .read()
- .get(&(url.clone(), use_placeholder))
- {
- return Some((*existing_webrender_image).clone());
- }
-
- match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) {
- Some(ImageOrMetadataAvailable::ImageAvailable(image, _)) => {
- let image_info = WebRenderImageInfo::from_image(&*image);
- if image_info.key.is_none() {
- Some(image_info)
- } else {
- let mut webrender_image_cache = self.webrender_image_cache.write();
- webrender_image_cache.insert((url, use_placeholder), image_info);
- Some(image_info)
- }
- },
- None | Some(ImageOrMetadataAvailable::MetadataAvailable(_)) => None,
- }
- }
-}
-
-/// A registered painter
-pub trait RegisteredPainter: RegisteredSpeculativePainter + Painter {}
-
-/// A set of registered painters
-pub trait RegisteredPainters: Sync {
- /// Look up a painter
- fn get(&self, name: &Atom) -> Option<&dyn RegisteredPainter>;
}
diff --git a/components/layout_2020/data.rs b/components/layout_2020/data.rs
index 5f6672d1488..c406a1bc4e7 100644
--- a/components/layout_2020/data.rs
+++ b/components/layout_2020/data.rs
@@ -2,67 +2,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use crate::construct::ConstructionResult;
-use atomic_refcell::AtomicRefCell;
use script_layout_interface::StyleData;
#[repr(C)]
pub struct StyleAndLayoutData {
- /// Data accessed by script_layout_interface. This must be first to allow
- /// casting between StyleAndLayoutData and StyleData.
pub style_data: StyleData,
- /// The layout data associated with a node.
- pub layout_data: AtomicRefCell<LayoutData>,
}
impl StyleAndLayoutData {
pub fn new() -> Self {
Self {
style_data: StyleData::new(),
- layout_data: AtomicRefCell::new(LayoutData::new()),
}
}
}
-
-/// Data that layout associates with a node.
-#[repr(C)]
-pub struct LayoutData {
- /// The current results of flow construction for this node. This is either a
- /// flow or a `ConstructionItem`. See comments in `construct.rs` for more
- /// details.
- pub flow_construction_result: ConstructionResult,
-
- pub before_flow_construction_result: ConstructionResult,
-
- pub after_flow_construction_result: ConstructionResult,
-
- pub details_summary_flow_construction_result: ConstructionResult,
-
- pub details_content_flow_construction_result: ConstructionResult,
-
- /// Various flags.
- pub flags: LayoutDataFlags,
-}
-
-impl LayoutData {
- /// Creates new layout data.
- pub fn new() -> LayoutData {
- Self {
- flow_construction_result: ConstructionResult::None,
- before_flow_construction_result: ConstructionResult::None,
- after_flow_construction_result: ConstructionResult::None,
- details_summary_flow_construction_result: ConstructionResult::None,
- details_content_flow_construction_result: ConstructionResult::None,
- flags: LayoutDataFlags::empty(),
- }
- }
-}
-
-bitflags! {
- pub struct LayoutDataFlags: u8 {
- #[doc = "Whether a flow has been newly constructed."]
- const HAS_NEWLY_CONSTRUCTED_FLOW = 0x01;
- #[doc = "Whether this node has been traversed by layout."]
- const HAS_BEEN_TRAVERSED = 0x02;
- }
-}
diff --git a/components/layout_2020/display_list/background.rs b/components/layout_2020/display_list/background.rs
deleted file mode 100644
index c6fa1691e46..00000000000
--- a/components/layout_2020/display_list/background.rs
+++ /dev/null
@@ -1,336 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::display_list::border;
-use app_units::Au;
-use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
-use style::computed_values::background_attachment::single_value::T as BackgroundAttachment;
-use style::computed_values::background_clip::single_value::T as BackgroundClip;
-use style::computed_values::background_origin::single_value::T as BackgroundOrigin;
-use style::properties::style_structs::Background;
-use style::values::computed::{BackgroundSize, NonNegativeLengthPercentageOrAuto};
-use style::values::specified::background::BackgroundRepeatKeyword;
-use webrender_api::BorderRadius;
-
-/// Placment information for both image and gradient backgrounds.
-#[derive(Clone, Copy, Debug)]
-pub struct BackgroundPlacement {
- /// Rendering bounds. The background will start in the uppper-left corner
- /// and fill the whole area.
- pub bounds: Rect<Au>,
- /// Background tile size. Some backgrounds are repeated. These are the
- /// dimensions of a single image of the background.
- pub tile_size: Size2D<Au>,
- /// Spacing between tiles. Some backgrounds are not repeated seamless
- /// but have seams between them like tiles in real life.
- pub tile_spacing: Size2D<Au>,
- /// A clip area. While the background is rendered according to all the
- /// measures above it is only shown within these bounds.
- pub clip_rect: Rect<Au>,
- /// Rounded corners for the clip_rect.
- pub clip_radii: BorderRadius,
- /// Whether or not the background is fixed to the viewport.
- pub fixed: bool,
-}
-
-/// Access element at index modulo the array length.
-///
-/// Obviously it does not work with empty arrays.
-///
-/// This is used for multiple layered background images.
-/// See: https://drafts.csswg.org/css-backgrounds-3/#layering
-pub fn get_cyclic<T>(arr: &[T], index: usize) -> &T {
- &arr[index % arr.len()]
-}
-
-/// For a given area and an image compute how big the
-/// image should be displayed on the background.
-fn compute_background_image_size(
- bg_size: BackgroundSize,
- bounds_size: Size2D<Au>,
- intrinsic_size: Option<Size2D<Au>>,
-) -> Size2D<Au> {
- match intrinsic_size {
- None => match bg_size {
- BackgroundSize::Cover | BackgroundSize::Contain => bounds_size,
- BackgroundSize::ExplicitSize { width, height } => Size2D::new(
- width
- .to_used_value(bounds_size.width)
- .unwrap_or(bounds_size.width),
- height
- .to_used_value(bounds_size.height)
- .unwrap_or(bounds_size.height),
- ),
- },
- Some(own_size) => {
- // If `image_aspect_ratio` < `bounds_aspect_ratio`, the image is tall; otherwise, it is
- // wide.
- let image_aspect_ratio = own_size.width.to_f32_px() / own_size.height.to_f32_px();
- let bounds_aspect_ratio =
- bounds_size.width.to_f32_px() / bounds_size.height.to_f32_px();
- match (bg_size, image_aspect_ratio < bounds_aspect_ratio) {
- (BackgroundSize::Contain, false) | (BackgroundSize::Cover, true) => Size2D::new(
- bounds_size.width,
- bounds_size.width.scale_by(image_aspect_ratio.recip()),
- ),
- (BackgroundSize::Contain, true) | (BackgroundSize::Cover, false) => Size2D::new(
- bounds_size.height.scale_by(image_aspect_ratio),
- bounds_size.height,
- ),
- (
- BackgroundSize::ExplicitSize {
- width,
- height: NonNegativeLengthPercentageOrAuto::Auto,
- },
- _,
- ) => {
- let width = width
- .to_used_value(bounds_size.width)
- .unwrap_or(own_size.width);
- Size2D::new(width, width.scale_by(image_aspect_ratio.recip()))
- },
- (
- BackgroundSize::ExplicitSize {
- width: NonNegativeLengthPercentageOrAuto::Auto,
- height,
- },
- _,
- ) => {
- let height = height
- .to_used_value(bounds_size.height)
- .unwrap_or(own_size.height);
- Size2D::new(height.scale_by(image_aspect_ratio), height)
- },
- (BackgroundSize::ExplicitSize { width, height }, _) => Size2D::new(
- width
- .to_used_value(bounds_size.width)
- .unwrap_or(own_size.width),
- height
- .to_used_value(bounds_size.height)
- .unwrap_or(own_size.height),
- ),
- }
- },
- }
-}
-
-/// Compute a rounded clip rect for the background.
-pub fn clip(
- bg_clip: BackgroundClip,
- absolute_bounds: Rect<Au>,
- border: SideOffsets2D<Au>,
- border_padding: SideOffsets2D<Au>,
- border_radii: BorderRadius,
-) -> (Rect<Au>, BorderRadius) {
- match bg_clip {
- BackgroundClip::BorderBox => (absolute_bounds, border_radii),
- BackgroundClip::PaddingBox => (
- absolute_bounds.inner_rect(border),
- border::inner_radii(border_radii, border),
- ),
- BackgroundClip::ContentBox => (
- absolute_bounds.inner_rect(border_padding),
- border::inner_radii(border_radii, border_padding),
- ),
- }
-}
-
-/// Determines where to place an element background image or gradient.
-///
-/// Photos have their resolution as intrinsic size while gradients have
-/// no intrinsic size.
-pub fn placement(
- bg: &Background,
- viewport_size: Size2D<Au>,
- absolute_bounds: Rect<Au>,
- intrinsic_size: Option<Size2D<Au>>,
- border: SideOffsets2D<Au>,
- border_padding: SideOffsets2D<Au>,
- border_radii: BorderRadius,
- index: usize,
-) -> BackgroundPlacement {
- let bg_attachment = *get_cyclic(&bg.background_attachment.0, index);
- let bg_clip = *get_cyclic(&bg.background_clip.0, index);
- let bg_origin = *get_cyclic(&bg.background_origin.0, index);
- let bg_position_x = get_cyclic(&bg.background_position_x.0, index);
- let bg_position_y = get_cyclic(&bg.background_position_y.0, index);
- let bg_repeat = get_cyclic(&bg.background_repeat.0, index);
- let bg_size = *get_cyclic(&bg.background_size.0, index);
-
- let (clip_rect, clip_radii) = clip(
- bg_clip,
- absolute_bounds,
- border,
- border_padding,
- border_radii,
- );
-
- let mut fixed = false;
- let mut bounds = match bg_attachment {
- BackgroundAttachment::Scroll => match bg_origin {
- BackgroundOrigin::BorderBox => absolute_bounds,
- BackgroundOrigin::PaddingBox => absolute_bounds.inner_rect(border),
- BackgroundOrigin::ContentBox => absolute_bounds.inner_rect(border_padding),
- },
- BackgroundAttachment::Fixed => {
- fixed = true;
- Rect::new(Point2D::origin(), viewport_size)
- },
- };
-
- let mut tile_size = compute_background_image_size(bg_size, bounds.size, intrinsic_size);
-
- let mut tile_spacing = Size2D::zero();
- let own_position = bounds.size - tile_size;
- let pos_x = bg_position_x.to_used_value(own_position.width);
- let pos_y = bg_position_y.to_used_value(own_position.height);
- tile_image_axis(
- bg_repeat.0,
- &mut bounds.origin.x,
- &mut bounds.size.width,
- &mut tile_size.width,
- &mut tile_spacing.width,
- pos_x,
- clip_rect.origin.x,
- clip_rect.size.width,
- );
- tile_image_axis(
- bg_repeat.1,
- &mut bounds.origin.y,
- &mut bounds.size.height,
- &mut tile_size.height,
- &mut tile_spacing.height,
- pos_y,
- clip_rect.origin.y,
- clip_rect.size.height,
- );
-
- BackgroundPlacement {
- bounds,
- tile_size,
- tile_spacing,
- clip_rect,
- clip_radii,
- fixed,
- }
-}
-
-fn tile_image_round(
- position: &mut Au,
- size: &mut Au,
- absolute_anchor_origin: Au,
- image_size: &mut Au,
-) {
- if *size == Au(0) || *image_size == Au(0) {
- *position = Au(0);
- *size = Au(0);
- return;
- }
-
- let number_of_tiles = (size.to_f32_px() / image_size.to_f32_px()).round().max(1.0);
- *image_size = *size / (number_of_tiles as i32);
- tile_image(position, size, absolute_anchor_origin, *image_size);
-}
-
-fn tile_image_spaced(
- position: &mut Au,
- size: &mut Au,
- tile_spacing: &mut Au,
- absolute_anchor_origin: Au,
- image_size: Au,
-) {
- if *size == Au(0) || image_size == Au(0) {
- *position = Au(0);
- *size = Au(0);
- *tile_spacing = Au(0);
- return;
- }
-
- // Per the spec, if the space available is not enough for two images, just tile as
- // normal but only display a single tile.
- if image_size * 2 >= *size {
- tile_image(position, size, absolute_anchor_origin, image_size);
- *tile_spacing = Au(0);
- *size = image_size;
- return;
- }
-
- // Take the box size, remove room for two tiles on the edges, and then calculate how many
- // other tiles fit in between them.
- let size_remaining = *size - (image_size * 2);
- let num_middle_tiles = (size_remaining.to_f32_px() / image_size.to_f32_px()).floor() as i32;
-
- // Allocate the remaining space as padding between tiles. background-position is ignored
- // as per the spec, so the position is just the box origin. We are also ignoring
- // background-attachment here, which seems unspecced when combined with
- // background-repeat: space.
- let space_for_middle_tiles = image_size * num_middle_tiles;
- *tile_spacing = (size_remaining - space_for_middle_tiles) / (num_middle_tiles + 1);
-}
-
-/// Tile an image
-fn tile_image(position: &mut Au, size: &mut Au, absolute_anchor_origin: Au, image_size: Au) {
- // Avoid division by zero below!
- // Images with a zero width or height are not displayed.
- // Therefore the positions do not matter and can be left unchanged.
- // NOTE: A possible optimization is not to build
- // display items in this case at all.
- if image_size == Au(0) {
- return;
- }
-
- let delta_pixels = absolute_anchor_origin - *position;
- let image_size_px = image_size.to_f32_px();
- let tile_count = ((delta_pixels.to_f32_px() + image_size_px - 1.0) / image_size_px).floor();
- let offset = image_size * (tile_count as i32);
- let new_position = absolute_anchor_origin - offset;
- *size = *position - new_position + *size;
- *position = new_position;
-}
-
-/// For either the x or the y axis ajust various values to account for tiling.
-///
-/// This is done separately for both axes because the repeat keywords may differ.
-fn tile_image_axis(
- repeat: BackgroundRepeatKeyword,
- position: &mut Au,
- size: &mut Au,
- tile_size: &mut Au,
- tile_spacing: &mut Au,
- offset: Au,
- clip_origin: Au,
- clip_size: Au,
-) {
- let absolute_anchor_origin = *position + offset;
- match repeat {
- BackgroundRepeatKeyword::NoRepeat => {
- *position += offset;
- *size = *tile_size;
- },
- BackgroundRepeatKeyword::Repeat => {
- *position = clip_origin;
- *size = clip_size;
- tile_image(position, size, absolute_anchor_origin, *tile_size);
- },
- BackgroundRepeatKeyword::Space => {
- tile_image_spaced(
- position,
- size,
- tile_spacing,
- absolute_anchor_origin,
- *tile_size,
- );
- let combined_tile_size = *tile_size + *tile_spacing;
- *position = clip_origin;
- *size = clip_size;
- tile_image(position, size, absolute_anchor_origin, combined_tile_size);
- },
- BackgroundRepeatKeyword::Round => {
- tile_image_round(position, size, absolute_anchor_origin, tile_size);
- *position = clip_origin;
- *size = clip_size;
- tile_image(position, size, absolute_anchor_origin, *tile_size);
- },
- }
-}
diff --git a/components/layout_2020/display_list/border.rs b/components/layout_2020/display_list/border.rs
deleted file mode 100644
index fff18fcf6c0..00000000000
--- a/components/layout_2020/display_list/border.rs
+++ /dev/null
@@ -1,199 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::display_list::ToLayout;
-use app_units::Au;
-use euclid::{Rect, SideOffsets2D, Size2D};
-use style::computed_values::border_image_outset::T as BorderImageOutset;
-use style::properties::style_structs::Border;
-use style::values::computed::NumberOrPercentage;
-use style::values::computed::{BorderCornerRadius, BorderImageWidth};
-use style::values::computed::{BorderImageSideWidth, NonNegativeLengthOrNumber};
-use style::values::generics::rect::Rect as StyleRect;
-use style::values::generics::NonNegative;
-use webrender_api::units::{LayoutSideOffsets, LayoutSize};
-use webrender_api::{BorderRadius, BorderSide, BorderStyle, ColorF, NormalBorder};
-
-/// Computes a border radius size against the containing size.
-///
-/// Note that percentages in `border-radius` are resolved against the relevant
-/// box dimension instead of only against the width per [1]:
-///
-/// > Percentages: Refer to corresponding dimension of the border box.
-///
-/// [1]: https://drafts.csswg.org/css-backgrounds-3/#border-radius
-fn corner_radius(radius: BorderCornerRadius, containing_size: Size2D<Au>) -> Size2D<Au> {
- let w = radius.0.width().to_used_value(containing_size.width);
- let h = radius.0.height().to_used_value(containing_size.height);
- Size2D::new(w, h)
-}
-
-fn scaled_radii(radii: BorderRadius, factor: f32) -> BorderRadius {
- BorderRadius {
- top_left: radii.top_left * factor,
- top_right: radii.top_right * factor,
- bottom_left: radii.bottom_left * factor,
- bottom_right: radii.bottom_right * factor,
- }
-}
-
-fn overlapping_radii(size: LayoutSize, radii: BorderRadius) -> BorderRadius {
- // No two corners' border radii may add up to more than the length of the edge
- // between them. To prevent that, all radii are scaled down uniformly.
- fn scale_factor(radius_a: f32, radius_b: f32, edge_length: f32) -> f32 {
- let required = radius_a + radius_b;
-
- if required <= edge_length {
- 1.0
- } else {
- edge_length / required
- }
- }
-
- let top_factor = scale_factor(radii.top_left.width, radii.top_right.width, size.width);
- let bottom_factor = scale_factor(
- radii.bottom_left.width,
- radii.bottom_right.width,
- size.width,
- );
- let left_factor = scale_factor(radii.top_left.height, radii.bottom_left.height, size.height);
- let right_factor = scale_factor(
- radii.top_right.height,
- radii.bottom_right.height,
- size.height,
- );
- let min_factor = top_factor
- .min(bottom_factor)
- .min(left_factor)
- .min(right_factor);
- if min_factor < 1.0 {
- scaled_radii(radii, min_factor)
- } else {
- radii
- }
-}
-
-/// Determine the four corner radii of a border.
-///
-/// Radii may either be absolute or relative to the absolute bounds.
-/// Each corner radius has a width and a height which may differ.
-/// Lastly overlapping radii are shrank so they don't collide anymore.
-pub fn radii(abs_bounds: Rect<Au>, border_style: &Border) -> BorderRadius {
- // TODO(cgaebel): Support border radii even in the case of multiple border widths.
- // This is an extension of supporting elliptical radii. For now, all percentage
- // radii will be relative to the width.
-
- overlapping_radii(
- abs_bounds.size.to_layout(),
- BorderRadius {
- top_left: corner_radius(border_style.border_top_left_radius, abs_bounds.size)
- .to_layout(),
- top_right: corner_radius(border_style.border_top_right_radius, abs_bounds.size)
- .to_layout(),
- bottom_right: corner_radius(border_style.border_bottom_right_radius, abs_bounds.size)
- .to_layout(),
- bottom_left: corner_radius(border_style.border_bottom_left_radius, abs_bounds.size)
- .to_layout(),
- },
- )
-}
-
-/// Calculates radii for the inner side.
-///
-/// Radii usually describe the outer side of a border but for the lines to look nice
-/// the inner radii need to be smaller depending on the line width.
-///
-/// This is used to determine clipping areas.
-pub fn inner_radii(mut radii: BorderRadius, offsets: SideOffsets2D<Au>) -> BorderRadius {
- fn inner_length(x: f32, offset: Au) -> f32 {
- 0.0_f32.max(x - offset.to_f32_px())
- }
- radii.top_left.width = inner_length(radii.top_left.width, offsets.left);
- radii.bottom_left.width = inner_length(radii.bottom_left.width, offsets.left);
-
- radii.top_right.width = inner_length(radii.top_right.width, offsets.right);
- radii.bottom_right.width = inner_length(radii.bottom_right.width, offsets.right);
-
- radii.top_left.height = inner_length(radii.top_left.height, offsets.top);
- radii.top_right.height = inner_length(radii.top_right.height, offsets.top);
-
- radii.bottom_left.height = inner_length(radii.bottom_left.height, offsets.bottom);
- radii.bottom_right.height = inner_length(radii.bottom_right.height, offsets.bottom);
- radii
-}
-
-/// Creates a four-sided border with square corners and uniform color and width.
-pub fn simple(color: ColorF, style: BorderStyle) -> NormalBorder {
- let side = BorderSide { color, style };
- NormalBorder {
- left: side,
- right: side,
- top: side,
- bottom: side,
- radius: BorderRadius::zero(),
- do_aa: true,
- }
-}
-
-fn side_image_outset(outset: NonNegativeLengthOrNumber, border_width: Au) -> Au {
- match outset {
- NonNegativeLengthOrNumber::Length(length) => length.into(),
- NonNegativeLengthOrNumber::Number(factor) => border_width.scale_by(factor.0),
- }
-}
-
-/// Compute the additional border-image area.
-pub fn image_outset(outset: BorderImageOutset, border: SideOffsets2D<Au>) -> SideOffsets2D<Au> {
- SideOffsets2D::new(
- side_image_outset(outset.0, border.top),
- side_image_outset(outset.1, border.right),
- side_image_outset(outset.2, border.bottom),
- side_image_outset(outset.3, border.left),
- )
-}
-
-fn side_image_width(
- border_image_width: BorderImageSideWidth,
- border_width: f32,
- total_length: Au,
-) -> f32 {
- match border_image_width {
- BorderImageSideWidth::LengthPercentage(v) => v.to_used_value(total_length).to_f32_px(),
- BorderImageSideWidth::Number(x) => border_width * x.0,
- BorderImageSideWidth::Auto => border_width,
- }
-}
-
-pub fn image_width(
- width: &BorderImageWidth,
- border: LayoutSideOffsets,
- border_area: Size2D<Au>,
-) -> LayoutSideOffsets {
- LayoutSideOffsets::new(
- side_image_width(width.0, border.top, border_area.height),
- side_image_width(width.1, border.right, border_area.width),
- side_image_width(width.2, border.bottom, border_area.height),
- side_image_width(width.3, border.left, border_area.width),
- )
-}
-
-fn resolve_percentage(value: NonNegative<NumberOrPercentage>, length: i32) -> i32 {
- match value.0 {
- NumberOrPercentage::Percentage(p) => (p.0 * length as f32).round() as i32,
- NumberOrPercentage::Number(n) => n.round() as i32,
- }
-}
-
-pub fn image_slice(
- border_image_slice: &StyleRect<NonNegative<NumberOrPercentage>>,
- width: i32,
- height: i32,
-) -> SideOffsets2D<i32> {
- SideOffsets2D::new(
- resolve_percentage(border_image_slice.0, height),
- resolve_percentage(border_image_slice.1, width),
- resolve_percentage(border_image_slice.2, height),
- resolve_percentage(border_image_slice.3, width),
- )
-}
diff --git a/components/layout_2020/display_list/builder.rs b/components/layout_2020/display_list/builder.rs
index e6ead2505ca..bc3756654fa 100644
--- a/components/layout_2020/display_list/builder.rs
+++ b/components/layout_2020/display_list/builder.rs
@@ -8,2968 +8,14 @@
//! list building, as the actual painting does not happen here—only deciding *what* we're going to
//! paint.
-use crate::block::BlockFlow;
-use crate::context::LayoutContext;
-use crate::display_list::background::{self, get_cyclic};
-use crate::display_list::border;
-use crate::display_list::gradient;
-use crate::display_list::items::{self, BaseDisplayItem, ClipScrollNode};
-use crate::display_list::items::{ClipScrollNodeIndex, ClipScrollNodeType, ClippingAndScrolling};
-use crate::display_list::items::{ClippingRegion, DisplayItem, DisplayItemMetadata, DisplayList};
-use crate::display_list::items::{CommonDisplayItem, DisplayListSection};
-use crate::display_list::items::{IframeDisplayItem, OpaqueNode};
-use crate::display_list::items::{PopAllTextShadowsDisplayItem, PushTextShadowDisplayItem};
-use crate::display_list::items::{StackingContext, StackingContextType, StickyFrameData};
-use crate::display_list::items::{TextOrientation, WebRenderImageInfo};
-use crate::display_list::ToLayout;
-use crate::flow::{BaseFlow, Flow, FlowFlags};
-use crate::flow_ref::FlowRef;
-use crate::fragment::SpecificFragmentInfo;
-use crate::fragment::{CanvasFragmentSource, CoordinateSystem, Fragment, ScannedTextFragmentInfo};
-use crate::inline::InlineFragmentNodeFlags;
-use crate::model::MaybeAuto;
-use crate::table_cell::CollapsedBordersForCell;
-use app_units::{Au, AU_PER_PX};
-use canvas_traits::canvas::{CanvasMsg, FromLayoutMsg};
-use embedder_traits::Cursor;
-use euclid::{rect, Point2D, Rect, SideOffsets2D, Size2D, TypedRect, TypedSize2D};
+use crate::display_list::items::OpaqueNode;
+use app_units::Au;
+use euclid::Point2D;
use fnv::FnvHashMap;
use gfx::text::glyph::ByteIndex;
use gfx::text::TextRun;
-use gfx_traits::{combine_id_with_fragment_type, FragmentType, StackingContextId};
-use ipc_channel::ipc;
-use msg::constellation_msg::PipelineId;
-use net_traits::image_cache::UsePlaceholder;
use range::Range;
-use script_traits::IFrameSize;
-use servo_config::opts;
-use servo_geometry::{self, MaxRect};
-use std::default::Default;
-use std::f32;
-use std::mem;
use std::sync::Arc;
-use style::computed_values::border_style::T as BorderStyle;
-use style::computed_values::overflow_x::T as StyleOverflow;
-use style::computed_values::pointer_events::T as PointerEvents;
-use style::computed_values::position::T as StylePosition;
-use style::computed_values::visibility::T as Visibility;
-use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect};
-use style::properties::{style_structs, ComputedValues};
-use style::servo::restyle_damage::ServoRestyleDamage;
-use style::values::computed::effects::SimpleShadow;
-use style::values::computed::image::{Image, ImageLayer};
-use style::values::computed::{Gradient, LengthOrAuto};
-use style::values::generics::background::BackgroundSize;
-use style::values::generics::image::{GradientKind, PaintWorklet};
-use style::values::specified::ui::CursorKind;
-use style::values::{Either, RGBA};
-use style_traits::ToCss;
-use webrender_api::units::{LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
-use webrender_api::{self, BorderDetails, BorderRadius, BorderSide, BoxShadowClipMode, ColorF};
-use webrender_api::{ColorU, ExternalScrollId, FilterOp, GlyphInstance, ImageRendering, LineStyle};
-use webrender_api::{NinePatchBorder, NinePatchBorderSource, NormalBorder};
-use webrender_api::{ScrollSensitivity, StickyOffsetBounds};
-
-static THREAD_TINT_COLORS: [ColorF; 8] = [
- ColorF {
- r: 6.0 / 255.0,
- g: 153.0 / 255.0,
- b: 198.0 / 255.0,
- a: 0.7,
- },
- ColorF {
- r: 255.0 / 255.0,
- g: 212.0 / 255.0,
- b: 83.0 / 255.0,
- a: 0.7,
- },
- ColorF {
- r: 116.0 / 255.0,
- g: 29.0 / 255.0,
- b: 109.0 / 255.0,
- a: 0.7,
- },
- ColorF {
- r: 204.0 / 255.0,
- g: 158.0 / 255.0,
- b: 199.0 / 255.0,
- a: 0.7,
- },
- ColorF {
- r: 242.0 / 255.0,
- g: 46.0 / 255.0,
- b: 121.0 / 255.0,
- a: 0.7,
- },
- ColorF {
- r: 116.0 / 255.0,
- g: 203.0 / 255.0,
- b: 196.0 / 255.0,
- a: 0.7,
- },
- ColorF {
- r: 255.0 / 255.0,
- g: 249.0 / 255.0,
- b: 201.0 / 255.0,
- a: 0.7,
- },
- ColorF {
- r: 137.0 / 255.0,
- g: 196.0 / 255.0,
- b: 78.0 / 255.0,
- a: 0.7,
- },
-];
-
-pub struct InlineNodeBorderInfo {
- is_first_fragment_of_element: bool,
- is_last_fragment_of_element: bool,
-}
-
-#[derive(Debug)]
-struct StackingContextInfo {
- children: Vec<StackingContext>,
- clip_scroll_nodes: Vec<ClipScrollNodeIndex>,
- real_stacking_context_id: StackingContextId,
-}
-
-impl StackingContextInfo {
- fn new(real_stacking_context_id: StackingContextId) -> StackingContextInfo {
- StackingContextInfo {
- children: Vec::new(),
- clip_scroll_nodes: Vec::new(),
- real_stacking_context_id,
- }
- }
-
- fn take_children(&mut self) -> Vec<StackingContext> {
- mem::replace(&mut self.children, Vec::new())
- }
-}
-
-pub struct StackingContextCollectionState {
- /// The PipelineId of this stacking context collection.
- pub pipeline_id: PipelineId,
-
- /// The root of the StackingContext tree.
- pub root_stacking_context: StackingContext,
-
- /// StackingContext and ClipScrollNode children for each StackingContext.
- stacking_context_info: FnvHashMap<StackingContextId, StackingContextInfo>,
-
- pub clip_scroll_nodes: Vec<ClipScrollNode>,
-
- /// The current stacking context id, used to keep track of state when building.
- /// recursively building and processing the display list.
- pub current_stacking_context_id: StackingContextId,
-
- /// The current reference frame ClipScrollNodeIndex.
- pub current_real_stacking_context_id: StackingContextId,
-
- /// The next stacking context id that we will assign to a stacking context.
- pub next_stacking_context_id: StackingContextId,
-
- /// The current reference frame id. This is used to assign items to the parent
- /// reference frame when we encounter a fixed position stacking context.
- pub current_parent_reference_frame_id: ClipScrollNodeIndex,
-
- /// The current clip and scroll info, used to keep track of state when
- /// recursively building and processing the display list.
- pub current_clipping_and_scrolling: ClippingAndScrolling,
-
- /// The clip and scroll info of the first ancestor which defines a containing block.
- /// This is necessary because absolutely positioned items should be clipped
- /// by their containing block's scroll root.
- pub containing_block_clipping_and_scrolling: ClippingAndScrolling,
-
- /// A stack of clips used to cull display list entries that are outside the
- /// rendered region.
- pub clip_stack: Vec<Rect<Au>>,
-
- /// A stack of clips used to cull display list entries that are outside the
- /// rendered region, but only collected at containing block boundaries.
- pub containing_block_clip_stack: Vec<Rect<Au>>,
-
- /// The flow parent's content box, used to calculate sticky constraints.
- parent_stacking_relative_content_box: Rect<Au>,
-}
-
-impl StackingContextCollectionState {
- pub fn new(pipeline_id: PipelineId) -> StackingContextCollectionState {
- let root_clip_indices =
- ClippingAndScrolling::simple(ClipScrollNodeIndex::root_scroll_node());
-
- let mut stacking_context_info = FnvHashMap::default();
- stacking_context_info.insert(
- StackingContextId::root(),
- StackingContextInfo::new(StackingContextId::root()),
- );
-
- // We add two empty nodes to represent the WebRender root reference frame and
- // root scroll nodes. WebRender adds these automatically and we add them here
- // so that the ids in the array match up with the ones we assign during display
- // list building. We ignore these two nodes during conversion to WebRender
- // display lists.
- let clip_scroll_nodes = vec![ClipScrollNode::placeholder(), ClipScrollNode::placeholder()];
-
- StackingContextCollectionState {
- pipeline_id: pipeline_id,
- root_stacking_context: StackingContext::root(),
- stacking_context_info,
- clip_scroll_nodes,
- current_stacking_context_id: StackingContextId::root(),
- current_real_stacking_context_id: StackingContextId::root(),
- next_stacking_context_id: StackingContextId::root().next(),
- current_parent_reference_frame_id: ClipScrollNodeIndex::root_reference_frame(),
- current_clipping_and_scrolling: root_clip_indices,
- containing_block_clipping_and_scrolling: root_clip_indices,
- clip_stack: Vec::new(),
- containing_block_clip_stack: Vec::new(),
- parent_stacking_relative_content_box: Rect::zero(),
- }
- }
-
- fn allocate_stacking_context_info(
- &mut self,
- stacking_context_type: StackingContextType,
- ) -> StackingContextId {
- let next_stacking_context_id = self.next_stacking_context_id.next();
- let allocated_id =
- mem::replace(&mut self.next_stacking_context_id, next_stacking_context_id);
-
- let real_stacking_context_id = match stacking_context_type {
- StackingContextType::Real => allocated_id,
- _ => self.current_real_stacking_context_id,
- };
-
- self.stacking_context_info.insert(
- allocated_id,
- StackingContextInfo::new(real_stacking_context_id),
- );
-
- allocated_id
- }
-
- fn add_stacking_context(
- &mut self,
- parent_id: StackingContextId,
- stacking_context: StackingContext,
- ) {
- self.stacking_context_info
- .get_mut(&parent_id)
- .unwrap()
- .children
- .push(stacking_context);
- }
-
- fn add_clip_scroll_node(&mut self, clip_scroll_node: ClipScrollNode) -> ClipScrollNodeIndex {
- let is_placeholder = clip_scroll_node.is_placeholder();
-
- self.clip_scroll_nodes.push(clip_scroll_node);
- let index = ClipScrollNodeIndex::new(self.clip_scroll_nodes.len() - 1);
-
- // If this node is a placeholder node (currently just reference frames), then don't add
- // it to the stacking context list. Placeholder nodes are created automatically by
- // WebRender and we don't want to explicitly create them in the display list. The node
- // is just there to take up a spot in the global list of ClipScrollNodes.
- if !is_placeholder {
- // We want the scroll root to be defined before any possible item that could use it,
- // so we make sure that it is added to the beginning of the parent "real" (non-pseudo)
- // stacking context. This ensures that item reordering will not result in an item using
- // the scroll root before it is defined.
- self.stacking_context_info
- .get_mut(&self.current_real_stacking_context_id)
- .unwrap()
- .clip_scroll_nodes
- .push(index);
- }
-
- index
- }
-}
-
-pub struct DisplayListBuildState<'a> {
- /// A LayoutContext reference important for creating WebRender images.
- pub layout_context: &'a LayoutContext<'a>,
-
- /// The root of the StackingContext tree.
- pub root_stacking_context: StackingContext,
-
- /// StackingContext and ClipScrollNode children for each StackingContext.
- stacking_context_info: FnvHashMap<StackingContextId, StackingContextInfo>,
-
- /// A vector of ClipScrollNodes which will be given ids during WebRender DL conversion.
- pub clip_scroll_nodes: Vec<ClipScrollNode>,
-
- /// The items in this display list.
- pub items: FnvHashMap<StackingContextId, Vec<DisplayItem>>,
-
- /// Whether or not we are processing an element that establishes scrolling overflow. Used
- /// to determine what ClipScrollNode to place backgrounds and borders into.
- pub processing_scrolling_overflow_element: bool,
-
- /// The current stacking context id, used to keep track of state when building.
- /// recursively building and processing the display list.
- pub current_stacking_context_id: StackingContextId,
-
- /// The current clip and scroll info, used to keep track of state when
- /// recursively building and processing the display list.
- pub current_clipping_and_scrolling: ClippingAndScrolling,
-
- /// Vector containing iframe sizes, used to inform the constellation about
- /// new iframe sizes
- pub iframe_sizes: Vec<IFrameSize>,
-
- /// Stores text runs to answer text queries used to place a cursor inside text.
- pub indexable_text: IndexableText,
-}
-
-impl<'a> DisplayListBuildState<'a> {
- pub fn new(
- layout_context: &'a LayoutContext,
- state: StackingContextCollectionState,
- ) -> DisplayListBuildState<'a> {
- DisplayListBuildState {
- layout_context: layout_context,
- root_stacking_context: state.root_stacking_context,
- items: FnvHashMap::default(),
- stacking_context_info: state.stacking_context_info,
- clip_scroll_nodes: state.clip_scroll_nodes,
- processing_scrolling_overflow_element: false,
- current_stacking_context_id: StackingContextId::root(),
- current_clipping_and_scrolling: ClippingAndScrolling::simple(
- ClipScrollNodeIndex::root_scroll_node(),
- ),
- iframe_sizes: Vec::new(),
- indexable_text: IndexableText::default(),
- }
- }
-
- pub fn add_display_item(&mut self, display_item: DisplayItem) {
- let items = self
- .items
- .entry(display_item.stacking_context_id())
- .or_insert(Vec::new());
- items.push(display_item);
- }
-
- fn add_image_item(&mut self, base: BaseDisplayItem, item: webrender_api::ImageDisplayItem) {
- if item.stretch_size == LayoutSize::zero() {
- return;
- }
- self.add_display_item(DisplayItem::Image(CommonDisplayItem::new(base, item)))
- }
-
- fn parent_clip_scroll_node_index(&self, index: ClipScrollNodeIndex) -> ClipScrollNodeIndex {
- if index.is_root_scroll_node() {
- return index;
- }
-
- self.clip_scroll_nodes[index.to_index()].parent_index
- }
-
- fn is_background_or_border_of_clip_scroll_node(&self, section: DisplayListSection) -> bool {
- (section == DisplayListSection::BackgroundAndBorders ||
- section == DisplayListSection::BlockBackgroundsAndBorders) &&
- self.processing_scrolling_overflow_element
- }
-
- pub fn create_base_display_item(
- &self,
- clip_rect: Rect<Au>,
- node: OpaqueNode,
- cursor: Option<Cursor>,
- section: DisplayListSection,
- ) -> BaseDisplayItem {
- let clipping_and_scrolling = if self.is_background_or_border_of_clip_scroll_node(section) {
- ClippingAndScrolling::simple(
- self.parent_clip_scroll_node_index(self.current_clipping_and_scrolling.scrolling),
- )
- } else {
- self.current_clipping_and_scrolling
- };
- self.create_base_display_item_with_clipping_and_scrolling(
- clip_rect,
- node,
- cursor,
- section,
- clipping_and_scrolling,
- )
- }
-
- fn create_base_display_item_with_clipping_and_scrolling(
- &self,
- clip_rect: Rect<Au>,
- node: OpaqueNode,
- cursor: Option<Cursor>,
- section: DisplayListSection,
- clipping_and_scrolling: ClippingAndScrolling,
- ) -> BaseDisplayItem {
- BaseDisplayItem::new(
- DisplayItemMetadata {
- node,
- // Store cursor id in display list.
- pointing: cursor.map(|x| x as u16),
- },
- clip_rect.to_layout(),
- section,
- self.current_stacking_context_id,
- clipping_and_scrolling,
- )
- }
-
- fn add_late_clip_node(&mut self, rect: LayoutRect, radii: BorderRadius) -> ClipScrollNodeIndex {
- let mut clip = ClippingRegion::from_rect(rect);
- clip.intersect_with_rounded_rect(rect, radii);
-
- let node = ClipScrollNode {
- parent_index: self.current_clipping_and_scrolling.scrolling,
- clip,
- content_rect: LayoutRect::zero(), // content_rect isn't important for clips.
- node_type: ClipScrollNodeType::Clip,
- };
-
- // We want the scroll root to be defined before any possible item that could use it,
- // so we make sure that it is added to the beginning of the parent "real" (non-pseudo)
- // stacking context. This ensures that item reordering will not result in an item using
- // the scroll root before it is defined.
- self.clip_scroll_nodes.push(node);
- let index = ClipScrollNodeIndex::new(self.clip_scroll_nodes.len() - 1);
- let real_stacking_context_id =
- self.stacking_context_info[&self.current_stacking_context_id].real_stacking_context_id;
- self.stacking_context_info
- .get_mut(&real_stacking_context_id)
- .unwrap()
- .clip_scroll_nodes
- .push(index);
-
- index
- }
-
- pub fn to_display_list(mut self) -> DisplayList {
- let mut list = Vec::new();
- let root_context = mem::replace(&mut self.root_stacking_context, StackingContext::root());
-
- self.to_display_list_for_stacking_context(&mut list, root_context);
-
- DisplayList {
- list: list,
- clip_scroll_nodes: self.clip_scroll_nodes,
- }
- }
-
- fn to_display_list_for_stacking_context(
- &mut self,
- list: &mut Vec<DisplayItem>,
- stacking_context: StackingContext,
- ) {
- let mut child_items = self
- .items
- .remove(&stacking_context.id)
- .unwrap_or(Vec::new());
- child_items.sort_by(|a, b| a.base().section.cmp(&b.base().section));
- child_items.reverse();
-
- let mut info = self
- .stacking_context_info
- .remove(&stacking_context.id)
- .unwrap();
-
- info.children.sort();
-
- if stacking_context.context_type != StackingContextType::Real {
- list.extend(
- info.clip_scroll_nodes
- .into_iter()
- .map(|index| index.to_define_item()),
- );
- self.to_display_list_for_items(list, child_items, info.children);
- } else {
- let (push_item, pop_item) = stacking_context.to_display_list_items();
- list.push(push_item);
- list.extend(
- info.clip_scroll_nodes
- .into_iter()
- .map(|index| index.to_define_item()),
- );
- self.to_display_list_for_items(list, child_items, info.children);
- list.push(pop_item);
- }
- }
-
- fn to_display_list_for_items(
- &mut self,
- list: &mut Vec<DisplayItem>,
- mut child_items: Vec<DisplayItem>,
- child_stacking_contexts: Vec<StackingContext>,
- ) {
- // Properly order display items that make up a stacking context. "Steps" here
- // refer to the steps in CSS 2.1 Appendix E.
- // Steps 1 and 2: Borders and background for the root.
- while child_items.last().map_or(false, |child| {
- child.section() == DisplayListSection::BackgroundAndBorders
- }) {
- list.push(child_items.pop().unwrap());
- }
-
- // Step 3: Positioned descendants with negative z-indices.
- let mut child_stacking_contexts = child_stacking_contexts.into_iter().peekable();
- while child_stacking_contexts
- .peek()
- .map_or(false, |child| child.z_index < 0)
- {
- let context = child_stacking_contexts.next().unwrap();
- self.to_display_list_for_stacking_context(list, context);
- }
-
- // Step 4: Block backgrounds and borders.
- while child_items.last().map_or(false, |child| {
- child.section() == DisplayListSection::BlockBackgroundsAndBorders
- }) {
- list.push(child_items.pop().unwrap());
- }
-
- // Step 5: Floats.
- while child_stacking_contexts.peek().map_or(false, |child| {
- child.context_type == StackingContextType::PseudoFloat
- }) {
- let context = child_stacking_contexts.next().unwrap();
- self.to_display_list_for_stacking_context(list, context);
- }
-
- // Step 6 & 7: Content and inlines that generate stacking contexts.
- while child_items.last().map_or(false, |child| {
- child.section() == DisplayListSection::Content
- }) {
- list.push(child_items.pop().unwrap());
- }
-
- // Step 8 & 9: Positioned descendants with nonnegative, numeric z-indices.
- for child in child_stacking_contexts {
- self.to_display_list_for_stacking_context(list, child);
- }
-
- // Step 10: Outlines.
- for item in child_items.drain(..) {
- list.push(item);
- }
- }
-
- fn clipping_and_scrolling_scope<R, F: FnOnce(&mut Self) -> R>(&mut self, function: F) -> R {
- let previous_clipping_and_scrolling = self.current_clipping_and_scrolling;
- let ret = function(self);
- self.current_clipping_and_scrolling = previous_clipping_and_scrolling;
- ret
- }
-}
-
-/// The logical width of an insertion point: at the moment, a one-pixel-wide line.
-const INSERTION_POINT_LOGICAL_WIDTH: Au = Au(1 * AU_PER_PX);
-
-/// Get the border radius for the rectangle inside of a rounded border. This is useful
-/// for building the clip for the content inside the border.
-fn build_border_radius_for_inner_rect(
- outer_rect: Rect<Au>,
- style: &ComputedValues,
-) -> BorderRadius {
- let radii = border::radii(outer_rect, style.get_border());
- if radii.is_zero() {
- return radii;
- }
-
- // Since we are going to using the inner rectangle (outer rectangle minus
- // border width), we need to adjust to border radius so that we are smaller
- // rectangle with the same border curve.
- let border_widths = style.logical_border_width().to_physical(style.writing_mode);
- border::inner_radii(radii, border_widths)
-}
-
-impl Fragment {
- pub fn collect_stacking_contexts_for_blocklike_fragment(
- &mut self,
- state: &mut StackingContextCollectionState,
- ) -> bool {
- match self.specific {
- SpecificFragmentInfo::InlineBlock(ref mut block_flow) => {
- let block_flow = FlowRef::deref_mut(&mut block_flow.flow_ref);
- block_flow.collect_stacking_contexts(state);
- true
- },
- SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut block_flow) => {
- let block_flow = FlowRef::deref_mut(&mut block_flow.flow_ref);
- block_flow.collect_stacking_contexts(state);
- true
- },
- SpecificFragmentInfo::InlineAbsolute(ref mut block_flow) => {
- let block_flow = FlowRef::deref_mut(&mut block_flow.flow_ref);
- block_flow.collect_stacking_contexts(state);
- true
- },
- // FIXME: In the future, if #15144 is fixed we can remove this case. See #18510.
- SpecificFragmentInfo::TruncatedFragment(ref mut info) => info
- .full
- .collect_stacking_contexts_for_blocklike_fragment(state),
- _ => false,
- }
- }
-
- pub fn create_stacking_context_for_inline_block(
- &mut self,
- base: &BaseFlow,
- state: &mut StackingContextCollectionState,
- ) -> bool {
- self.stacking_context_id = state.allocate_stacking_context_info(StackingContextType::Real);
-
- let established_reference_frame = if self.can_establish_reference_frame() {
- // WebRender currently creates reference frames automatically, so just add
- // a placeholder node to allocate a ClipScrollNodeIndex for this reference frame.
- self.established_reference_frame =
- Some(state.add_clip_scroll_node(ClipScrollNode::placeholder()));
- self.established_reference_frame
- } else {
- None
- };
-
- let current_stacking_context_id = state.current_stacking_context_id;
- let stacking_context = self.create_stacking_context(
- self.stacking_context_id,
- &base,
- StackingContextType::Real,
- established_reference_frame,
- state.current_clipping_and_scrolling,
- );
- state.add_stacking_context(current_stacking_context_id, stacking_context);
- true
- }
-
- /// Adds the display items necessary to paint the background of this fragment to the display
- /// list if necessary.
- fn build_display_list_for_background_if_applicable(
- &self,
- state: &mut DisplayListBuildState,
- style: &ComputedValues,
- display_list_section: DisplayListSection,
- absolute_bounds: Rect<Au>,
- ) {
- let background = style.get_background();
- let background_color = style.resolve_color(background.background_color);
- // XXXManishearth the below method should ideally use an iterator over
- // backgrounds
- self.build_display_list_for_background_if_applicable_with_background(
- state,
- style,
- background,
- background_color,
- display_list_section,
- absolute_bounds,
- )
- }
-
- /// Same as build_display_list_for_background_if_applicable, but lets you
- /// override the actual background used
- fn build_display_list_for_background_if_applicable_with_background(
- &self,
- state: &mut DisplayListBuildState,
- style: &ComputedValues,
- background: &style_structs::Background,
- background_color: RGBA,
- display_list_section: DisplayListSection,
- absolute_bounds: Rect<Au>,
- ) {
- // FIXME: This causes a lot of background colors to be displayed when they are clearly not
- // needed. We could use display list optimization to clean this up, but it still seems
- // inefficient. What we really want is something like "nearest ancestor element that
- // doesn't have a fragment".
-
- // Quote from CSS Backgrounds and Borders Module Level 3:
- //
- // > The background color is clipped according to the background-clip value associated
- // > with the bottom-most background image layer.
- let last_background_image_index = background.background_image.0.len() - 1;
- let color_clip = *get_cyclic(&background.background_clip.0, last_background_image_index);
- let (bounds, border_radii) = background::clip(
- color_clip,
- absolute_bounds,
- style.logical_border_width().to_physical(style.writing_mode),
- self.border_padding.to_physical(self.style.writing_mode),
- border::radii(absolute_bounds, style.get_border()),
- );
-
- state.clipping_and_scrolling_scope(|state| {
- if !border_radii.is_zero() {
- let clip_id = state.add_late_clip_node(bounds.to_layout(), border_radii);
- state.current_clipping_and_scrolling = ClippingAndScrolling::simple(clip_id);
- }
-
- let base = state.create_base_display_item(
- bounds,
- self.node,
- get_cursor(&style, Cursor::Default),
- display_list_section,
- );
- state.add_display_item(DisplayItem::Rectangle(CommonDisplayItem::new(
- base,
- webrender_api::RectangleDisplayItem {
- color: background_color.to_layout(),
- common: items::empty_common_item_properties(),
- },
- )));
- });
-
- // The background image is painted on top of the background color.
- // Implements background image, per spec:
- // http://www.w3.org/TR/CSS21/colors.html#background
- let background = style.get_background();
- for (i, background_image) in background.background_image.0.iter().enumerate().rev() {
- let background_image = match *background_image {
- ImageLayer::None => continue,
- ImageLayer::Image(ref image) => image,
- };
-
- match *background_image {
- Image::Gradient(ref gradient) => {
- self.build_display_list_for_background_gradient(
- state,
- display_list_section,
- absolute_bounds,
- gradient,
- style,
- i,
- );
- },
- Image::Url(ref image_url) => {
- if let Some(url) = image_url.url() {
- let webrender_image = state.layout_context.get_webrender_image_for_url(
- self.node,
- url.clone(),
- UsePlaceholder::No,
- );
- if let Some(webrender_image) = webrender_image {
- self.build_display_list_for_webrender_image(
- state,
- style,
- display_list_section,
- absolute_bounds,
- webrender_image,
- i,
- );
- }
- }
- },
- Image::PaintWorklet(ref paint_worklet) => {
- let bounding_box = self.border_box - style.logical_border_width();
- let bounding_box_size = bounding_box.size.to_physical(style.writing_mode);
- let background_size =
- get_cyclic(&style.get_background().background_size.0, i).clone();
- let size = match background_size {
- BackgroundSize::ExplicitSize { width, height } => Size2D::new(
- width
- .to_used_value(bounding_box_size.width)
- .unwrap_or(bounding_box_size.width),
- height
- .to_used_value(bounding_box_size.height)
- .unwrap_or(bounding_box_size.height),
- ),
- _ => bounding_box_size,
- };
- let webrender_image = self.get_webrender_image_for_paint_worklet(
- state,
- style,
- paint_worklet,
- size,
- );
- if let Some(webrender_image) = webrender_image {
- self.build_display_list_for_webrender_image(
- state,
- style,
- display_list_section,
- absolute_bounds,
- webrender_image,
- i,
- );
- }
- },
- Image::Rect(_) => {
- // TODO: Implement `-moz-image-rect`
- },
- Image::Element(_) => {
- // TODO: Implement `-moz-element`
- },
- }
- }
- }
-
- /// Adds the display items necessary to paint a webrender image of this fragment to the
- /// appropriate section of the display list.
- fn build_display_list_for_webrender_image(
- &self,
- state: &mut DisplayListBuildState,
- style: &ComputedValues,
- display_list_section: DisplayListSection,
- absolute_bounds: Rect<Au>,
- webrender_image: WebRenderImageInfo,
- index: usize,
- ) {
- debug!("(building display list) building background image");
- if webrender_image.key.is_none() {
- return;
- }
-
- let image = Size2D::new(
- Au::from_px(webrender_image.width as i32),
- Au::from_px(webrender_image.height as i32),
- );
- let placement = background::placement(
- style.get_background(),
- state.layout_context.shared_context().viewport_size(),
- absolute_bounds,
- Some(image),
- style.logical_border_width().to_physical(style.writing_mode),
- self.border_padding.to_physical(self.style.writing_mode),
- border::radii(absolute_bounds, style.get_border()),
- index,
- );
-
- state.clipping_and_scrolling_scope(|state| {
- if !placement.clip_radii.is_zero() {
- let clip_id =
- state.add_late_clip_node(placement.clip_rect.to_layout(), placement.clip_radii);
- state.current_clipping_and_scrolling = ClippingAndScrolling::simple(clip_id);
- }
-
- // Create the image display item.
- let base = state.create_base_display_item(
- placement.clip_rect,
- self.node,
- get_cursor(&style, Cursor::Default),
- display_list_section,
- );
-
- debug!("(building display list) adding background image.");
- state.add_image_item(
- base,
- webrender_api::ImageDisplayItem {
- bounds: placement.bounds.to_f32_px(),
- common: items::empty_common_item_properties(),
- image_key: webrender_image.key.unwrap(),
- stretch_size: placement.tile_size.to_layout(),
- tile_spacing: placement.tile_spacing.to_layout(),
- image_rendering: style.get_inherited_box().image_rendering.to_layout(),
- alpha_type: webrender_api::AlphaType::PremultipliedAlpha,
- color: webrender_api::ColorF::WHITE,
- },
- );
- });
- }
-
- /// Calculates the webrender image for a paint worklet.
- /// Returns None if the worklet is not registered.
- /// If the worklet has missing image URLs, it passes them to the image cache for loading.
- fn get_webrender_image_for_paint_worklet(
- &self,
- state: &mut DisplayListBuildState,
- style: &ComputedValues,
- paint_worklet: &PaintWorklet,
- size_in_au: Size2D<Au>,
- ) -> Option<WebRenderImageInfo> {
- let device_pixel_ratio = state.layout_context.style_context.device_pixel_ratio();
- let size_in_px =
- TypedSize2D::new(size_in_au.width.to_f32_px(), size_in_au.height.to_f32_px());
-
- // TODO: less copying.
- let name = paint_worklet.name.clone();
- let arguments = paint_worklet
- .arguments
- .iter()
- .map(|argument| argument.to_css_string())
- .collect();
-
- let draw_result = match state.layout_context.registered_painters.get(&name) {
- Some(painter) => {
- debug!(
- "Drawing a paint image {}({},{}).",
- name, size_in_px.width, size_in_px.height
- );
- let properties = painter
- .properties()
- .iter()
- .filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id)))
- .map(|(name, id)| (name.clone(), style.computed_value_to_string(id)))
- .collect();
- painter.draw_a_paint_image(size_in_px, device_pixel_ratio, properties, arguments)
- },
- None => {
- debug!("Worklet {} called before registration.", name);
- return None;
- },
- };
-
- if let Ok(draw_result) = draw_result {
- let webrender_image = WebRenderImageInfo {
- width: draw_result.width,
- height: draw_result.height,
- key: draw_result.image_key,
- };
-
- for url in draw_result.missing_image_urls.into_iter() {
- debug!("Requesting missing image URL {}.", url);
- state.layout_context.get_webrender_image_for_url(
- self.node,
- url,
- UsePlaceholder::No,
- );
- }
- Some(webrender_image)
- } else {
- None
- }
- }
-
- /// Adds the display items necessary to paint the background linear gradient of this fragment
- /// to the appropriate section of the display list.
- fn build_display_list_for_background_gradient(
- &self,
- state: &mut DisplayListBuildState,
- display_list_section: DisplayListSection,
- absolute_bounds: Rect<Au>,
- gradient: &Gradient,
- style: &ComputedValues,
- index: usize,
- ) {
- let placement = background::placement(
- style.get_background(),
- state.layout_context.shared_context().viewport_size(),
- absolute_bounds,
- None,
- style.logical_border_width().to_physical(style.writing_mode),
- self.border_padding.to_physical(self.style.writing_mode),
- border::radii(absolute_bounds, style.get_border()),
- index,
- );
-
- state.clipping_and_scrolling_scope(|state| {
- if !placement.clip_radii.is_zero() {
- let clip_id =
- state.add_late_clip_node(placement.clip_rect.to_layout(), placement.clip_radii);
- state.current_clipping_and_scrolling = ClippingAndScrolling::simple(clip_id);
- }
-
- let base = state.create_base_display_item(
- placement.clip_rect,
- self.node,
- get_cursor(&style, Cursor::Default),
- display_list_section,
- );
-
- let display_item = match gradient.kind {
- GradientKind::Linear(angle_or_corner) => {
- let (gradient, stops) = gradient::linear(
- style,
- placement.tile_size,
- &gradient.items[..],
- angle_or_corner,
- gradient.repeating,
- );
- let item = webrender_api::GradientDisplayItem {
- gradient,
- bounds: placement.bounds.to_f32_px(),
- common: items::empty_common_item_properties(),
- tile_size: placement.tile_size.to_layout(),
- tile_spacing: placement.tile_spacing.to_layout(),
- };
- DisplayItem::Gradient(CommonDisplayItem::with_data(base, item, stops))
- },
- GradientKind::Radial(shape, center) => {
- let (gradient, stops) = gradient::radial(
- style,
- placement.tile_size,
- &gradient.items[..],
- shape,
- center,
- gradient.repeating,
- );
- let item = webrender_api::RadialGradientDisplayItem {
- gradient,
- bounds: placement.bounds.to_f32_px(),
- common: items::empty_common_item_properties(),
- tile_size: placement.tile_size.to_layout(),
- tile_spacing: placement.tile_spacing.to_layout(),
- };
- DisplayItem::RadialGradient(CommonDisplayItem::with_data(base, item, stops))
- },
- };
- state.add_display_item(display_item);
- });
- }
-
- /// Adds the display items necessary to paint the box shadow of this fragment to the display
- /// list if necessary.
- fn build_display_list_for_box_shadow_if_applicable(
- &self,
- state: &mut DisplayListBuildState,
- style: &ComputedValues,
- display_list_section: DisplayListSection,
- absolute_bounds: Rect<Au>,
- clip: Rect<Au>,
- ) {
- // NB: According to CSS-BACKGROUNDS, box shadows render in *reverse* order (front to back).
- for box_shadow in style.get_effects().box_shadow.0.iter().rev() {
- let base = state.create_base_display_item(
- clip,
- self.node,
- get_cursor(&style, Cursor::Default),
- display_list_section,
- );
- let border_radius = border::radii(absolute_bounds, style.get_border());
- state.add_display_item(DisplayItem::BoxShadow(CommonDisplayItem::new(
- base,
- webrender_api::BoxShadowDisplayItem {
- common: items::empty_common_item_properties(),
- box_bounds: absolute_bounds.to_layout(),
- color: style.resolve_color(box_shadow.base.color).to_layout(),
- offset: LayoutVector2D::new(
- box_shadow.base.horizontal.px(),
- box_shadow.base.vertical.px(),
- ),
- blur_radius: box_shadow.base.blur.px(),
- spread_radius: box_shadow.spread.px(),
- border_radius: border_radius,
- clip_mode: if box_shadow.inset {
- BoxShadowClipMode::Inset
- } else {
- BoxShadowClipMode::Outset
- },
- },
- )));
- }
- }
-
- /// Adds the display items necessary to paint the borders of this fragment to a display list if
- /// necessary.
- fn build_display_list_for_borders_if_applicable(
- &self,
- state: &mut DisplayListBuildState,
- style: &ComputedValues,
- inline_info: Option<InlineNodeBorderInfo>,
- border_painting_mode: BorderPaintingMode,
- mut bounds: Rect<Au>,
- display_list_section: DisplayListSection,
- clip: Rect<Au>,
- ) {
- let mut border = style.logical_border_width();
-
- if let Some(inline_info) = inline_info {
- modify_border_width_for_inline_sides(&mut border, inline_info);
- }
-
- match border_painting_mode {
- BorderPaintingMode::Separate => {},
- BorderPaintingMode::Collapse(collapsed_borders) => {
- collapsed_borders.adjust_border_widths_for_painting(&mut border)
- },
- BorderPaintingMode::Hidden => return,
- }
-
- let border_style_struct = style.get_border();
- let mut colors = SideOffsets2D::new(
- border_style_struct.border_top_color,
- border_style_struct.border_right_color,
- border_style_struct.border_bottom_color,
- border_style_struct.border_left_color,
- );
- let mut border_style = SideOffsets2D::new(
- border_style_struct.border_top_style,
- border_style_struct.border_right_style,
- border_style_struct.border_bottom_style,
- border_style_struct.border_left_style,
- );
-
- if let BorderPaintingMode::Collapse(collapsed_borders) = border_painting_mode {
- collapsed_borders.adjust_border_colors_and_styles_for_painting(
- &mut colors,
- &mut border_style,
- style.writing_mode,
- );
- }
-
- // If this border collapses, then we draw outside the boundaries we were given.
- if let BorderPaintingMode::Collapse(collapsed_borders) = border_painting_mode {
- collapsed_borders.adjust_border_bounds_for_painting(&mut bounds, style.writing_mode)
- }
-
- // Append the border to the display list.
- let base = state.create_base_display_item(
- clip,
- self.node,
- get_cursor(&style, Cursor::Default),
- display_list_section,
- );
-
- let border_radius = border::radii(bounds, border_style_struct);
- let border_widths = border.to_physical(style.writing_mode);
-
- if let ImageLayer::Image(ref image) = border_style_struct.border_image_source {
- if self
- .build_display_list_for_border_image(
- state,
- style,
- base.clone(),
- bounds,
- image,
- border_widths,
- )
- .is_some()
- {
- return;
- }
- // Fallback to rendering a solid border.
- }
- if border_widths == SideOffsets2D::zero() {
- return;
- }
- let details = BorderDetails::Normal(NormalBorder {
- left: BorderSide {
- color: style.resolve_color(colors.left).to_layout(),
- style: border_style.left.to_layout(),
- },
- right: BorderSide {
- color: style.resolve_color(colors.right).to_layout(),
- style: border_style.right.to_layout(),
- },
- top: BorderSide {
- color: style.resolve_color(colors.top).to_layout(),
- style: border_style.top.to_layout(),
- },
- bottom: BorderSide {
- color: style.resolve_color(colors.bottom).to_layout(),
- style: border_style.bottom.to_layout(),
- },
- radius: border_radius,
- do_aa: true,
- });
- state.add_display_item(DisplayItem::Border(CommonDisplayItem::with_data(
- base,
- webrender_api::BorderDisplayItem {
- bounds: bounds.to_layout(),
- common: items::empty_common_item_properties(),
- widths: border_widths.to_layout(),
- details,
- },
- Vec::new(),
- )));
- }
-
- /// Add display item for image border.
- ///
- /// Returns `Some` if the addition was successful.
- fn build_display_list_for_border_image(
- &self,
- state: &mut DisplayListBuildState,
- style: &ComputedValues,
- base: BaseDisplayItem,
- bounds: Rect<Au>,
- image: &Image,
- border_width: SideOffsets2D<Au>,
- ) -> Option<()> {
- let border_style_struct = style.get_border();
- let border_image_outset =
- border::image_outset(border_style_struct.border_image_outset, border_width);
- let border_image_area = bounds.outer_rect(border_image_outset).size;
- let border_image_width = border::image_width(
- &border_style_struct.border_image_width,
- border_width.to_layout(),
- border_image_area,
- );
- let border_image_repeat = &border_style_struct.border_image_repeat;
- let border_image_fill = border_style_struct.border_image_slice.fill;
- let border_image_slice = &border_style_struct.border_image_slice.offsets;
-
- let mut stops = Vec::new();
- let mut width = border_image_area.width.to_px() as u32;
- let mut height = border_image_area.height.to_px() as u32;
- let source = match image {
- Image::Url(ref image_url) => {
- let url = image_url.url()?;
- let image = state.layout_context.get_webrender_image_for_url(
- self.node,
- url.clone(),
- UsePlaceholder::No,
- )?;
- width = image.width;
- height = image.height;
- NinePatchBorderSource::Image(image.key?)
- },
- Image::PaintWorklet(ref paint_worklet) => {
- let image = self.get_webrender_image_for_paint_worklet(
- state,
- style,
- paint_worklet,
- border_image_area,
- )?;
- width = image.width;
- height = image.height;
- NinePatchBorderSource::Image(image.key?)
- },
- Image::Gradient(ref gradient) => match gradient.kind {
- GradientKind::Linear(angle_or_corner) => {
- let (wr_gradient, linear_stops) = gradient::linear(
- style,
- border_image_area,
- &gradient.items[..],
- angle_or_corner,
- gradient.repeating,
- );
- stops = linear_stops;
- NinePatchBorderSource::Gradient(wr_gradient)
- },
- GradientKind::Radial(shape, center) => {
- let (wr_gradient, radial_stops) = gradient::radial(
- style,
- border_image_area,
- &gradient.items[..],
- shape,
- center,
- gradient.repeating,
- );
- stops = radial_stops;
- NinePatchBorderSource::RadialGradient(wr_gradient)
- },
- },
- _ => return None,
- };
-
- let details = BorderDetails::NinePatch(NinePatchBorder {
- source,
- width: width as i32,
- height: height as i32,
- slice: border::image_slice(border_image_slice, width as i32, height as i32),
- fill: border_image_fill,
- repeat_horizontal: border_image_repeat.0.to_layout(),
- repeat_vertical: border_image_repeat.1.to_layout(),
- outset: SideOffsets2D::new(
- border_image_outset.top.to_f32_px(),
- border_image_outset.right.to_f32_px(),
- border_image_outset.bottom.to_f32_px(),
- border_image_outset.left.to_f32_px(),
- ),
- });
- state.add_display_item(DisplayItem::Border(CommonDisplayItem::with_data(
- base,
- webrender_api::BorderDisplayItem {
- bounds: bounds.to_layout(),
- common: items::empty_common_item_properties(),
- widths: border_image_width,
- details,
- },
- stops,
- )));
- Some(())
- }
-
- /// Adds the display items necessary to paint the outline of this fragment to the display list
- /// if necessary.
- fn build_display_list_for_outline_if_applicable(
- &self,
- state: &mut DisplayListBuildState,
- style: &ComputedValues,
- mut bounds: Rect<Au>,
- clip: Rect<Au>,
- ) {
- use style::values::specified::outline::OutlineStyle;
-
- let width = Au::from(style.get_outline().outline_width);
- if width == Au(0) {
- return;
- }
-
- let outline_style = match style.get_outline().outline_style {
- OutlineStyle::Auto => BorderStyle::Solid,
- // FIXME(emilio): I don't think this border-style check is
- // necessary, since border-style: none implies an outline-width of
- // zero at computed value time.
- OutlineStyle::BorderStyle(BorderStyle::None) => return,
- OutlineStyle::BorderStyle(s) => s,
- };
-
- // Outlines are not accounted for in the dimensions of the border box, so adjust the
- // absolute bounds.
- let offset = width + Au::from(style.get_outline().outline_offset);
- bounds = bounds.inflate(offset, offset);
-
- // Append the outline to the display list.
- let color = style
- .resolve_color(style.get_outline().outline_color)
- .to_layout();
- let base = state.create_base_display_item(
- clip,
- self.node,
- get_cursor(&style, Cursor::Default),
- DisplayListSection::Outlines,
- );
- state.add_display_item(DisplayItem::Border(CommonDisplayItem::with_data(
- base,
- webrender_api::BorderDisplayItem {
- bounds: bounds.to_layout(),
- common: items::empty_common_item_properties(),
- widths: SideOffsets2D::new_all_same(width).to_layout(),
- details: BorderDetails::Normal(border::simple(color, outline_style.to_layout())),
- },
- Vec::new(),
- )));
- }
-
- /// Adds display items necessary to draw debug boxes around a scanned text fragment.
- fn build_debug_borders_around_text_fragments(
- &self,
- state: &mut DisplayListBuildState,
- style: &ComputedValues,
- stacking_relative_border_box: Rect<Au>,
- stacking_relative_content_box: Rect<Au>,
- text_fragment: &ScannedTextFragmentInfo,
- clip: Rect<Au>,
- ) {
- // FIXME(pcwalton, #2795): Get the real container size.
- let container_size = Size2D::zero();
-
- // Compute the text fragment bounds and draw a border surrounding them.
- let base = state.create_base_display_item(
- clip,
- self.node,
- get_cursor(&style, Cursor::Default),
- DisplayListSection::Content,
- );
- state.add_display_item(DisplayItem::Border(CommonDisplayItem::with_data(
- base,
- webrender_api::BorderDisplayItem {
- bounds: stacking_relative_border_box.to_layout(),
- common: items::empty_common_item_properties(),
- widths: SideOffsets2D::new_all_same(Au::from_px(1)).to_layout(),
- details: BorderDetails::Normal(border::simple(
- ColorU::new(0, 0, 200, 1).into(),
- webrender_api::BorderStyle::Solid,
- )),
- },
- Vec::new(),
- )));
-
- // Draw a rectangle representing the baselines.
- let mut baseline = LogicalRect::from_physical(
- self.style.writing_mode,
- stacking_relative_content_box,
- container_size,
- );
- baseline.start.b = baseline.start.b + text_fragment.run.ascent();
- baseline.size.block = Au(0);
- let baseline = baseline.to_physical(self.style.writing_mode, container_size);
-
- let base = state.create_base_display_item(
- clip,
- self.node,
- get_cursor(&style, Cursor::Default),
- DisplayListSection::Content,
- );
- // TODO(gw): Use a better estimate for wavy line thickness.
- let area = baseline.to_layout();
- let wavy_line_thickness = (0.33 * area.size.height).ceil();
- state.add_display_item(DisplayItem::Line(CommonDisplayItem::new(
- base,
- webrender_api::LineDisplayItem {
- common: items::empty_common_item_properties(),
- area,
- orientation: webrender_api::LineOrientation::Horizontal,
- wavy_line_thickness,
- color: ColorU::new(0, 200, 0, 1).into(),
- style: LineStyle::Dashed,
- },
- )));
- }
-
- /// Adds display items necessary to draw debug boxes around this fragment.
- fn build_debug_borders_around_fragment(
- &self,
- state: &mut DisplayListBuildState,
- stacking_relative_border_box: Rect<Au>,
- clip: Rect<Au>,
- ) {
- // This prints a debug border around the border of this fragment.
- let base = state.create_base_display_item(
- clip,
- self.node,
- get_cursor(&self.style, Cursor::Default),
- DisplayListSection::Content,
- );
- state.add_display_item(DisplayItem::Border(CommonDisplayItem::with_data(
- base,
- webrender_api::BorderDisplayItem {
- bounds: stacking_relative_border_box.to_layout(),
- common: items::empty_common_item_properties(),
- widths: SideOffsets2D::new_all_same(Au::from_px(1)).to_layout(),
- details: BorderDetails::Normal(border::simple(
- ColorU::new(0, 0, 200, 1).into(),
- webrender_api::BorderStyle::Solid,
- )),
- },
- Vec::new(),
- )));
- }
-
- /// Builds the display items necessary to paint the selection and/or caret for this fragment,
- /// if any.
- fn build_display_items_for_selection_if_necessary(
- &self,
- state: &mut DisplayListBuildState,
- stacking_relative_border_box: Rect<Au>,
- display_list_section: DisplayListSection,
- ) {
- let scanned_text_fragment_info = match self.specific {
- SpecificFragmentInfo::ScannedText(ref scanned_text_fragment_info) => {
- scanned_text_fragment_info
- },
- _ => return,
- };
-
- // Draw a highlighted background if the text is selected.
- //
- // TODO: Allow non-text fragments to be selected too.
- if scanned_text_fragment_info.selected() {
- let style = self.selected_style();
- let background_color = style.resolve_color(style.get_background().background_color);
- let base = state.create_base_display_item(
- stacking_relative_border_box,
- self.node,
- get_cursor(&self.style, Cursor::Default),
- display_list_section,
- );
- state.add_display_item(DisplayItem::Rectangle(CommonDisplayItem::new(
- base,
- webrender_api::RectangleDisplayItem {
- common: items::empty_common_item_properties(),
- color: background_color.to_layout(),
- },
- )));
- }
-
- // Draw a caret at the insertion point.
- let insertion_point_index = match scanned_text_fragment_info.insertion_point {
- Some(insertion_point_index) => insertion_point_index,
- None => return,
- };
- let range = Range::new(
- scanned_text_fragment_info.range.begin(),
- insertion_point_index - scanned_text_fragment_info.range.begin(),
- );
- let advance = scanned_text_fragment_info.run.advance_for_range(&range);
-
- let insertion_point_bounds;
- let cursor;
- if !self.style.writing_mode.is_vertical() {
- insertion_point_bounds = rect(
- stacking_relative_border_box.origin.x + advance,
- stacking_relative_border_box.origin.y,
- INSERTION_POINT_LOGICAL_WIDTH,
- stacking_relative_border_box.size.height,
- );
- cursor = Cursor::Text;
- } else {
- insertion_point_bounds = rect(
- stacking_relative_border_box.origin.x,
- stacking_relative_border_box.origin.y + advance,
- stacking_relative_border_box.size.width,
- INSERTION_POINT_LOGICAL_WIDTH,
- );
- cursor = Cursor::VerticalText;
- };
-
- let base = state.create_base_display_item(
- insertion_point_bounds,
- self.node,
- get_cursor(&self.style, cursor),
- display_list_section,
- );
- state.add_display_item(DisplayItem::Rectangle(CommonDisplayItem::new(
- base,
- webrender_api::RectangleDisplayItem {
- common: items::empty_common_item_properties(),
- color: self.style().get_inherited_text().color.to_layout(),
- },
- )));
- }
-
- /// Adds the display items for this fragment to the given display list.
- ///
- /// Arguments:
- ///
- /// * `state`: The display building state, including the display list currently
- /// under construction and other metadata useful for constructing it.
- /// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
- /// * `clip`: The region to clip the display items to.
- /// * `overflow_content_size`: The size of content associated with this fragment
- /// that must have overflow handling applied to it. For a scrollable block
- /// flow, it is expected that this is the size of the child boxes.
- pub fn build_display_list(
- &mut self,
- state: &mut DisplayListBuildState,
- stacking_relative_border_box: Rect<Au>,
- border_painting_mode: BorderPaintingMode,
- display_list_section: DisplayListSection,
- clip: Rect<Au>,
- overflow_content_size: Option<Size2D<Au>>,
- ) {
- let previous_clipping_and_scrolling = state.current_clipping_and_scrolling;
- if let Some(index) = self.established_reference_frame {
- state.current_clipping_and_scrolling = ClippingAndScrolling::simple(index);
- }
-
- self.restyle_damage.remove(ServoRestyleDamage::REPAINT);
- self.build_display_list_no_damage(
- state,
- stacking_relative_border_box,
- border_painting_mode,
- display_list_section,
- clip,
- overflow_content_size,
- );
-
- state.current_clipping_and_scrolling = previous_clipping_and_scrolling;
- }
-
- /// build_display_list, but don't update the restyle damage
- ///
- /// Must be paired with a self.restyle_damage.remove(REPAINT) somewhere
- fn build_display_list_no_damage(
- &self,
- state: &mut DisplayListBuildState,
- stacking_relative_border_box: Rect<Au>,
- border_painting_mode: BorderPaintingMode,
- display_list_section: DisplayListSection,
- clip: Rect<Au>,
- overflow_content_size: Option<Size2D<Au>>,
- ) {
- if self.style().get_inherited_box().visibility != Visibility::Visible {
- return;
- }
-
- debug!(
- "Fragment::build_display_list at rel={:?}, abs={:?}: {:?}",
- self.border_box, stacking_relative_border_box, self
- );
-
- // Check the clip rect. If there's nothing to render at all, don't even construct display
- // list items.
- let empty_rect = !clip.intersects(&stacking_relative_border_box);
- if self.is_primary_fragment() && !empty_rect {
- // Add shadows, background, borders, and outlines, if applicable.
- if let Some(ref inline_context) = self.inline_context {
- for node in inline_context.nodes.iter().rev() {
- self.build_display_list_for_background_if_applicable(
- state,
- &*node.style,
- display_list_section,
- stacking_relative_border_box,
- );
-
- self.build_display_list_for_box_shadow_if_applicable(
- state,
- &*node.style,
- display_list_section,
- stacking_relative_border_box,
- clip,
- );
-
- self.build_display_list_for_borders_if_applicable(
- state,
- &*node.style,
- Some(InlineNodeBorderInfo {
- is_first_fragment_of_element: node
- .flags
- .contains(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT),
- is_last_fragment_of_element: node
- .flags
- .contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT),
- }),
- border_painting_mode,
- stacking_relative_border_box,
- display_list_section,
- clip,
- );
-
- // FIXME(emilio): Why does outline not do the same width
- // fixup as border?
- self.build_display_list_for_outline_if_applicable(
- state,
- &*node.style,
- stacking_relative_border_box,
- clip,
- );
- }
- }
-
- if !self.is_scanned_text_fragment() {
- self.build_display_list_for_background_if_applicable(
- state,
- &*self.style,
- display_list_section,
- stacking_relative_border_box,
- );
-
- self.build_display_list_for_box_shadow_if_applicable(
- state,
- &*self.style,
- display_list_section,
- stacking_relative_border_box,
- clip,
- );
-
- self.build_display_list_for_borders_if_applicable(
- state,
- &*self.style,
- /* inline_node_info = */ None,
- border_painting_mode,
- stacking_relative_border_box,
- display_list_section,
- clip,
- );
-
- self.build_display_list_for_outline_if_applicable(
- state,
- &*self.style,
- stacking_relative_border_box,
- clip,
- );
- }
- }
-
- if self.is_primary_fragment() {
- // Paint the selection point if necessary. Even an empty text fragment may have an
- // insertion point, so we do this even if `empty_rect` is true.
- self.build_display_items_for_selection_if_necessary(
- state,
- stacking_relative_border_box,
- display_list_section,
- );
- }
-
- if empty_rect {
- return;
- }
-
- debug!("Fragment::build_display_list: intersected. Adding display item...");
-
- if let Some(content_size) = overflow_content_size {
- // Create a transparent rectangle for hit-testing purposes that exists in front
- // of this fragment's background but behind its content. This ensures that any
- // hit tests inside the content box but not on actual content target the current
- // scrollable ancestor.
- let content_size = TypedRect::new(stacking_relative_border_box.origin, content_size);
- let base = state.create_base_display_item_with_clipping_and_scrolling(
- content_size,
- self.node,
- // FIXME(emilio): Why does this ignore pointer-events?
- get_cursor(&self.style, Cursor::Default).or(Some(Cursor::Default)),
- display_list_section,
- state.current_clipping_and_scrolling,
- );
- state.add_display_item(DisplayItem::Rectangle(CommonDisplayItem::new(
- base,
- webrender_api::RectangleDisplayItem {
- common: items::empty_common_item_properties(),
- color: ColorF::TRANSPARENT,
- },
- )));
- }
-
- // Create special per-fragment-type display items.
- state.clipping_and_scrolling_scope(|state| {
- self.build_fragment_type_specific_display_items(
- state,
- stacking_relative_border_box,
- clip,
- );
- });
-
- if opts::get().show_debug_fragment_borders {
- self.build_debug_borders_around_fragment(state, stacking_relative_border_box, clip)
- }
- }
-
- /// A helper method that `build_display_list` calls to create per-fragment-type display items.
- fn build_fragment_type_specific_display_items(
- &self,
- state: &mut DisplayListBuildState,
- stacking_relative_border_box: Rect<Au>,
- clip: Rect<Au>,
- ) {
- // Compute the context box position relative to the parent stacking context.
- let stacking_relative_content_box =
- self.stacking_relative_content_box(stacking_relative_border_box);
-
- let create_base_display_item = |state: &mut DisplayListBuildState| {
- // Adjust the clipping region as necessary to account for `border-radius`.
- let radii =
- build_border_radius_for_inner_rect(stacking_relative_border_box, &self.style);
-
- if !radii.is_zero() {
- let clip_id =
- state.add_late_clip_node(stacking_relative_border_box.to_layout(), radii);
- state.current_clipping_and_scrolling = ClippingAndScrolling::simple(clip_id);
- }
-
- state.create_base_display_item(
- stacking_relative_border_box,
- self.node,
- get_cursor(&self.style, Cursor::Default),
- DisplayListSection::Content,
- )
- };
-
- match self.specific {
- SpecificFragmentInfo::TruncatedFragment(ref truncated_fragment)
- if truncated_fragment.text_info.is_some() =>
- {
- let text_fragment = truncated_fragment.text_info.as_ref().unwrap();
- // Create the main text display item.
- self.build_display_list_for_text_fragment(
- state,
- &text_fragment,
- stacking_relative_content_box,
- &self.style.get_inherited_text().text_shadow.0,
- clip,
- );
-
- if opts::get().show_debug_fragment_borders {
- self.build_debug_borders_around_text_fragments(
- state,
- self.style(),
- stacking_relative_border_box,
- stacking_relative_content_box,
- &text_fragment,
- clip,
- );
- }
- }
- SpecificFragmentInfo::ScannedText(ref text_fragment) => {
- // Create the main text display item.
- self.build_display_list_for_text_fragment(
- state,
- &text_fragment,
- stacking_relative_content_box,
- &self.style.get_inherited_text().text_shadow.0,
- clip,
- );
-
- if opts::get().show_debug_fragment_borders {
- self.build_debug_borders_around_text_fragments(
- state,
- self.style(),
- stacking_relative_border_box,
- stacking_relative_content_box,
- &text_fragment,
- clip,
- );
- }
- },
- SpecificFragmentInfo::Generic |
- SpecificFragmentInfo::GeneratedContent(..) |
- SpecificFragmentInfo::Table |
- SpecificFragmentInfo::TableCell |
- SpecificFragmentInfo::TableRow |
- SpecificFragmentInfo::TableWrapper |
- SpecificFragmentInfo::Multicol |
- SpecificFragmentInfo::MulticolColumn |
- SpecificFragmentInfo::InlineBlock(_) |
- SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
- SpecificFragmentInfo::InlineAbsolute(_) |
- SpecificFragmentInfo::TruncatedFragment(_) |
- SpecificFragmentInfo::Svg(_) => {
- if opts::get().show_debug_fragment_borders {
- self.build_debug_borders_around_fragment(
- state,
- stacking_relative_border_box,
- clip,
- );
- }
- },
- SpecificFragmentInfo::Iframe(ref fragment_info) => {
- if !stacking_relative_content_box.is_empty() {
- let browsing_context_id = match fragment_info.browsing_context_id {
- Some(browsing_context_id) => browsing_context_id,
- None => return warn!("No browsing context id for iframe."),
- };
- let pipeline_id = match fragment_info.pipeline_id {
- Some(pipeline_id) => pipeline_id,
- None => return warn!("No pipeline id for iframe {}.", browsing_context_id),
- };
-
- let base = create_base_display_item(state);
- let bounds = stacking_relative_content_box.to_layout();
- let item = DisplayItem::Iframe(Box::new(IframeDisplayItem {
- base,
- bounds,
- iframe: pipeline_id,
- }));
-
- // XXXjdm: This sleight-of-hand to convert LayoutRect -> Size2D<CSSPixel>
- // looks bogus.
- let size = Size2D::new(bounds.size.width, bounds.size.height);
- state.iframe_sizes.push(IFrameSize {
- id: browsing_context_id,
- size: TypedSize2D::from_untyped(&size),
- });
-
- state.add_display_item(item);
- }
- },
- SpecificFragmentInfo::Image(ref image_fragment) => {
- // Place the image into the display list.
- if let Some(ref image) = image_fragment.image {
- if let Some(id) = image.id {
- let base = create_base_display_item(state);
- state.add_image_item(
- base,
- webrender_api::ImageDisplayItem {
- bounds: stacking_relative_content_box.to_layout(),
- common: items::empty_common_item_properties(),
- image_key: id,
- stretch_size: stacking_relative_content_box.size.to_layout(),
- tile_spacing: LayoutSize::zero(),
- image_rendering: self
- .style
- .get_inherited_box()
- .image_rendering
- .to_layout(),
- alpha_type: webrender_api::AlphaType::PremultipliedAlpha,
- color: webrender_api::ColorF::WHITE,
- },
- );
- }
- }
- },
- SpecificFragmentInfo::Media(ref fragment_info) => {
- if let Some((ref image_key, _, _)) = fragment_info.current_frame {
- let base = create_base_display_item(state);
- state.add_image_item(
- base,
- webrender_api::ImageDisplayItem {
- bounds: stacking_relative_content_box.to_layout(),
- common: items::empty_common_item_properties(),
- image_key: *image_key,
- stretch_size: stacking_relative_border_box.size.to_layout(),
- tile_spacing: LayoutSize::zero(),
- image_rendering: ImageRendering::Auto,
- alpha_type: webrender_api::AlphaType::PremultipliedAlpha,
- color: webrender_api::ColorF::WHITE,
- },
- );
- }
- },
- SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => {
- let image_key = match canvas_fragment_info.source {
- CanvasFragmentSource::WebGL(image_key) => image_key,
- CanvasFragmentSource::Image(ref ipc_renderer) => match *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),
- canvas_fragment_info.canvas_id.clone(),
- ))
- .unwrap();
- receiver.recv().unwrap().image_key
- },
- None => return,
- },
- };
-
- let base = create_base_display_item(state);
- let display_item = webrender_api::ImageDisplayItem {
- bounds: stacking_relative_border_box.to_layout(),
- common: items::empty_common_item_properties(),
- image_key,
- stretch_size: stacking_relative_content_box.size.to_layout(),
- tile_spacing: LayoutSize::zero(),
- image_rendering: ImageRendering::Auto,
- alpha_type: webrender_api::AlphaType::PremultipliedAlpha,
- color: webrender_api::ColorF::WHITE,
- };
-
- state.add_image_item(base, display_item);
- },
- SpecificFragmentInfo::UnscannedText(_) => {
- panic!("Shouldn't see unscanned fragments here.")
- },
- SpecificFragmentInfo::TableColumn(_) => {
- panic!("Shouldn't see table column fragments here.")
- },
- }
- }
-
- /// Creates a stacking context for associated fragment.
- fn create_stacking_context(
- &self,
- id: StackingContextId,
- base_flow: &BaseFlow,
- context_type: StackingContextType,
- established_reference_frame: Option<ClipScrollNodeIndex>,
- parent_clipping_and_scrolling: ClippingAndScrolling,
- ) -> StackingContext {
- let border_box = self.stacking_relative_border_box(
- &base_flow.stacking_relative_position,
- &base_flow
- .early_absolute_position_info
- .relative_containing_block_size,
- base_flow
- .early_absolute_position_info
- .relative_containing_block_mode,
- CoordinateSystem::Parent,
- );
- // First, compute the offset of our border box (including relative positioning)
- // from our flow origin, since that is what `BaseFlow::overflow` is relative to.
- let border_box_offset = border_box
- .translate(&-base_flow.stacking_relative_position)
- .origin;
- // Then, using that, compute our overflow region relative to our border box.
- let overflow = base_flow
- .overflow
- .paint
- .translate(&-border_box_offset.to_vector());
-
- // Create the filter pipeline.
- let effects = self.style().get_effects();
- let mut filters: Vec<FilterOp> = effects.filter.0.iter().map(ToLayout::to_layout).collect();
- if effects.opacity != 1.0 {
- filters.push(FilterOp::Opacity(effects.opacity.into(), effects.opacity));
- }
-
- StackingContext::new(
- id,
- context_type,
- border_box.to_layout(),
- overflow.to_layout(),
- self.effective_z_index(),
- self.style().get_box()._servo_top_layer,
- filters,
- self.style().get_effects().mix_blend_mode.to_layout(),
- self.transform_matrix(&border_box),
- self.style().get_used_transform_style().to_layout(),
- self.perspective_matrix(&border_box),
- parent_clipping_and_scrolling,
- established_reference_frame,
- )
- }
-
- /// Creates the text display item for one text fragment. This can be called multiple times for
- /// one fragment if there are text shadows.
- ///
- /// `text_shadow` will be `Some` if this is rendering a shadow.
- fn build_display_list_for_text_fragment(
- &self,
- state: &mut DisplayListBuildState,
- text_fragment: &ScannedTextFragmentInfo,
- stacking_relative_content_box: Rect<Au>,
- text_shadows: &[SimpleShadow],
- clip: Rect<Au>,
- ) {
- // NB: The order for painting text components (CSS Text Decoration Module Level 3) is:
- // shadows, underline, overline, text, text-emphasis, and then line-through.
-
- // TODO(emilio): Allow changing more properties by ::selection
- // Paint the text with the color as described in its styling.
- let text_color = if text_fragment.selected() {
- self.selected_style().get_inherited_text().color
- } else {
- self.style().get_inherited_text().color
- };
-
- // Determine the orientation and cursor to use.
- let (_orientation, cursor) = if self.style.writing_mode.is_vertical() {
- // TODO: Distinguish between 'sideways-lr' and 'sideways-rl' writing modes in CSS
- // Writing Modes Level 4.
- (TextOrientation::SidewaysRight, Cursor::VerticalText)
- } else {
- (TextOrientation::Upright, Cursor::Text)
- };
-
- // Compute location of the baseline.
- //
- // FIXME(pcwalton): Get the real container size.
- let container_size = Size2D::zero();
- let metrics = &text_fragment.run.font_metrics;
- let baseline_origin = stacking_relative_content_box.origin +
- LogicalPoint::new(self.style.writing_mode, Au(0), metrics.ascent)
- .to_physical(self.style.writing_mode, container_size)
- .to_vector();
-
- // Base item for all text/shadows
- let base = state.create_base_display_item(
- clip,
- self.node,
- get_cursor(&self.style, cursor),
- DisplayListSection::Content,
- );
-
- // NB: According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front
- // to back).
-
- // Shadows
- for shadow in text_shadows.iter().rev() {
- state.add_display_item(DisplayItem::PushTextShadow(Box::new(
- PushTextShadowDisplayItem {
- base: base.clone(),
- shadow: webrender_api::Shadow {
- offset: LayoutVector2D::new(shadow.horizontal.px(), shadow.vertical.px()),
- color: self.style.resolve_color(shadow.color).to_layout(),
- blur_radius: shadow.blur.px(),
- },
- },
- )));
- }
-
- // Create display items for text decorations.
- let text_decorations = self.style().get_inherited_text().text_decorations_in_effect;
-
- let logical_stacking_relative_content_box = LogicalRect::from_physical(
- self.style.writing_mode,
- stacking_relative_content_box,
- container_size,
- );
-
- // Underline
- if text_decorations.underline {
- let mut stacking_relative_box = logical_stacking_relative_content_box;
- stacking_relative_box.start.b = logical_stacking_relative_content_box.start.b +
- metrics.ascent -
- metrics.underline_offset;
- stacking_relative_box.size.block = metrics.underline_size;
- self.build_display_list_for_text_decoration(
- state,
- &text_color,
- &stacking_relative_box,
- clip,
- );
- }
-
- // Overline
- if text_decorations.overline {
- let mut stacking_relative_box = logical_stacking_relative_content_box;
- stacking_relative_box.size.block = metrics.underline_size;
- self.build_display_list_for_text_decoration(
- state,
- &text_color,
- &stacking_relative_box,
- clip,
- );
- }
-
- // Text
- let glyphs = convert_text_run_to_glyphs(
- text_fragment.run.clone(),
- text_fragment.range,
- baseline_origin,
- );
- if !glyphs.is_empty() {
- let indexable_text = IndexableTextItem {
- origin: stacking_relative_content_box.origin,
- text_run: text_fragment.run.clone(),
- range: text_fragment.range,
- baseline_origin,
- };
- state.indexable_text.insert(self.node, indexable_text);
-
- state.add_display_item(DisplayItem::Text(CommonDisplayItem::with_data(
- base.clone(),
- webrender_api::TextDisplayItem {
- bounds: stacking_relative_content_box.to_layout(),
- common: items::empty_common_item_properties(),
- font_key: text_fragment.run.font_key,
- color: text_color.to_layout(),
- glyph_options: None,
- },
- glyphs,
- )));
- }
-
- // TODO(#17715): emit text-emphasis marks here.
- // (just push another TextDisplayItem?)
-
- // Line-Through
- if text_decorations.line_through {
- let mut stacking_relative_box = logical_stacking_relative_content_box;
- stacking_relative_box.start.b =
- stacking_relative_box.start.b + metrics.ascent - metrics.strikeout_offset;
- stacking_relative_box.size.block = metrics.strikeout_size;
- self.build_display_list_for_text_decoration(
- state,
- &text_color,
- &stacking_relative_box,
- clip,
- );
- }
-
- // Pop all the PushTextShadows
- if !text_shadows.is_empty() {
- state.add_display_item(DisplayItem::PopAllTextShadows(Box::new(
- PopAllTextShadowsDisplayItem { base },
- )));
- }
- }
-
- /// Creates the display item for a text decoration: underline, overline, or line-through.
- fn build_display_list_for_text_decoration(
- &self,
- state: &mut DisplayListBuildState,
- color: &RGBA,
- stacking_relative_box: &LogicalRect<Au>,
- clip: Rect<Au>,
- ) {
- // FIXME(pcwalton, #2795): Get the real container size.
- let container_size = Size2D::zero();
- let stacking_relative_box =
- stacking_relative_box.to_physical(self.style.writing_mode, container_size);
- let base = state.create_base_display_item(
- clip,
- self.node,
- get_cursor(&self.style, Cursor::Default),
- DisplayListSection::Content,
- );
-
- // TODO(gw): Use a better estimate for wavy line thickness.
- let area = stacking_relative_box.to_layout();
- let wavy_line_thickness = (0.33 * area.size.height).ceil();
- state.add_display_item(DisplayItem::Line(CommonDisplayItem::new(
- base,
- webrender_api::LineDisplayItem {
- common: items::empty_common_item_properties(),
- area,
- orientation: webrender_api::LineOrientation::Horizontal,
- wavy_line_thickness,
- color: color.to_layout(),
- style: LineStyle::Solid,
- },
- )));
- }
-
- fn unique_id(&self) -> u64 {
- let fragment_type = self.fragment_type();
- let id = self.node.id() as usize;
- combine_id_with_fragment_type(id, fragment_type) as u64
- }
-
- fn fragment_type(&self) -> FragmentType {
- self.pseudo.fragment_type()
- }
-}
-
-bitflags! {
- pub struct StackingContextCollectionFlags: u8 {
- /// This flow never establishes a containing block.
- const POSITION_NEVER_CREATES_CONTAINING_BLOCK = 0b001;
- /// This flow never creates a ClipScrollNode.
- const NEVER_CREATES_CLIP_SCROLL_NODE = 0b010;
- /// This flow never creates a stacking context.
- const NEVER_CREATES_STACKING_CONTEXT = 0b100;
- }
-}
-
-/// This structure manages ensuring that modification to StackingContextCollectionState is
-/// only temporary. It's useful for moving recursively down the flow tree and ensuring
-/// that the state is restored for siblings. To use this structure, we must call
-/// SavedStackingContextCollectionState::restore in order to restore the state.
-/// TODO(mrobinson): It would be nice to use RAII here to avoid having to call restore.
-pub struct SavedStackingContextCollectionState {
- stacking_context_id: StackingContextId,
- real_stacking_context_id: StackingContextId,
- parent_reference_frame_id: ClipScrollNodeIndex,
- clipping_and_scrolling: ClippingAndScrolling,
- containing_block_clipping_and_scrolling: ClippingAndScrolling,
- clips_pushed: usize,
- containing_block_clips_pushed: usize,
- stacking_relative_content_box: Rect<Au>,
-}
-
-impl SavedStackingContextCollectionState {
- fn new(state: &mut StackingContextCollectionState) -> SavedStackingContextCollectionState {
- SavedStackingContextCollectionState {
- stacking_context_id: state.current_stacking_context_id,
- real_stacking_context_id: state.current_real_stacking_context_id,
- parent_reference_frame_id: state.current_parent_reference_frame_id,
- clipping_and_scrolling: state.current_clipping_and_scrolling,
- containing_block_clipping_and_scrolling: state.containing_block_clipping_and_scrolling,
- clips_pushed: 0,
- containing_block_clips_pushed: 0,
- stacking_relative_content_box: state.parent_stacking_relative_content_box,
- }
- }
-
- fn switch_to_containing_block_clip(&mut self, state: &mut StackingContextCollectionState) {
- let clip = state
- .containing_block_clip_stack
- .last()
- .cloned()
- .unwrap_or_else(MaxRect::max_rect);
- state.clip_stack.push(clip);
- self.clips_pushed += 1;
- }
-
- fn restore(self, state: &mut StackingContextCollectionState) {
- state.current_stacking_context_id = self.stacking_context_id;
- state.current_real_stacking_context_id = self.real_stacking_context_id;
- state.current_parent_reference_frame_id = self.parent_reference_frame_id;
- state.current_clipping_and_scrolling = self.clipping_and_scrolling;
- state.containing_block_clipping_and_scrolling =
- self.containing_block_clipping_and_scrolling;
- state.parent_stacking_relative_content_box = self.stacking_relative_content_box;
-
- let truncate_length = state.clip_stack.len() - self.clips_pushed;
- state.clip_stack.truncate(truncate_length);
-
- let truncate_length =
- state.containing_block_clip_stack.len() - self.containing_block_clips_pushed;
- state.containing_block_clip_stack.truncate(truncate_length);
- }
-
- fn push_clip(
- &mut self,
- state: &mut StackingContextCollectionState,
- mut clip: Rect<Au>,
- positioning: StylePosition,
- ) {
- if positioning != StylePosition::Fixed {
- if let Some(old_clip) = state.clip_stack.last() {
- clip = old_clip.intersection(&clip).unwrap_or_else(Rect::zero);
- }
- }
-
- state.clip_stack.push(clip);
- self.clips_pushed += 1;
-
- if StylePosition::Absolute == positioning {
- state.containing_block_clip_stack.push(clip);
- self.containing_block_clips_pushed += 1;
- }
- }
-}
-
-impl BlockFlow {
- fn transform_clip_to_coordinate_space(
- &mut self,
- state: &mut StackingContextCollectionState,
- preserved_state: &mut SavedStackingContextCollectionState,
- ) {
- if state.clip_stack.is_empty() {
- return;
- }
- let border_box = self.stacking_relative_border_box(CoordinateSystem::Parent);
- let transform = match self.fragment.transform_matrix(&border_box) {
- Some(transform) => transform,
- None => return,
- };
-
- let perspective = self
- .fragment
- .perspective_matrix(&border_box)
- .unwrap_or(LayoutTransform::identity());
- let transform = transform.pre_mul(&perspective).inverse();
-
- let origin = border_box.origin;
- let transform_clip = |clip: Rect<Au>| {
- if clip == Rect::max_rect() {
- return clip;
- }
-
- match transform {
- Some(transform) if transform.m13 != 0.0 || transform.m23 != 0.0 => {
- // We cannot properly handle perspective transforms, because there may be a
- // situation where an element is transformed from outside the clip into the
- // clip region. Here we don't have enough information to detect when that is
- // happening. For the moment we just punt on trying to optimize the display
- // list for those cases.
- Rect::max_rect()
- },
- Some(transform) => {
- let clip = rect(
- (clip.origin.x - origin.x).to_f32_px(),
- (clip.origin.y - origin.y).to_f32_px(),
- clip.size.width.to_f32_px(),
- clip.size.height.to_f32_px(),
- );
-
- let clip = transform.transform_rect(&clip).unwrap();
-
- rect(
- Au::from_f32_px(clip.origin.x),
- Au::from_f32_px(clip.origin.y),
- Au::from_f32_px(clip.size.width),
- Au::from_f32_px(clip.size.height),
- )
- },
- None => Rect::zero(),
- }
- };
-
- if let Some(clip) = state.clip_stack.last().cloned() {
- state.clip_stack.push(transform_clip(clip));
- preserved_state.clips_pushed += 1;
- }
-
- if let Some(clip) = state.containing_block_clip_stack.last().cloned() {
- state.containing_block_clip_stack.push(transform_clip(clip));
- preserved_state.containing_block_clips_pushed += 1;
- }
- }
-
- /// Returns true if this fragment may establish a reference frame and this block
- /// creates a stacking context. Both are necessary in order to establish a reference
- /// frame.
- fn is_reference_frame(&self, context_type: Option<StackingContextType>) -> bool {
- match context_type {
- Some(StackingContextType::Real) => self.fragment.can_establish_reference_frame(),
- _ => false,
- }
- }
-
- pub fn collect_stacking_contexts_for_block(
- &mut self,
- state: &mut StackingContextCollectionState,
- flags: StackingContextCollectionFlags,
- ) {
- let mut preserved_state = SavedStackingContextCollectionState::new(state);
-
- let stacking_context_type = self.stacking_context_type(flags);
- self.base.stacking_context_id = match stacking_context_type {
- None => state.current_stacking_context_id,
- Some(sc_type) => state.allocate_stacking_context_info(sc_type),
- };
- state.current_stacking_context_id = self.base.stacking_context_id;
-
- if stacking_context_type == Some(StackingContextType::Real) {
- state.current_real_stacking_context_id = self.base.stacking_context_id;
- }
-
- let established_reference_frame = if self.is_reference_frame(stacking_context_type) {
- // WebRender currently creates reference frames automatically, so just add
- // a placeholder node to allocate a ClipScrollNodeIndex for this reference frame.
- Some(state.add_clip_scroll_node(ClipScrollNode::placeholder()))
- } else {
- None
- };
-
- // We are getting the id of the scroll root that contains us here, not the id of
- // any scroll root that we create. If we create a scroll root, its index will be
- // stored in state.current_clipping_and_scrolling. If we create a stacking context,
- // we don't want it to be contained by its own scroll root.
- let containing_clipping_and_scrolling = self.setup_clipping_for_block(
- state,
- &mut preserved_state,
- stacking_context_type,
- established_reference_frame,
- flags,
- );
-
- let creates_containing_block = !flags
- .contains(StackingContextCollectionFlags::POSITION_NEVER_CREATES_CONTAINING_BLOCK);
- let abspos_containing_block = established_reference_frame.is_some() ||
- (creates_containing_block && self.positioning() != StylePosition::Static);
- if abspos_containing_block {
- state.containing_block_clipping_and_scrolling = state.current_clipping_and_scrolling;
- }
-
- match stacking_context_type {
- None => self.base.collect_stacking_contexts_for_children(state),
- Some(StackingContextType::Real) => {
- self.create_real_stacking_context_for_block(
- preserved_state.stacking_context_id,
- containing_clipping_and_scrolling,
- established_reference_frame,
- state,
- );
- },
- Some(stacking_context_type) => {
- self.create_pseudo_stacking_context_for_block(
- stacking_context_type,
- preserved_state.stacking_context_id,
- containing_clipping_and_scrolling,
- state,
- );
- },
- }
-
- preserved_state.restore(state);
- }
-
- fn setup_clipping_for_block(
- &mut self,
- state: &mut StackingContextCollectionState,
- preserved_state: &mut SavedStackingContextCollectionState,
- stacking_context_type: Option<StackingContextType>,
- established_reference_frame: Option<ClipScrollNodeIndex>,
- flags: StackingContextCollectionFlags,
- ) -> ClippingAndScrolling {
- // If this block is absolutely positioned, we should be clipped and positioned by
- // the scroll root of our nearest ancestor that establishes a containing block.
- let containing_clipping_and_scrolling = match self.positioning() {
- StylePosition::Absolute => {
- preserved_state.switch_to_containing_block_clip(state);
- state.current_clipping_and_scrolling =
- state.containing_block_clipping_and_scrolling;
- state.containing_block_clipping_and_scrolling
- },
- StylePosition::Fixed => {
- // If we are a fixed positioned stacking context, we want to be scrolled by
- // our reference frame instead of the clip scroll node that we are inside.
- preserved_state.push_clip(state, Rect::max_rect(), StylePosition::Fixed);
- state.current_clipping_and_scrolling.scrolling =
- state.current_parent_reference_frame_id;
- state.current_clipping_and_scrolling
- },
- _ => state.current_clipping_and_scrolling,
- };
- self.base.clipping_and_scrolling = Some(containing_clipping_and_scrolling);
-
- if let Some(reference_frame_index) = established_reference_frame {
- let clipping_and_scrolling = ClippingAndScrolling::simple(reference_frame_index);
- state.current_clipping_and_scrolling = clipping_and_scrolling;
- self.base.clipping_and_scrolling = Some(clipping_and_scrolling);
- }
-
- let stacking_relative_border_box = if self.fragment.establishes_stacking_context() {
- self.stacking_relative_border_box(CoordinateSystem::Own)
- } else {
- self.stacking_relative_border_box(CoordinateSystem::Parent)
- };
-
- if stacking_context_type == Some(StackingContextType::Real) {
- self.transform_clip_to_coordinate_space(state, preserved_state);
- }
-
- if !flags.contains(StackingContextCollectionFlags::NEVER_CREATES_CLIP_SCROLL_NODE) {
- self.setup_clip_scroll_node_for_position(state, stacking_relative_border_box);
- self.setup_clip_scroll_node_for_overflow(state, stacking_relative_border_box);
- self.setup_clip_scroll_node_for_css_clip(
- state,
- preserved_state,
- stacking_relative_border_box,
- );
- }
- self.base.clip = state
- .clip_stack
- .last()
- .cloned()
- .unwrap_or_else(Rect::max_rect);
-
- // We keep track of our position so that any stickily positioned elements can
- // properly determine the extent of their movement relative to scrolling containers.
- if !flags.contains(StackingContextCollectionFlags::POSITION_NEVER_CREATES_CONTAINING_BLOCK)
- {
- let border_box = if self.fragment.establishes_stacking_context() {
- stacking_relative_border_box
- } else {
- self.stacking_relative_border_box(CoordinateSystem::Own)
- };
- state.parent_stacking_relative_content_box =
- self.fragment.stacking_relative_content_box(border_box)
- }
-
- match self.positioning() {
- StylePosition::Absolute | StylePosition::Relative | StylePosition::Fixed => {
- state.containing_block_clipping_and_scrolling = state.current_clipping_and_scrolling
- },
- _ => {},
- }
-
- containing_clipping_and_scrolling
- }
-
- fn setup_clip_scroll_node_for_position(
- &mut self,
- state: &mut StackingContextCollectionState,
- border_box: Rect<Au>,
- ) {
- if self.positioning() != StylePosition::Sticky {
- return;
- }
-
- let sticky_position = self.sticky_position();
- if sticky_position.left == MaybeAuto::Auto &&
- sticky_position.right == MaybeAuto::Auto &&
- sticky_position.top == MaybeAuto::Auto &&
- sticky_position.bottom == MaybeAuto::Auto
- {
- return;
- }
-
- // Since position: sticky elements always establish a stacking context, we will
- // have previously calculated our border box in our own coordinate system. In
- // order to properly calculate max offsets we need to compare our size and
- // position in our parent's coordinate system.
- let border_box_in_parent = self.stacking_relative_border_box(CoordinateSystem::Parent);
- let margins = self.fragment.margin.to_physical(
- self.base
- .early_absolute_position_info
- .relative_containing_block_mode,
- );
-
- // Position:sticky elements are always restricted based on the size and position of
- // their containing block, which for sticky items is like relative and statically
- // positioned items: just the parent block.
- let constraint_rect = state.parent_stacking_relative_content_box;
-
- let to_offset_bound = |constraint_edge: Au, moving_edge: Au| -> f32 {
- (constraint_edge - moving_edge).to_f32_px()
- };
-
- // This is the minimum negative offset and then the maximum positive offset. We just
- // specify every edge, but if the corresponding margin is None, that offset has no effect.
- let vertical_offset_bounds = StickyOffsetBounds::new(
- to_offset_bound(
- constraint_rect.min_y(),
- border_box_in_parent.min_y() - margins.top,
- ),
- to_offset_bound(constraint_rect.max_y(), border_box_in_parent.max_y()),
- );
- let horizontal_offset_bounds = StickyOffsetBounds::new(
- to_offset_bound(
- constraint_rect.min_x(),
- border_box_in_parent.min_x() - margins.left,
- ),
- to_offset_bound(constraint_rect.max_x(), border_box_in_parent.max_x()),
- );
-
- // The margins control which edges have sticky behavior.
- let sticky_frame_data = StickyFrameData {
- margins: SideOffsets2D::new(
- sticky_position.top.to_option().map(|v| v.to_f32_px()),
- sticky_position.right.to_option().map(|v| v.to_f32_px()),
- sticky_position.bottom.to_option().map(|v| v.to_f32_px()),
- sticky_position.left.to_option().map(|v| v.to_f32_px()),
- ),
- vertical_offset_bounds,
- horizontal_offset_bounds,
- };
-
- let new_clip_scroll_index = state.add_clip_scroll_node(ClipScrollNode {
- parent_index: self.clipping_and_scrolling().scrolling,
- clip: ClippingRegion::from_rect(border_box.to_layout()),
- content_rect: LayoutRect::zero(),
- node_type: ClipScrollNodeType::StickyFrame(sticky_frame_data),
- });
-
- let new_clipping_and_scrolling = ClippingAndScrolling::simple(new_clip_scroll_index);
- self.base.clipping_and_scrolling = Some(new_clipping_and_scrolling);
- state.current_clipping_and_scrolling = new_clipping_and_scrolling;
- }
-
- fn setup_clip_scroll_node_for_overflow(
- &mut self,
- state: &mut StackingContextCollectionState,
- border_box: Rect<Au>,
- ) {
- if !self.overflow_style_may_require_clip_scroll_node() {
- return;
- }
-
- let content_box = 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 > content_box.size.width ||
- self.base.overflow.scroll.size.height > content_box.size.height ||
- StyleOverflow::Hidden == self.fragment.style.get_box().overflow_x ||
- StyleOverflow::Hidden == self.fragment.style.get_box().overflow_y;
-
- self.mark_scrolling_overflow(has_scrolling_overflow);
- if !has_scrolling_overflow {
- return;
- }
-
- let sensitivity = if StyleOverflow::Hidden == self.fragment.style.get_box().overflow_x &&
- StyleOverflow::Hidden == self.fragment.style.get_box().overflow_y
- {
- ScrollSensitivity::Script
- } else {
- ScrollSensitivity::ScriptAndInputEvents
- };
-
- let border_widths = self
- .fragment
- .style
- .logical_border_width()
- .to_physical(self.fragment.style.writing_mode);
- let clip_rect = border_box.inner_rect(border_widths);
-
- let mut clip = ClippingRegion::from_rect(clip_rect.to_layout());
- let radii = build_border_radius_for_inner_rect(border_box, &self.fragment.style);
- if !radii.is_zero() {
- clip.intersect_with_rounded_rect(clip_rect.to_layout(), radii)
- }
-
- let content_size = self.base.overflow.scroll.origin + self.base.overflow.scroll.size;
- let content_size = Size2D::new(content_size.x, content_size.y);
-
- let external_id =
- ExternalScrollId(self.fragment.unique_id(), state.pipeline_id.to_webrender());
- let new_clip_scroll_index = state.add_clip_scroll_node(ClipScrollNode {
- parent_index: self.clipping_and_scrolling().scrolling,
- clip: clip,
- content_rect: Rect::new(content_box.origin, content_size).to_layout(),
- node_type: ClipScrollNodeType::ScrollFrame(sensitivity, external_id),
- });
-
- let new_clipping_and_scrolling = ClippingAndScrolling::simple(new_clip_scroll_index);
- self.base.clipping_and_scrolling = Some(new_clipping_and_scrolling);
- state.current_clipping_and_scrolling = new_clipping_and_scrolling;
- }
-
- /// Adds a scroll root for a block to take the `clip` property into account
- /// per CSS 2.1 § 11.1.2.
- fn setup_clip_scroll_node_for_css_clip(
- &mut self,
- state: &mut StackingContextCollectionState,
- preserved_state: &mut SavedStackingContextCollectionState,
- stacking_relative_border_box: Rect<Au>,
- ) {
- // Account for `clip` per CSS 2.1 § 11.1.2.
- let style_clip_rect = match self.fragment.style().get_effects().clip {
- Either::First(style_clip_rect) => style_clip_rect,
- _ => return,
- };
-
- // CSS `clip` should only apply to position:absolute or positione:fixed elements.
- // CSS Masking Appendix A: "Applies to: Absolutely positioned elements."
- match self.positioning() {
- StylePosition::Absolute | StylePosition::Fixed => {},
- _ => return,
- }
-
- fn extract_clip_component(p: &LengthOrAuto) -> Option<Au> {
- match *p {
- LengthOrAuto::Auto => None,
- LengthOrAuto::LengthPercentage(ref length) => Some(Au::from(*length)),
- }
- }
-
- let clip_origin = Point2D::new(
- stacking_relative_border_box.origin.x +
- extract_clip_component(&style_clip_rect.left).unwrap_or_default(),
- stacking_relative_border_box.origin.y +
- extract_clip_component(&style_clip_rect.top).unwrap_or_default(),
- );
- let right = extract_clip_component(&style_clip_rect.right)
- .unwrap_or(stacking_relative_border_box.size.width);
- let bottom = extract_clip_component(&style_clip_rect.bottom)
- .unwrap_or(stacking_relative_border_box.size.height);
- let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y);
-
- let clip_rect = Rect::new(clip_origin, clip_size);
- preserved_state.push_clip(state, clip_rect, self.positioning());
-
- let new_index = state.add_clip_scroll_node(ClipScrollNode {
- parent_index: self.clipping_and_scrolling().scrolling,
- clip: ClippingRegion::from_rect(clip_rect.to_layout()),
- content_rect: LayoutRect::zero(), // content_rect isn't important for clips.
- node_type: ClipScrollNodeType::Clip,
- });
-
- let new_indices = ClippingAndScrolling::new(new_index, new_index);
- self.base.clipping_and_scrolling = Some(new_indices);
- state.current_clipping_and_scrolling = new_indices;
- }
-
- fn create_pseudo_stacking_context_for_block(
- &mut self,
- stacking_context_type: StackingContextType,
- parent_stacking_context_id: StackingContextId,
- parent_clipping_and_scrolling: ClippingAndScrolling,
- state: &mut StackingContextCollectionState,
- ) {
- let new_context = self.fragment.create_stacking_context(
- self.base.stacking_context_id,
- &self.base,
- stacking_context_type,
- None,
- parent_clipping_and_scrolling,
- );
- state.add_stacking_context(parent_stacking_context_id, new_context);
-
- self.base.collect_stacking_contexts_for_children(state);
-
- let children = state
- .stacking_context_info
- .get_mut(&self.base.stacking_context_id)
- .map(|info| info.take_children());
- if let Some(children) = children {
- for child in children {
- if child.context_type == StackingContextType::PseudoFloat {
- state.add_stacking_context(self.base.stacking_context_id, child);
- } else {
- state.add_stacking_context(parent_stacking_context_id, child);
- }
- }
- }
- }
-
- fn create_real_stacking_context_for_block(
- &mut self,
- parent_stacking_context_id: StackingContextId,
- parent_clipping_and_scrolling: ClippingAndScrolling,
- established_reference_frame: Option<ClipScrollNodeIndex>,
- state: &mut StackingContextCollectionState,
- ) {
- let stacking_context = self.fragment.create_stacking_context(
- self.base.stacking_context_id,
- &self.base,
- StackingContextType::Real,
- established_reference_frame,
- parent_clipping_and_scrolling,
- );
-
- state.add_stacking_context(parent_stacking_context_id, stacking_context);
- self.base.collect_stacking_contexts_for_children(state);
- }
-
- pub fn build_display_list_for_block_no_damage(
- &self,
- state: &mut DisplayListBuildState,
- border_painting_mode: BorderPaintingMode,
- ) {
- let background_border_section = self.background_border_section();
-
- state.processing_scrolling_overflow_element = self.has_scrolling_overflow();
-
- let content_size = if state.processing_scrolling_overflow_element {
- let content_size = self.base.overflow.scroll.origin + self.base.overflow.scroll.size;
- Some(Size2D::new(content_size.x, content_size.y))
- } else {
- None
- };
-
- let stacking_relative_border_box = self
- .base
- .stacking_relative_border_box_for_display_list(&self.fragment);
- // Add the box that starts the block context.
- self.fragment.build_display_list_no_damage(
- state,
- stacking_relative_border_box,
- border_painting_mode,
- background_border_section,
- self.base.clip,
- content_size,
- );
-
- self.base
- .build_display_items_for_debugging_tint(state, self.fragment.node);
-
- state.processing_scrolling_overflow_element = false;
- }
-
- pub fn build_display_list_for_block(
- &mut self,
- state: &mut DisplayListBuildState,
- border_painting_mode: BorderPaintingMode,
- ) {
- self.fragment
- .restyle_damage
- .remove(ServoRestyleDamage::REPAINT);
- self.build_display_list_for_block_no_damage(state, border_painting_mode);
- }
-
- pub fn build_display_list_for_background_if_applicable_with_background(
- &self,
- state: &mut DisplayListBuildState,
- background: &style_structs::Background,
- background_color: RGBA,
- ) {
- let stacking_relative_border_box = self
- .base
- .stacking_relative_border_box_for_display_list(&self.fragment);
- let background_border_section = self.background_border_section();
-
- self.fragment
- .build_display_list_for_background_if_applicable_with_background(
- state,
- self.fragment.style(),
- background,
- background_color,
- background_border_section,
- stacking_relative_border_box,
- )
- }
-
- #[inline]
- fn stacking_context_type(
- &self,
- flags: StackingContextCollectionFlags,
- ) -> Option<StackingContextType> {
- if flags.contains(StackingContextCollectionFlags::NEVER_CREATES_STACKING_CONTEXT) {
- return None;
- }
-
- if self.fragment.establishes_stacking_context() {
- return Some(StackingContextType::Real);
- }
-
- if self
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- return Some(StackingContextType::PseudoPositioned);
- }
-
- if self.fragment.style.get_box().position != StylePosition::Static {
- return Some(StackingContextType::PseudoPositioned);
- }
-
- if self.base.flags.is_float() {
- return Some(StackingContextType::PseudoFloat);
- }
-
- None
- }
-}
-
-impl BaseFlow {
- pub fn build_display_items_for_debugging_tint(
- &self,
- state: &mut DisplayListBuildState,
- node: OpaqueNode,
- ) {
- if !opts::get().show_debug_parallel_layout {
- return;
- }
-
- let thread_id = self.thread_id;
- let stacking_context_relative_bounds = Rect::new(
- self.stacking_relative_position.to_point(),
- self.position.size.to_physical(self.writing_mode),
- );
-
- let mut color = THREAD_TINT_COLORS[thread_id as usize % THREAD_TINT_COLORS.len()];
- color.a = 1.0;
- let base =
- state.create_base_display_item(self.clip, node, None, DisplayListSection::Content);
- let bounds = stacking_context_relative_bounds.inflate(Au::from_px(2), Au::from_px(2));
- state.add_display_item(DisplayItem::Border(CommonDisplayItem::with_data(
- base,
- webrender_api::BorderDisplayItem {
- bounds: bounds.to_layout(),
- common: items::empty_common_item_properties(),
- widths: SideOffsets2D::new_all_same(Au::from_px(2)).to_layout(),
- details: BorderDetails::Normal(border::simple(
- color,
- webrender_api::BorderStyle::Solid,
- )),
- },
- Vec::new(),
- )));
- }
-}
-
-/// Gets the cursor to use given the specific ComputedValues. `default_cursor` specifies
-/// the cursor to use if `cursor` is `auto`. Typically, this will be `PointerCursor`, but for
-/// text display items it may be `TextCursor` or `VerticalTextCursor`.
-#[inline]
-fn get_cursor(values: &ComputedValues, default_cursor: Cursor) -> Option<Cursor> {
- let inherited_ui = values.get_inherited_ui();
- if inherited_ui.pointer_events == PointerEvents::None {
- return None;
- }
-
- Some(match inherited_ui.cursor.keyword {
- CursorKind::Auto => default_cursor,
- CursorKind::None => Cursor::None,
- CursorKind::Default => Cursor::Default,
- CursorKind::Pointer => Cursor::Pointer,
- CursorKind::ContextMenu => Cursor::ContextMenu,
- CursorKind::Help => Cursor::Help,
- CursorKind::Progress => Cursor::Progress,
- CursorKind::Wait => Cursor::Wait,
- CursorKind::Cell => Cursor::Cell,
- CursorKind::Crosshair => Cursor::Crosshair,
- CursorKind::Text => Cursor::Text,
- CursorKind::VerticalText => Cursor::VerticalText,
- CursorKind::Alias => Cursor::Alias,
- CursorKind::Copy => Cursor::Copy,
- CursorKind::Move => Cursor::Move,
- CursorKind::NoDrop => Cursor::NoDrop,
- CursorKind::NotAllowed => Cursor::NotAllowed,
- CursorKind::Grab => Cursor::Grab,
- CursorKind::Grabbing => Cursor::Grabbing,
- CursorKind::EResize => Cursor::EResize,
- CursorKind::NResize => Cursor::NResize,
- CursorKind::NeResize => Cursor::NeResize,
- CursorKind::NwResize => Cursor::NwResize,
- CursorKind::SResize => Cursor::SResize,
- CursorKind::SeResize => Cursor::SeResize,
- CursorKind::SwResize => Cursor::SwResize,
- CursorKind::WResize => Cursor::WResize,
- CursorKind::EwResize => Cursor::EwResize,
- CursorKind::NsResize => Cursor::NsResize,
- CursorKind::NeswResize => Cursor::NeswResize,
- CursorKind::NwseResize => Cursor::NwseResize,
- CursorKind::ColResize => Cursor::ColResize,
- CursorKind::RowResize => Cursor::RowResize,
- CursorKind::AllScroll => Cursor::AllScroll,
- CursorKind::ZoomIn => Cursor::ZoomIn,
- CursorKind::ZoomOut => Cursor::ZoomOut,
- })
-}
-
-/// Adjusts borders as appropriate to account for a fragment's status as the
-/// first or last fragment within the range of an element.
-///
-/// Specifically, this function sets border widths to zero on the sides for
-/// which the fragment is not outermost.
-fn modify_border_width_for_inline_sides(
- border_width: &mut LogicalMargin<Au>,
- inline_border_info: InlineNodeBorderInfo,
-) {
- if !inline_border_info.is_first_fragment_of_element {
- border_width.inline_start = Au(0);
- }
-
- if !inline_border_info.is_last_fragment_of_element {
- border_width.inline_end = Au(0);
- }
-}
-
-/// Describes how to paint the borders.
-#[derive(Clone, Copy)]
-pub enum BorderPaintingMode<'a> {
- /// Paint borders separately (`border-collapse: separate`).
- Separate,
- /// Paint collapsed borders.
- Collapse(&'a CollapsedBordersForCell),
- /// Paint no borders.
- Hidden,
-}
-
-fn convert_text_run_to_glyphs(
- text_run: Arc<TextRun>,
- range: Range<ByteIndex>,
- mut origin: Point2D<Au>,
-) -> Vec<GlyphInstance> {
- let mut glyphs = vec![];
-
- for slice in text_run.natural_word_slices_in_visual_order(&range) {
- for glyph in slice.glyphs.iter_glyphs_for_byte_range(&slice.range) {
- let glyph_advance = if glyph.char_is_space() {
- glyph.advance() + text_run.extra_word_spacing
- } else {
- glyph.advance()
- };
- if !slice.glyphs.is_whitespace() {
- let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
- let point = origin + glyph_offset.to_vector();
- let glyph = GlyphInstance {
- index: glyph.id(),
- point: point.to_layout(),
- };
- glyphs.push(glyph);
- }
- origin.x += glyph_advance;
- }
- }
- return glyphs;
-}
pub struct IndexableTextItem {
/// The placement of the text item on the plane.
@@ -2988,11 +34,6 @@ pub struct IndexableText {
}
impl IndexableText {
- fn insert(&mut self, node: OpaqueNode, item: IndexableTextItem) {
- let entries = self.inner.entry(node).or_insert(Vec::new());
- entries.push(item);
- }
-
pub fn get(&self, node: OpaqueNode) -> Option<&[IndexableTextItem]> {
self.inner.get(&node).map(|x| x.as_slice())
}
@@ -3010,15 +51,3 @@ impl IndexableText {
)
}
}
-
-trait ToF32Px {
- type Output;
- fn to_f32_px(&self) -> Self::Output;
-}
-
-impl ToF32Px for TypedRect<Au> {
- type Output = LayoutRect;
- fn to_f32_px(&self) -> LayoutRect {
- LayoutRect::from_untyped(&servo_geometry::au_rect_to_f32_rect(*self))
- }
-}
diff --git a/components/layout_2020/display_list/conversions.rs b/components/layout_2020/display_list/conversions.rs
deleted file mode 100644
index 4624b5f8dd0..00000000000
--- a/components/layout_2020/display_list/conversions.rs
+++ /dev/null
@@ -1,167 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use app_units::Au;
-use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Vector2D};
-use style::computed_values::image_rendering::T as ImageRendering;
-use style::computed_values::mix_blend_mode::T as MixBlendMode;
-use style::computed_values::transform_style::T as TransformStyle;
-use style::values::computed::{BorderStyle, Filter};
-use style::values::specified::border::BorderImageRepeatKeyword;
-use style::values::RGBA;
-use webrender_api as wr;
-
-pub trait ToLayout {
- type Type;
- fn to_layout(&self) -> Self::Type;
-}
-
-impl ToLayout for BorderStyle {
- type Type = wr::BorderStyle;
- fn to_layout(&self) -> Self::Type {
- match *self {
- BorderStyle::None => wr::BorderStyle::None,
- BorderStyle::Solid => wr::BorderStyle::Solid,
- BorderStyle::Double => wr::BorderStyle::Double,
- BorderStyle::Dotted => wr::BorderStyle::Dotted,
- BorderStyle::Dashed => wr::BorderStyle::Dashed,
- BorderStyle::Hidden => wr::BorderStyle::Hidden,
- BorderStyle::Groove => wr::BorderStyle::Groove,
- BorderStyle::Ridge => wr::BorderStyle::Ridge,
- BorderStyle::Inset => wr::BorderStyle::Inset,
- BorderStyle::Outset => wr::BorderStyle::Outset,
- }
- }
-}
-
-impl ToLayout for Filter {
- type Type = wr::FilterOp;
- fn to_layout(&self) -> Self::Type {
- match *self {
- Filter::Blur(radius) => wr::FilterOp::Blur(radius.px()),
- Filter::Brightness(amount) => wr::FilterOp::Brightness(amount.0),
- Filter::Contrast(amount) => wr::FilterOp::Contrast(amount.0),
- Filter::Grayscale(amount) => wr::FilterOp::Grayscale(amount.0),
- Filter::HueRotate(angle) => wr::FilterOp::HueRotate(angle.radians()),
- Filter::Invert(amount) => wr::FilterOp::Invert(amount.0),
- Filter::Opacity(amount) => wr::FilterOp::Opacity(amount.0.into(), amount.0),
- Filter::Saturate(amount) => wr::FilterOp::Saturate(amount.0),
- Filter::Sepia(amount) => wr::FilterOp::Sepia(amount.0),
- // Statically check that DropShadow is impossible.
- Filter::DropShadow(ref shadow) => match *shadow {},
- // Statically check that Url is impossible.
- Filter::Url(ref url) => match *url {},
- }
- }
-}
-
-impl ToLayout for ImageRendering {
- type Type = wr::ImageRendering;
- fn to_layout(&self) -> Self::Type {
- match *self {
- ImageRendering::Auto => wr::ImageRendering::Auto,
- ImageRendering::CrispEdges => wr::ImageRendering::CrispEdges,
- ImageRendering::Pixelated => wr::ImageRendering::Pixelated,
- }
- }
-}
-
-impl ToLayout for MixBlendMode {
- type Type = wr::MixBlendMode;
- fn to_layout(&self) -> Self::Type {
- match *self {
- MixBlendMode::Normal => wr::MixBlendMode::Normal,
- MixBlendMode::Multiply => wr::MixBlendMode::Multiply,
- MixBlendMode::Screen => wr::MixBlendMode::Screen,
- MixBlendMode::Overlay => wr::MixBlendMode::Overlay,
- MixBlendMode::Darken => wr::MixBlendMode::Darken,
- MixBlendMode::Lighten => wr::MixBlendMode::Lighten,
- MixBlendMode::ColorDodge => wr::MixBlendMode::ColorDodge,
- MixBlendMode::ColorBurn => wr::MixBlendMode::ColorBurn,
- MixBlendMode::HardLight => wr::MixBlendMode::HardLight,
- MixBlendMode::SoftLight => wr::MixBlendMode::SoftLight,
- MixBlendMode::Difference => wr::MixBlendMode::Difference,
- MixBlendMode::Exclusion => wr::MixBlendMode::Exclusion,
- MixBlendMode::Hue => wr::MixBlendMode::Hue,
- MixBlendMode::Saturation => wr::MixBlendMode::Saturation,
- MixBlendMode::Color => wr::MixBlendMode::Color,
- MixBlendMode::Luminosity => wr::MixBlendMode::Luminosity,
- }
- }
-}
-
-impl ToLayout for TransformStyle {
- type Type = wr::TransformStyle;
- fn to_layout(&self) -> Self::Type {
- match *self {
- TransformStyle::Auto | TransformStyle::Flat => wr::TransformStyle::Flat,
- TransformStyle::Preserve3d => wr::TransformStyle::Preserve3D,
- }
- }
-}
-
-impl ToLayout for RGBA {
- type Type = wr::ColorF;
- fn to_layout(&self) -> Self::Type {
- wr::ColorF::new(
- self.red_f32(),
- self.green_f32(),
- self.blue_f32(),
- self.alpha_f32(),
- )
- }
-}
-
-impl ToLayout for Point2D<Au> {
- type Type = wr::units::LayoutPoint;
- fn to_layout(&self) -> Self::Type {
- wr::units::LayoutPoint::new(self.x.to_f32_px(), self.y.to_f32_px())
- }
-}
-
-impl ToLayout for Rect<Au> {
- type Type = wr::units::LayoutRect;
- fn to_layout(&self) -> Self::Type {
- wr::units::LayoutRect::new(self.origin.to_layout(), self.size.to_layout())
- }
-}
-
-impl ToLayout for SideOffsets2D<Au> {
- type Type = wr::units::LayoutSideOffsets;
- fn to_layout(&self) -> Self::Type {
- wr::units::LayoutSideOffsets::new(
- self.top.to_f32_px(),
- self.right.to_f32_px(),
- self.bottom.to_f32_px(),
- self.left.to_f32_px(),
- )
- }
-}
-
-impl ToLayout for Size2D<Au> {
- type Type = wr::units::LayoutSize;
- fn to_layout(&self) -> Self::Type {
- wr::units::LayoutSize::new(self.width.to_f32_px(), self.height.to_f32_px())
- }
-}
-
-impl ToLayout for Vector2D<Au> {
- type Type = wr::units::LayoutVector2D;
- fn to_layout(&self) -> Self::Type {
- wr::units::LayoutVector2D::new(self.x.to_f32_px(), self.y.to_f32_px())
- }
-}
-
-impl ToLayout for BorderImageRepeatKeyword {
- type Type = wr::RepeatMode;
-
- fn to_layout(&self) -> Self::Type {
- match *self {
- BorderImageRepeatKeyword::Stretch => wr::RepeatMode::Stretch,
- BorderImageRepeatKeyword::Repeat => wr::RepeatMode::Repeat,
- BorderImageRepeatKeyword::Round => wr::RepeatMode::Round,
- BorderImageRepeatKeyword::Space => wr::RepeatMode::Space,
- }
- }
-}
diff --git a/components/layout_2020/display_list/gradient.rs b/components/layout_2020/display_list/gradient.rs
deleted file mode 100644
index ab56b1d6c9c..00000000000
--- a/components/layout_2020/display_list/gradient.rs
+++ /dev/null
@@ -1,323 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::display_list::ToLayout;
-use app_units::Au;
-use euclid::{Point2D, Size2D, Vector2D};
-use style::properties::ComputedValues;
-use style::values::computed::image::{EndingShape, LineDirection};
-use style::values::computed::{Angle, GradientItem, LengthPercentage, Percentage, Position};
-use style::values::generics::image::{Circle, ColorStop, Ellipse, ShapeExtent};
-use webrender_api::{ExtendMode, Gradient, GradientBuilder, GradientStop, RadialGradient};
-
-/// A helper data structure for gradients.
-#[derive(Clone, Copy)]
-struct StopRun {
- start_offset: f32,
- end_offset: f32,
- start_index: usize,
- stop_count: usize,
-}
-
-/// Determines the radius of a circle if it was not explictly provided.
-/// <https://drafts.csswg.org/css-images-3/#typedef-size>
-fn circle_size_keyword(
- keyword: ShapeExtent,
- size: &Size2D<Au>,
- center: &Point2D<Au>,
-) -> Size2D<Au> {
- let radius = match keyword {
- ShapeExtent::ClosestSide | ShapeExtent::Contain => {
- let dist = distance_to_sides(size, center, ::std::cmp::min);
- ::std::cmp::min(dist.width, dist.height)
- },
- ShapeExtent::FarthestSide => {
- let dist = distance_to_sides(size, center, ::std::cmp::max);
- ::std::cmp::max(dist.width, dist.height)
- },
- ShapeExtent::ClosestCorner => distance_to_corner(size, center, ::std::cmp::min),
- ShapeExtent::FarthestCorner | ShapeExtent::Cover => {
- distance_to_corner(size, center, ::std::cmp::max)
- },
- };
- Size2D::new(radius, radius)
-}
-
-/// Returns the radius for an ellipse with the same ratio as if it was matched to the sides.
-fn ellipse_radius<F>(size: &Size2D<Au>, center: &Point2D<Au>, cmp: F) -> Size2D<Au>
-where
- F: Fn(Au, Au) -> Au,
-{
- let dist = distance_to_sides(size, center, cmp);
- Size2D::new(
- dist.width.scale_by(::std::f32::consts::FRAC_1_SQRT_2 * 2.0),
- dist.height
- .scale_by(::std::f32::consts::FRAC_1_SQRT_2 * 2.0),
- )
-}
-
-/// Determines the radius of an ellipse if it was not explictly provided.
-/// <https://drafts.csswg.org/css-images-3/#typedef-size>
-fn ellipse_size_keyword(
- keyword: ShapeExtent,
- size: &Size2D<Au>,
- center: &Point2D<Au>,
-) -> Size2D<Au> {
- match keyword {
- ShapeExtent::ClosestSide | ShapeExtent::Contain => {
- distance_to_sides(size, center, ::std::cmp::min)
- },
- ShapeExtent::FarthestSide => distance_to_sides(size, center, ::std::cmp::max),
- ShapeExtent::ClosestCorner => ellipse_radius(size, center, ::std::cmp::min),
- ShapeExtent::FarthestCorner | ShapeExtent::Cover => {
- ellipse_radius(size, center, ::std::cmp::max)
- },
- }
-}
-
-fn convert_gradient_stops(
- style: &ComputedValues,
- gradient_items: &[GradientItem],
- total_length: Au,
-) -> GradientBuilder {
- // Determine the position of each stop per CSS-IMAGES § 3.4.
-
- // Only keep the color stops, discard the color interpolation hints.
- let mut stop_items = gradient_items
- .iter()
- .filter_map(|item| match *item {
- GradientItem::SimpleColorStop(color) => Some(ColorStop {
- color,
- position: None,
- }),
- GradientItem::ComplexColorStop { color, position } => Some(ColorStop {
- color,
- position: Some(position),
- }),
- _ => None,
- })
- .collect::<Vec<_>>();
-
- assert!(stop_items.len() >= 2);
-
- // Run the algorithm from
- // https://drafts.csswg.org/css-images-3/#color-stop-syntax
-
- // Step 1:
- // If the first color stop does not have a position, set its position to 0%.
- {
- let first = stop_items.first_mut().unwrap();
- if first.position.is_none() {
- first.position = Some(LengthPercentage::new_percent(Percentage(0.)));
- }
- }
- // If the last color stop does not have a position, set its position to 100%.
- {
- let last = stop_items.last_mut().unwrap();
- if last.position.is_none() {
- last.position = Some(LengthPercentage::new_percent(Percentage(1.0)));
- }
- }
-
- // Step 2: Move any stops placed before earlier stops to the
- // same position as the preceding stop.
- let mut last_stop_position = stop_items.first().unwrap().position.unwrap();
- for stop in stop_items.iter_mut().skip(1) {
- if let Some(pos) = stop.position {
- if position_to_offset(last_stop_position, total_length) >
- position_to_offset(pos, total_length)
- {
- stop.position = Some(last_stop_position);
- }
- last_stop_position = stop.position.unwrap();
- }
- }
-
- // Step 3: Evenly space stops without position.
- let mut stops = GradientBuilder::new();
- let mut stop_run = None;
- for (i, stop) in stop_items.iter().enumerate() {
- let offset = match stop.position {
- None => {
- if stop_run.is_none() {
- // Initialize a new stop run.
- // `unwrap()` here should never fail because this is the beginning of
- // a stop run, which is always bounded by a length or percentage.
- let start_offset =
- position_to_offset(stop_items[i - 1].position.unwrap(), total_length);
- // `unwrap()` here should never fail because this is the end of
- // a stop run, which is always bounded by a length or percentage.
- let (end_index, end_stop) = stop_items[(i + 1)..]
- .iter()
- .enumerate()
- .find(|&(_, ref stop)| stop.position.is_some())
- .unwrap();
- let end_offset = position_to_offset(end_stop.position.unwrap(), total_length);
- stop_run = Some(StopRun {
- start_offset,
- end_offset,
- start_index: i - 1,
- stop_count: end_index,
- })
- }
-
- let stop_run = stop_run.unwrap();
- let stop_run_length = stop_run.end_offset - stop_run.start_offset;
- stop_run.start_offset +
- stop_run_length * (i - stop_run.start_index) as f32 /
- ((2 + stop_run.stop_count) as f32)
- },
- Some(position) => {
- stop_run = None;
- position_to_offset(position, total_length)
- },
- };
- assert!(offset.is_finite());
- stops.push(GradientStop {
- offset: offset,
- color: style.resolve_color(stop.color).to_layout(),
- })
- }
- stops
-}
-
-fn extend_mode(repeating: bool) -> ExtendMode {
- if repeating {
- ExtendMode::Repeat
- } else {
- ExtendMode::Clamp
- }
-}
-/// Returns the the distance to the nearest or farthest corner depending on the comperator.
-fn distance_to_corner<F>(size: &Size2D<Au>, center: &Point2D<Au>, cmp: F) -> Au
-where
- F: Fn(Au, Au) -> Au,
-{
- let dist = distance_to_sides(size, center, cmp);
- Au::from_f32_px(dist.width.to_f32_px().hypot(dist.height.to_f32_px()))
-}
-
-/// Returns the distance to the nearest or farthest sides depending on the comparator.
-///
-/// The first return value is horizontal distance the second vertical distance.
-fn distance_to_sides<F>(size: &Size2D<Au>, center: &Point2D<Au>, cmp: F) -> Size2D<Au>
-where
- F: Fn(Au, Au) -> Au,
-{
- let top_side = center.y;
- let right_side = size.width - center.x;
- let bottom_side = size.height - center.y;
- let left_side = center.x;
- Size2D::new(cmp(left_side, right_side), cmp(top_side, bottom_side))
-}
-
-fn position_to_offset(position: LengthPercentage, total_length: Au) -> f32 {
- if total_length == Au(0) {
- return 0.0;
- }
- position.to_used_value(total_length).0 as f32 / total_length.0 as f32
-}
-
-pub fn linear(
- style: &ComputedValues,
- size: Size2D<Au>,
- stops: &[GradientItem],
- direction: LineDirection,
- repeating: bool,
-) -> (Gradient, Vec<GradientStop>) {
- use style::values::specified::position::HorizontalPositionKeyword::*;
- use style::values::specified::position::VerticalPositionKeyword::*;
- let angle = match direction {
- LineDirection::Angle(angle) => angle.radians(),
- LineDirection::Horizontal(x) => match x {
- Left => Angle::from_degrees(270.).radians(),
- Right => Angle::from_degrees(90.).radians(),
- },
- LineDirection::Vertical(y) => match y {
- Top => Angle::from_degrees(0.).radians(),
- Bottom => Angle::from_degrees(180.).radians(),
- },
- LineDirection::Corner(horizontal, vertical) => {
- // This the angle for one of the diagonals of the box. Our angle
- // will either be this one, this one + PI, or one of the other
- // two perpendicular angles.
- let atan = (size.height.to_f32_px() / size.width.to_f32_px()).atan();
- match (horizontal, vertical) {
- (Right, Bottom) => ::std::f32::consts::PI - atan,
- (Left, Bottom) => ::std::f32::consts::PI + atan,
- (Right, Top) => atan,
- (Left, Top) => -atan,
- }
- },
- };
-
- // Get correct gradient line length, based on:
- // https://drafts.csswg.org/css-images-3/#linear-gradients
- let dir = Point2D::new(angle.sin(), -angle.cos());
-
- let line_length =
- (dir.x * size.width.to_f32_px()).abs() + (dir.y * size.height.to_f32_px()).abs();
-
- let inv_dir_length = 1.0 / (dir.x * dir.x + dir.y * dir.y).sqrt();
-
- // This is the vector between the center and the ending point; i.e. half
- // of the distance between the starting point and the ending point.
- let delta = Vector2D::new(
- Au::from_f32_px(dir.x * inv_dir_length * line_length / 2.0),
- Au::from_f32_px(dir.y * inv_dir_length * line_length / 2.0),
- );
-
- // This is the length of the gradient line.
- let length = Au::from_f32_px((delta.x.to_f32_px() * 2.0).hypot(delta.y.to_f32_px() * 2.0));
-
- let mut builder = convert_gradient_stops(style, stops, length);
-
- let center = Point2D::new(size.width / 2, size.height / 2);
-
- (
- builder.gradient(
- (center - delta).to_layout(),
- (center + delta).to_layout(),
- extend_mode(repeating),
- ),
- builder.into_stops(),
- )
-}
-
-pub fn radial(
- style: &ComputedValues,
- size: Size2D<Au>,
- stops: &[GradientItem],
- shape: EndingShape,
- center: Position,
- repeating: bool,
-) -> (RadialGradient, Vec<GradientStop>) {
- let center = Point2D::new(
- center.horizontal.to_used_value(size.width),
- center.vertical.to_used_value(size.height),
- );
- let radius = match shape {
- EndingShape::Circle(Circle::Radius(length)) => {
- let length = Au::from(length);
- Size2D::new(length, length)
- },
- EndingShape::Circle(Circle::Extent(extent)) => circle_size_keyword(extent, &size, &center),
- EndingShape::Ellipse(Ellipse::Radii(x, y)) => {
- Size2D::new(x.to_used_value(size.width), y.to_used_value(size.height))
- },
- EndingShape::Ellipse(Ellipse::Extent(extent)) => {
- ellipse_size_keyword(extent, &size, &center)
- },
- };
-
- let mut builder = convert_gradient_stops(style, stops, radius.width);
- (
- builder.radial_gradient(
- center.to_layout(),
- radius.to_layout(),
- extend_mode(repeating),
- ),
- builder.into_stops(),
- )
-}
diff --git a/components/layout_2020/display_list/items.rs b/components/layout_2020/display_list/items.rs
index b6674d972fe..8cfa145b43c 100644
--- a/components/layout_2020/display_list/items.rs
+++ b/components/layout_2020/display_list/items.rs
@@ -2,794 +2,22 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-//! Servo heavily uses display lists, which are retained-mode lists of painting commands to
-//! perform. Using a list instead of painting elements in immediate mode allows transforms, hit
-//! testing, and invalidation to be performed using the same primitives as painting. It also allows
-//! Servo to aggressively cull invisible and out-of-bounds painting elements, to reduce overdraw.
-//!
-//! Display items describe relatively high-level drawing operations (for example, entire borders
-//! and shadows instead of lines and blur operations), to reduce the amount of allocation required.
-//! They are therefore not exactly analogous to constructs like Skia pictures, which consist of
-//! low-level drawing primitives.
-
-use euclid::{SideOffsets2D, Vector2D};
-use gfx_traits::print_tree::PrintTree;
-use gfx_traits::{self, StackingContextId};
-use msg::constellation_msg::PipelineId;
-use net_traits::image::base::Image;
-use servo_geometry::MaxRect;
-use std::cmp::Ordering;
+use euclid::Vector2D;
+use gfx_traits;
use std::collections::HashMap;
use std::f32;
-use std::fmt;
-use style::computed_values::_servo_top_layer::T as InTopLayer;
-use webrender_api as wr;
-use webrender_api::units::{LayoutPoint, LayoutRect, LayoutSize, LayoutTransform};
-use webrender_api::{BorderRadius, ClipId, ClipMode, CommonItemProperties, ComplexClipRegion};
-use webrender_api::{ExternalScrollId, FilterOp, GlyphInstance, GradientStop, ImageKey};
-use webrender_api::{MixBlendMode, ScrollSensitivity, Shadow, SpatialId};
-use webrender_api::{StickyOffsetBounds, TransformStyle};
+use webrender_api::ExternalScrollId;
pub use style::dom::OpaqueNode;
-/// The factor that we multiply the blur radius by in order to inflate the boundaries of display
-/// items that involve a blur. This ensures that the display item boundaries include all the ink.
-pub static BLUR_INFLATION_FACTOR: i32 = 3;
-
-/// An index into the vector of ClipScrollNodes. During WebRender conversion these nodes
-/// are given ClipIds.
-#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
-pub struct ClipScrollNodeIndex(usize);
-
-impl ClipScrollNodeIndex {
- pub fn root_scroll_node() -> ClipScrollNodeIndex {
- ClipScrollNodeIndex(1)
- }
-
- pub fn root_reference_frame() -> ClipScrollNodeIndex {
- ClipScrollNodeIndex(0)
- }
-
- pub fn new(index: usize) -> ClipScrollNodeIndex {
- assert_ne!(index, 0, "Use the root_reference_frame constructor");
- assert_ne!(index, 1, "Use the root_scroll_node constructor");
- ClipScrollNodeIndex(index)
- }
-
- pub fn is_root_scroll_node(&self) -> bool {
- *self == Self::root_scroll_node()
- }
-
- pub fn to_define_item(&self) -> DisplayItem {
- DisplayItem::DefineClipScrollNode(Box::new(DefineClipScrollNodeItem {
- base: BaseDisplayItem::empty(),
- node_index: *self,
- }))
- }
-
- pub fn to_index(self) -> usize {
- self.0
- }
-}
-
-/// A set of indices into the clip scroll node vector for a given item.
-#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
-pub struct ClippingAndScrolling {
- pub scrolling: ClipScrollNodeIndex,
- pub clipping: Option<ClipScrollNodeIndex>,
-}
-
-impl ClippingAndScrolling {
- pub fn simple(scrolling: ClipScrollNodeIndex) -> ClippingAndScrolling {
- ClippingAndScrolling {
- scrolling,
- clipping: None,
- }
- }
-
- pub fn new(scrolling: ClipScrollNodeIndex, clipping: ClipScrollNodeIndex) -> Self {
- ClippingAndScrolling {
- scrolling,
- clipping: Some(clipping),
- }
- }
-}
-
#[derive(Serialize)]
-pub struct DisplayList {
- pub list: Vec<DisplayItem>,
- pub clip_scroll_nodes: Vec<ClipScrollNode>,
-}
-
-impl DisplayList {
- /// Return the bounds of this display list based on the dimensions of the root
- /// stacking context.
- pub fn bounds(&self) -> LayoutRect {
- match self.list.get(0) {
- Some(&DisplayItem::PushStackingContext(ref item)) => item.stacking_context.bounds,
- Some(_) => unreachable!("Root element of display list not stacking context."),
- None => LayoutRect::zero(),
- }
- }
-
- pub fn print(&self) {
- let mut print_tree = PrintTree::new("Display List".to_owned());
- self.print_with_tree(&mut print_tree);
- }
-
- pub fn print_with_tree(&self, print_tree: &mut PrintTree) {
- print_tree.new_level("ClipScrollNodes".to_owned());
- for node in &self.clip_scroll_nodes {
- print_tree.add_item(format!("{:?}", node));
- }
- print_tree.end_level();
-
- print_tree.new_level("Items".to_owned());
- for item in &self.list {
- print_tree.add_item(format!(
- "{:?} StackingContext: {:?} {:?}",
- item,
- item.base().stacking_context_id,
- item.clipping_and_scrolling()
- ));
- }
- print_tree.end_level();
- }
-}
+pub struct DisplayList {}
impl gfx_traits::DisplayList for DisplayList {
- /// Analyze the display list to figure out if this may be the first
- /// contentful paint (i.e. the display list contains items of type text,
- /// image, non-white canvas or SVG). Used by metrics.
fn is_contentful(&self) -> bool {
- for item in &self.list {
- match item {
- &DisplayItem::Text(_) | &DisplayItem::Image(_) => return true,
- _ => (),
- }
- }
-
false
}
}
-/// Display list sections that make up a stacking context. Each section here refers
-/// to the steps in CSS 2.1 Appendix E.
-///
-#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
-pub enum DisplayListSection {
- BackgroundAndBorders,
- BlockBackgroundsAndBorders,
- Content,
- Outlines,
-}
-
-#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
-pub enum StackingContextType {
- Real,
- PseudoPositioned,
- PseudoFloat,
-}
-
-#[derive(Clone, Serialize)]
-/// Represents one CSS stacking context, which may or may not have a hardware layer.
-pub struct StackingContext {
- /// The ID of this StackingContext for uniquely identifying it.
- pub id: StackingContextId,
-
- /// The type of this StackingContext. Used for collecting and sorting.
- pub context_type: StackingContextType,
-
- /// The position and size of this stacking context.
- pub bounds: LayoutRect,
-
- /// The overflow rect for this stacking context in its coordinate system.
- pub overflow: LayoutRect,
-
- /// The `z-index` for this stacking context.
- pub z_index: i32,
-
- /// Whether this is the top layer.
- pub in_top_layer: InTopLayer,
-
- /// CSS filters to be applied to this stacking context (including opacity).
- pub filters: Vec<FilterOp>,
-
- /// The blend mode with which this stacking context blends with its backdrop.
- pub mix_blend_mode: MixBlendMode,
-
- /// A transform to be applied to this stacking context.
- pub transform: Option<LayoutTransform>,
-
- /// The transform style of this stacking context.
- pub transform_style: TransformStyle,
-
- /// The perspective matrix to be applied to children.
- pub perspective: Option<LayoutTransform>,
-
- /// The clip and scroll info for this StackingContext.
- pub parent_clipping_and_scrolling: ClippingAndScrolling,
-
- /// The index of the reference frame that this stacking context estalishes.
- pub established_reference_frame: Option<ClipScrollNodeIndex>,
-}
-
-impl StackingContext {
- /// Creates a new stacking context.
- #[inline]
- pub fn new(
- id: StackingContextId,
- context_type: StackingContextType,
- bounds: LayoutRect,
- overflow: LayoutRect,
- z_index: i32,
- in_top_layer: InTopLayer,
- filters: Vec<FilterOp>,
- mix_blend_mode: MixBlendMode,
- transform: Option<LayoutTransform>,
- transform_style: TransformStyle,
- perspective: Option<LayoutTransform>,
- parent_clipping_and_scrolling: ClippingAndScrolling,
- established_reference_frame: Option<ClipScrollNodeIndex>,
- ) -> StackingContext {
- StackingContext {
- id,
- context_type,
- bounds,
- overflow,
- z_index,
- in_top_layer,
- filters,
- mix_blend_mode,
- transform,
- transform_style,
- perspective,
- parent_clipping_and_scrolling,
- established_reference_frame,
- }
- }
-
- #[inline]
- pub fn root() -> StackingContext {
- StackingContext::new(
- StackingContextId::root(),
- StackingContextType::Real,
- LayoutRect::zero(),
- LayoutRect::zero(),
- 0,
- InTopLayer::None,
- vec![],
- MixBlendMode::Normal,
- None,
- TransformStyle::Flat,
- None,
- ClippingAndScrolling::simple(ClipScrollNodeIndex::root_scroll_node()),
- None,
- )
- }
-
- pub fn to_display_list_items(self) -> (DisplayItem, DisplayItem) {
- let mut base_item = BaseDisplayItem::empty();
- base_item.stacking_context_id = self.id;
- base_item.clipping_and_scrolling = self.parent_clipping_and_scrolling;
-
- let pop_item = DisplayItem::PopStackingContext(Box::new(PopStackingContextItem {
- base: base_item.clone(),
- stacking_context_id: self.id,
- }));
-
- let push_item = DisplayItem::PushStackingContext(Box::new(PushStackingContextItem {
- base: base_item,
- stacking_context: self,
- }));
-
- (push_item, pop_item)
- }
-}
-
-impl Ord for StackingContext {
- fn cmp(&self, other: &Self) -> Ordering {
- if self.in_top_layer == InTopLayer::Top {
- if other.in_top_layer == InTopLayer::Top {
- return Ordering::Equal;
- } else {
- return Ordering::Greater;
- }
- } else if other.in_top_layer == InTopLayer::Top {
- return Ordering::Less;
- }
-
- if self.z_index != 0 || other.z_index != 0 {
- return self.z_index.cmp(&other.z_index);
- }
-
- match (self.context_type, other.context_type) {
- (StackingContextType::PseudoFloat, StackingContextType::PseudoFloat) => Ordering::Equal,
- (StackingContextType::PseudoFloat, _) => Ordering::Less,
- (_, StackingContextType::PseudoFloat) => Ordering::Greater,
- (_, _) => Ordering::Equal,
- }
- }
-}
-
-impl PartialOrd for StackingContext {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Eq for StackingContext {}
-impl PartialEq for StackingContext {
- fn eq(&self, other: &Self) -> bool {
- self.id == other.id
- }
-}
-
-impl fmt::Debug for StackingContext {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let type_string = if self.context_type == StackingContextType::Real {
- "StackingContext"
- } else {
- "Pseudo-StackingContext"
- };
-
- write!(
- f,
- "{} at {:?} with overflow {:?}: {:?}",
- type_string, self.bounds, self.overflow, self.id
- )
- }
-}
-
-#[derive(Clone, Debug, PartialEq, Serialize)]
-pub struct StickyFrameData {
- pub margins: SideOffsets2D<Option<f32>>,
- pub vertical_offset_bounds: StickyOffsetBounds,
- pub horizontal_offset_bounds: StickyOffsetBounds,
-}
-
-#[derive(Clone, Debug, PartialEq, Serialize)]
-pub enum ClipScrollNodeType {
- Placeholder,
- ScrollFrame(ScrollSensitivity, ExternalScrollId),
- StickyFrame(StickyFrameData),
- Clip,
-}
-
-/// Defines a clip scroll node.
-#[derive(Clone, Debug, Serialize)]
-pub struct ClipScrollNode {
- /// The index of the parent of this ClipScrollNode.
- pub parent_index: ClipScrollNodeIndex,
-
- /// The position of this scroll root's frame in the parent stacking context.
- pub clip: ClippingRegion,
-
- /// The rect of the contents that can be scrolled inside of the scroll root.
- pub content_rect: LayoutRect,
-
- /// The type of this ClipScrollNode.
- pub node_type: ClipScrollNodeType,
-}
-
-impl ClipScrollNode {
- pub fn placeholder() -> ClipScrollNode {
- ClipScrollNode {
- parent_index: ClipScrollNodeIndex(0),
- clip: ClippingRegion::from_rect(LayoutRect::zero()),
- content_rect: LayoutRect::zero(),
- node_type: ClipScrollNodeType::Placeholder,
- }
- }
-
- pub fn is_placeholder(&self) -> bool {
- self.node_type == ClipScrollNodeType::Placeholder
- }
-}
-
-/// One drawing command in the list.
-#[derive(Clone, Serialize)]
-pub enum DisplayItem {
- Rectangle(Box<CommonDisplayItem<wr::RectangleDisplayItem>>),
- Text(Box<CommonDisplayItem<wr::TextDisplayItem, Vec<GlyphInstance>>>),
- Image(Box<CommonDisplayItem<wr::ImageDisplayItem>>),
- Border(Box<CommonDisplayItem<wr::BorderDisplayItem, Vec<GradientStop>>>),
- Gradient(Box<CommonDisplayItem<wr::GradientDisplayItem, Vec<GradientStop>>>),
- RadialGradient(Box<CommonDisplayItem<wr::RadialGradientDisplayItem, Vec<GradientStop>>>),
- Line(Box<CommonDisplayItem<wr::LineDisplayItem>>),
- BoxShadow(Box<CommonDisplayItem<wr::BoxShadowDisplayItem>>),
- PushTextShadow(Box<PushTextShadowDisplayItem>),
- PopAllTextShadows(Box<PopAllTextShadowsDisplayItem>),
- Iframe(Box<IframeDisplayItem>),
- PushStackingContext(Box<PushStackingContextItem>),
- PopStackingContext(Box<PopStackingContextItem>),
- DefineClipScrollNode(Box<DefineClipScrollNodeItem>),
-}
-
-/// Information common to all display items.
-#[derive(Clone, Serialize)]
-pub struct BaseDisplayItem {
- /// Metadata attached to this display item.
- pub metadata: DisplayItemMetadata,
-
- /// The clip rectangle to use for this item.
- pub clip_rect: LayoutRect,
-
- /// The section of the display list that this item belongs to.
- pub section: DisplayListSection,
-
- /// The id of the stacking context this item belongs to.
- pub stacking_context_id: StackingContextId,
-
- /// The clip and scroll info for this item.
- pub clipping_and_scrolling: ClippingAndScrolling,
-}
-
-impl BaseDisplayItem {
- #[inline(always)]
- pub fn new(
- metadata: DisplayItemMetadata,
- clip_rect: LayoutRect,
- section: DisplayListSection,
- stacking_context_id: StackingContextId,
- clipping_and_scrolling: ClippingAndScrolling,
- ) -> BaseDisplayItem {
- BaseDisplayItem {
- metadata,
- clip_rect,
- section,
- stacking_context_id,
- clipping_and_scrolling,
- }
- }
-
- #[inline(always)]
- pub fn empty() -> BaseDisplayItem {
- BaseDisplayItem {
- metadata: DisplayItemMetadata {
- node: OpaqueNode(0),
- pointing: None,
- },
- // Create a rectangle of maximal size.
- clip_rect: LayoutRect::max_rect(),
- section: DisplayListSection::Content,
- stacking_context_id: StackingContextId::root(),
- clipping_and_scrolling: ClippingAndScrolling::simple(
- ClipScrollNodeIndex::root_scroll_node(),
- ),
- }
- }
-}
-
-pub fn empty_common_item_properties() -> CommonItemProperties {
- CommonItemProperties {
- clip_rect: LayoutRect::max_rect(),
- clip_id: ClipId::root(wr::PipelineId::dummy()),
- spatial_id: SpatialId::root_scroll_node(wr::PipelineId::dummy()),
- hit_info: None,
- is_backface_visible: false,
- }
-}
-
-/// A clipping region for a display item. Currently, this can describe rectangles, rounded
-/// rectangles (for `border-radius`), or arbitrary intersections of the two. Arbitrary transforms
-/// are not supported because those are handled by the higher-level `StackingContext` abstraction.
-#[derive(Clone, PartialEq, Serialize)]
-pub struct ClippingRegion {
- /// The main rectangular region. This does not include any corners.
- pub main: LayoutRect,
- /// Any complex regions.
- ///
- /// TODO(pcwalton): Atomically reference count these? Not sure if it's worth the trouble.
- /// Measure and follow up.
- pub complex: Vec<ComplexClipRegion>,
-}
-
-impl ClippingRegion {
- /// Returns an empty clipping region that, if set, will result in no pixels being visible.
- #[inline]
- pub fn empty() -> ClippingRegion {
- ClippingRegion {
- main: LayoutRect::zero(),
- complex: Vec::new(),
- }
- }
-
- /// Returns an all-encompassing clipping region that clips no pixels out.
- #[inline]
- pub fn max() -> ClippingRegion {
- ClippingRegion {
- main: LayoutRect::max_rect(),
- complex: Vec::new(),
- }
- }
-
- /// Returns a clipping region that represents the given rectangle.
- #[inline]
- pub fn from_rect(rect: LayoutRect) -> ClippingRegion {
- ClippingRegion {
- main: rect,
- complex: Vec::new(),
- }
- }
-
- /// Intersects this clipping region with the given rounded rectangle.
- #[inline]
- pub fn intersect_with_rounded_rect(&mut self, rect: LayoutRect, radii: BorderRadius) {
- let new_complex_region = ComplexClipRegion {
- rect,
- radii,
- mode: ClipMode::Clip,
- };
-
- // FIXME(pcwalton): This is O(n²) worst case for disjoint clipping regions. Is that OK?
- // They're slow anyway…
- //
- // Possibly relevant if we want to do better:
- //
- // http://www.inrg.csie.ntu.edu.tw/algorithm2014/presentation/D&C%20Lee-84.pdf
- for existing_complex_region in &mut self.complex {
- if completely_encloses(&existing_complex_region, &new_complex_region) {
- *existing_complex_region = new_complex_region;
- return;
- }
- if completely_encloses(&new_complex_region, &existing_complex_region) {
- return;
- }
- }
-
- self.complex.push(new_complex_region);
- }
-}
-
-impl fmt::Debug for ClippingRegion {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if *self == ClippingRegion::max() {
- write!(f, "ClippingRegion::Max")
- } else if *self == ClippingRegion::empty() {
- write!(f, "ClippingRegion::Empty")
- } else if self.main == LayoutRect::max_rect() {
- write!(f, "ClippingRegion(Complex={:?})", self.complex)
- } else {
- write!(
- f,
- "ClippingRegion(Rect={:?}, Complex={:?})",
- self.main, self.complex
- )
- }
- }
-}
-
-// TODO(pcwalton): This could be more aggressive by considering points that touch the inside of
-// the border radius ellipse.
-fn completely_encloses(this: &ComplexClipRegion, other: &ComplexClipRegion) -> bool {
- let left = this.radii.top_left.width.max(this.radii.bottom_left.width);
- let top = this.radii.top_left.height.max(this.radii.top_right.height);
- let right = this
- .radii
- .top_right
- .width
- .max(this.radii.bottom_right.width);
- let bottom = this
- .radii
- .bottom_left
- .height
- .max(this.radii.bottom_right.height);
- let interior = LayoutRect::new(
- LayoutPoint::new(this.rect.origin.x + left, this.rect.origin.y + top),
- LayoutSize::new(
- this.rect.size.width - left - right,
- this.rect.size.height - top - bottom,
- ),
- );
- interior.origin.x <= other.rect.origin.x &&
- interior.origin.y <= other.rect.origin.y &&
- interior.max_x() >= other.rect.max_x() &&
- interior.max_y() >= other.rect.max_y()
-}
-
-/// Metadata attached to each display item. This is useful for performing auxiliary threads with
-/// the display list involving hit testing: finding the originating DOM node and determining the
-/// cursor to use when the element is hovered over.
-#[derive(Clone, Copy, Serialize)]
-pub struct DisplayItemMetadata {
- /// The DOM node from which this display item originated.
- pub node: OpaqueNode,
- /// The value of the `cursor` property when the mouse hovers over this display item. If `None`,
- /// this display item is ineligible for pointer events (`pointer-events: none`).
- pub pointing: Option<u16>,
-}
-
-#[derive(Clone, Eq, PartialEq, Serialize)]
-pub enum TextOrientation {
- Upright,
- SidewaysLeft,
- SidewaysRight,
-}
-
-/// Paints an iframe.
-#[derive(Clone, Serialize)]
-pub struct IframeDisplayItem {
- pub base: BaseDisplayItem,
- pub iframe: PipelineId,
- pub bounds: LayoutRect,
-}
-
-#[derive(Clone, Serialize)]
-pub struct CommonDisplayItem<T, U = ()> {
- pub base: BaseDisplayItem,
- pub item: T,
- pub data: U,
-}
-
-impl<T> CommonDisplayItem<T> {
- pub fn new(base: BaseDisplayItem, item: T) -> Box<CommonDisplayItem<T>> {
- Box::new(CommonDisplayItem {
- base,
- item,
- data: (),
- })
- }
-}
-
-impl<T, U> CommonDisplayItem<T, U> {
- pub fn with_data(base: BaseDisplayItem, item: T, data: U) -> Box<CommonDisplayItem<T, U>> {
- Box::new(CommonDisplayItem { base, item, data })
- }
-}
-
-/// Defines a text shadow that affects all items until the paired PopTextShadow.
-#[derive(Clone, Serialize)]
-pub struct PushTextShadowDisplayItem {
- /// Fields common to all display items.
- pub base: BaseDisplayItem,
-
- pub shadow: Shadow,
-}
-
-/// Defines a text shadow that affects all items until the next PopTextShadow.
-#[derive(Clone, Serialize)]
-pub struct PopAllTextShadowsDisplayItem {
- /// Fields common to all display items.
- pub base: BaseDisplayItem,
-}
-
-/// Defines a stacking context.
-#[derive(Clone, Serialize)]
-pub struct PushStackingContextItem {
- /// Fields common to all display items.
- pub base: BaseDisplayItem,
-
- pub stacking_context: StackingContext,
-}
-
-/// Defines a stacking context.
-#[derive(Clone, Serialize)]
-pub struct PopStackingContextItem {
- /// Fields common to all display items.
- pub base: BaseDisplayItem,
-
- pub stacking_context_id: StackingContextId,
-}
-
-/// Starts a group of items inside a particular scroll root.
-#[derive(Clone, Serialize)]
-pub struct DefineClipScrollNodeItem {
- /// Fields common to all display items.
- pub base: BaseDisplayItem,
-
- /// The scroll root that this item starts.
- pub node_index: ClipScrollNodeIndex,
-}
-
-impl DisplayItem {
- pub fn base(&self) -> &BaseDisplayItem {
- match *self {
- DisplayItem::Rectangle(ref rect) => &rect.base,
- DisplayItem::Text(ref text) => &text.base,
- DisplayItem::Image(ref image_item) => &image_item.base,
- DisplayItem::Border(ref border) => &border.base,
- DisplayItem::Gradient(ref gradient) => &gradient.base,
- DisplayItem::RadialGradient(ref gradient) => &gradient.base,
- DisplayItem::Line(ref line) => &line.base,
- DisplayItem::BoxShadow(ref box_shadow) => &box_shadow.base,
- DisplayItem::PushTextShadow(ref push_text_shadow) => &push_text_shadow.base,
- DisplayItem::PopAllTextShadows(ref pop_text_shadow) => &pop_text_shadow.base,
- DisplayItem::Iframe(ref iframe) => &iframe.base,
- DisplayItem::PushStackingContext(ref stacking_context) => &stacking_context.base,
- DisplayItem::PopStackingContext(ref item) => &item.base,
- DisplayItem::DefineClipScrollNode(ref item) => &item.base,
- }
- }
-
- pub fn clipping_and_scrolling(&self) -> ClippingAndScrolling {
- self.base().clipping_and_scrolling
- }
-
- pub fn stacking_context_id(&self) -> StackingContextId {
- self.base().stacking_context_id
- }
-
- pub fn section(&self) -> DisplayListSection {
- self.base().section
- }
-
- pub fn bounds(&self) -> LayoutRect {
- match *self {
- DisplayItem::Rectangle(ref item) => item.item.common.clip_rect,
- DisplayItem::Text(ref item) => item.item.bounds,
- DisplayItem::Image(ref item) => item.item.bounds,
- DisplayItem::Border(ref item) => item.item.bounds,
- DisplayItem::Gradient(ref item) => item.item.bounds,
- DisplayItem::RadialGradient(ref item) => item.item.bounds,
- DisplayItem::Line(ref item) => item.item.area,
- DisplayItem::BoxShadow(ref item) => item.item.box_bounds,
- DisplayItem::PushTextShadow(_) => LayoutRect::zero(),
- DisplayItem::PopAllTextShadows(_) => LayoutRect::zero(),
- DisplayItem::Iframe(ref item) => item.bounds,
- DisplayItem::PushStackingContext(ref item) => item.stacking_context.bounds,
- DisplayItem::PopStackingContext(_) => LayoutRect::zero(),
- DisplayItem::DefineClipScrollNode(_) => LayoutRect::zero(),
- }
- }
-}
-
-impl fmt::Debug for DisplayItem {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if let DisplayItem::PushStackingContext(ref item) = *self {
- return write!(f, "PushStackingContext({:?})", item.stacking_context);
- }
-
- if let DisplayItem::PopStackingContext(ref item) = *self {
- return write!(f, "PopStackingContext({:?}", item.stacking_context_id);
- }
-
- if let DisplayItem::DefineClipScrollNode(ref item) = *self {
- return write!(f, "DefineClipScrollNode({:?}", item.node_index);
- }
-
- write!(
- f,
- "{} @ {:?} {:?}",
- match *self {
- DisplayItem::Rectangle(_) => "Rectangle".to_owned(),
- DisplayItem::Text(_) => "Text".to_owned(),
- DisplayItem::Image(_) => "Image".to_owned(),
- DisplayItem::Border(_) => "Border".to_owned(),
- DisplayItem::Gradient(_) => "Gradient".to_owned(),
- DisplayItem::RadialGradient(_) => "RadialGradient".to_owned(),
- DisplayItem::Line(_) => "Line".to_owned(),
- DisplayItem::BoxShadow(_) => "BoxShadow".to_owned(),
- DisplayItem::PushTextShadow(_) => "PushTextShadow".to_owned(),
- DisplayItem::PopAllTextShadows(_) => "PopTextShadow".to_owned(),
- DisplayItem::Iframe(_) => "Iframe".to_owned(),
- DisplayItem::PushStackingContext(_) |
- DisplayItem::PopStackingContext(_) |
- DisplayItem::DefineClipScrollNode(_) => "".to_owned(),
- },
- self.bounds(),
- self.base().clip_rect
- )
- }
-}
-
-#[derive(Clone, Copy, Serialize)]
-pub struct WebRenderImageInfo {
- pub width: u32,
- pub height: u32,
- pub key: Option<ImageKey>,
-}
-
-impl WebRenderImageInfo {
- #[inline]
- pub fn from_image(image: &Image) -> WebRenderImageInfo {
- WebRenderImageInfo {
- width: image.width,
- height: image.height,
- key: image.id,
- }
- }
-}
-
/// The type of the scroll offset list. This is only populated if WebRender is in use.
pub type ScrollOffsetMap = HashMap<ExternalScrollId, Vector2D<f32>>;
diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs
index 302728fbf22..fc9e5359699 100644
--- a/components/layout_2020/display_list/mod.rs
+++ b/components/layout_2020/display_list/mod.rs
@@ -2,18 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-pub use self::builder::BorderPaintingMode;
-pub use self::builder::DisplayListBuildState;
pub use self::builder::IndexableText;
-pub use self::builder::StackingContextCollectionFlags;
-pub use self::builder::StackingContextCollectionState;
-pub use self::conversions::ToLayout;
pub use self::webrender_helpers::WebRenderDisplayListConverter;
-mod background;
-mod border;
mod builder;
-mod conversions;
-mod gradient;
pub mod items;
mod webrender_helpers;
diff --git a/components/layout_2020/display_list/webrender_helpers.rs b/components/layout_2020/display_list/webrender_helpers.rs
index 6bdb1bfcfdc..801e404a64b 100644
--- a/components/layout_2020/display_list/webrender_helpers.rs
+++ b/components/layout_2020/display_list/webrender_helpers.rs
@@ -2,320 +2,25 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-// TODO(gw): This contains helper traits and implementations for converting Servo display lists
-// into WebRender display lists. In the future, this step should be completely removed.
-// This might be achieved by sharing types between WR and Servo display lists, or
-// completely converting layout to directly generate WebRender display lists, for example.
-
-use crate::display_list::items::{BaseDisplayItem, ClipScrollNode, ClipScrollNodeType};
-use crate::display_list::items::{DisplayItem, DisplayList, StackingContextType};
+use crate::display_list::items::DisplayList;
use msg::constellation_msg::PipelineId;
-use webrender_api::units::LayoutPoint;
-use webrender_api::{self, ClipId, CommonItemProperties, DisplayItem as WrDisplayItem};
-use webrender_api::{DisplayListBuilder, PropertyBinding, PushStackingContextDisplayItem};
-use webrender_api::{
- RasterSpace, ReferenceFrameKind, SpaceAndClipInfo, SpatialId, StackingContext,
-};
+use webrender_api::{self, DisplayListBuilder};
+use webrender_api::units::{LayoutSize};
pub trait WebRenderDisplayListConverter {
fn convert_to_webrender(&mut self, pipeline_id: PipelineId) -> DisplayListBuilder;
}
-struct ClipScrollState {
- clip_ids: Vec<Option<ClipId>>,
- spatial_ids: Vec<Option<SpatialId>>,
- active_clip_id: ClipId,
- active_spatial_id: SpatialId,
-}
-
-trait WebRenderDisplayItemConverter {
- fn convert_to_webrender(
- &mut self,
- clip_scroll_nodes: &[ClipScrollNode],
- state: &mut ClipScrollState,
- builder: &mut DisplayListBuilder,
- );
-}
-
impl WebRenderDisplayListConverter for DisplayList {
fn convert_to_webrender(&mut self, pipeline_id: PipelineId) -> DisplayListBuilder {
- let mut clip_ids = vec![None; self.clip_scroll_nodes.len()];
- let mut spatial_ids = vec![None; self.clip_scroll_nodes.len()];
-
- // We need to add the WebRender root reference frame and root scroll node ids
- // here manually, because WebRender creates these automatically.
- // We also follow the "old" WebRender API for clip/scroll for now,
- // hence both arrays are initialized based on FIRST_SPATIAL_NODE_INDEX,
- // while FIRST_CLIP_NODE_INDEX is not taken into account.
-
let webrender_pipeline = pipeline_id.to_webrender();
- clip_ids[0] = Some(ClipId::root(webrender_pipeline));
- clip_ids[1] = Some(ClipId::root(webrender_pipeline));
- spatial_ids[0] = Some(SpatialId::root_reference_frame(webrender_pipeline));
- spatial_ids[1] = Some(SpatialId::root_scroll_node(webrender_pipeline));
-
- let mut state = ClipScrollState {
- clip_ids,
- spatial_ids,
- active_clip_id: ClipId::root(webrender_pipeline),
- active_spatial_id: SpatialId::root_scroll_node(webrender_pipeline),
- };
- let mut builder = DisplayListBuilder::with_capacity(
+ let builder = DisplayListBuilder::with_capacity(
webrender_pipeline,
- self.bounds().size,
+ LayoutSize::zero(),
1024 * 1024, // 1 MB of space
);
- for item in &mut self.list {
- item.convert_to_webrender(&self.clip_scroll_nodes, &mut state, &mut builder);
- }
-
builder
}
}
-
-impl WebRenderDisplayItemConverter for DisplayItem {
- fn convert_to_webrender(
- &mut self,
- clip_scroll_nodes: &[ClipScrollNode],
- state: &mut ClipScrollState,
- builder: &mut DisplayListBuilder,
- ) {
- // Note: for each time of a display item, if we register one of `clip_ids` or `spatial_ids`,
- // we also register the other one as inherited from the current state or the stack.
- // This is not an ideal behavior, but it is compatible with the old WebRender model
- // of the clip-scroll tree.
-
- let clip_and_scroll_indices = self.base().clipping_and_scrolling;
- trace!("converting {:?}", clip_and_scroll_indices);
-
- let cur_spatial_id = state.spatial_ids[clip_and_scroll_indices.scrolling.to_index()]
- .expect("Tried to use WebRender SpatialId before it was defined.");
- if cur_spatial_id != state.active_spatial_id {
- state.active_spatial_id = cur_spatial_id;
- }
-
- let internal_clip_id = clip_and_scroll_indices
- .clipping
- .unwrap_or(clip_and_scroll_indices.scrolling);
- let cur_clip_id = state.clip_ids[internal_clip_id.to_index()]
- .expect("Tried to use WebRender ClipId before it was defined.");
- if cur_clip_id != state.active_clip_id {
- state.active_clip_id = cur_clip_id;
- }
-
- match *self {
- DisplayItem::Rectangle(ref mut item) => {
- item.item.common = build_common_item_properties(&item.base, state);
- builder.push_item(&WrDisplayItem::Rectangle(item.item));
- },
- DisplayItem::Text(ref mut item) => {
- item.item.common = build_common_item_properties(&item.base, state);
- builder.push_item(&WrDisplayItem::Text(item.item));
- builder.push_iter(item.data.iter());
- },
- DisplayItem::Image(ref mut item) => {
- item.item.common = build_common_item_properties(&item.base, state);
- builder.push_item(&WrDisplayItem::Image(item.item));
- },
- DisplayItem::Border(ref mut item) => {
- item.item.common = build_common_item_properties(&item.base, state);
- if !item.data.is_empty() {
- builder.push_stops(item.data.as_ref());
- }
- builder.push_item(&WrDisplayItem::Border(item.item));
- },
- DisplayItem::Gradient(ref mut item) => {
- item.item.common = build_common_item_properties(&item.base, state);
- builder.push_stops(item.data.as_ref());
- builder.push_item(&WrDisplayItem::Gradient(item.item));
- },
- DisplayItem::RadialGradient(ref mut item) => {
- item.item.common = build_common_item_properties(&item.base, state);
- builder.push_stops(item.data.as_ref());
- builder.push_item(&WrDisplayItem::RadialGradient(item.item));
- },
- DisplayItem::Line(ref mut item) => {
- item.item.common = build_common_item_properties(&item.base, state);
- builder.push_item(&WrDisplayItem::Line(item.item));
- },
- DisplayItem::BoxShadow(ref mut item) => {
- item.item.common = build_common_item_properties(&item.base, state);
- builder.push_item(&WrDisplayItem::BoxShadow(item.item));
- },
- DisplayItem::PushTextShadow(ref mut item) => {
- let common = build_common_item_properties(&item.base, state);
- builder.push_shadow(
- &SpaceAndClipInfo {
- spatial_id: common.spatial_id,
- clip_id: common.clip_id,
- },
- item.shadow,
- true,
- );
- },
- DisplayItem::PopAllTextShadows(_) => {
- builder.push_item(&WrDisplayItem::PopAllShadows);
- },
- DisplayItem::Iframe(ref mut item) => {
- let common = build_common_item_properties(&item.base, state);
- builder.push_iframe(
- item.bounds,
- common.clip_rect,
- &SpaceAndClipInfo {
- spatial_id: common.spatial_id,
- clip_id: common.clip_id,
- },
- item.iframe.to_webrender(),
- true,
- );
- },
- DisplayItem::PushStackingContext(ref mut item) => {
- let stacking_context = &item.stacking_context;
- debug_assert_eq!(stacking_context.context_type, StackingContextType::Real);
-
- //let mut info = webrender_api::LayoutPrimitiveInfo::new(stacking_context.bounds);
- let mut bounds = stacking_context.bounds;
- let spatial_id =
- if let Some(frame_index) = stacking_context.established_reference_frame {
- let (transform, ref_frame) =
- match (stacking_context.transform, stacking_context.perspective) {
- (None, Some(p)) => (
- p,
- ReferenceFrameKind::Perspective {
- scrolling_relative_to: None,
- },
- ),
- (Some(t), None) => (t, ReferenceFrameKind::Transform),
- (Some(t), Some(p)) => (
- t.pre_mul(&p),
- ReferenceFrameKind::Perspective {
- scrolling_relative_to: None,
- },
- ),
- (None, None) => unreachable!(),
- };
-
- let spatial_id = builder.push_reference_frame(
- stacking_context.bounds.origin,
- state.active_spatial_id,
- stacking_context.transform_style,
- PropertyBinding::Value(transform),
- ref_frame,
- );
-
- state.spatial_ids[frame_index.to_index()] = Some(spatial_id);
- state.clip_ids[frame_index.to_index()] = Some(cur_clip_id);
-
- bounds.origin = LayoutPoint::zero();
- spatial_id
- } else {
- state.active_spatial_id
- };
-
- if !stacking_context.filters.is_empty() {
- builder.push_item(&WrDisplayItem::SetFilterOps);
- builder.push_iter(&stacking_context.filters);
- }
-
- let wr_item = PushStackingContextDisplayItem {
- origin: bounds.origin,
- spatial_id,
- is_backface_visible: true,
- stacking_context: StackingContext {
- transform_style: stacking_context.transform_style,
- mix_blend_mode: stacking_context.mix_blend_mode,
- clip_id: None,
- raster_space: RasterSpace::Screen,
- // TODO(pcwalton): Enable picture caching?
- cache_tiles: false,
- },
- };
-
- builder.push_item(&WrDisplayItem::PushStackingContext(wr_item));
- },
- DisplayItem::PopStackingContext(_) => builder.pop_stacking_context(),
- DisplayItem::DefineClipScrollNode(ref mut item) => {
- let node = &clip_scroll_nodes[item.node_index.to_index()];
- let item_rect = node.clip.main;
-
- let parent_spatial_id = state.spatial_ids[node.parent_index.to_index()]
- .expect("Tried to use WebRender parent SpatialId before it was defined.");
- let parent_clip_id = state.clip_ids[node.parent_index.to_index()]
- .expect("Tried to use WebRender parent ClipId before it was defined.");
-
- match node.node_type {
- ClipScrollNodeType::Clip => {
- let id = builder.define_clip(
- &SpaceAndClipInfo {
- clip_id: parent_clip_id,
- spatial_id: parent_spatial_id,
- },
- item_rect,
- node.clip.complex.clone(),
- None,
- );
-
- state.spatial_ids[item.node_index.to_index()] = Some(parent_spatial_id);
- state.clip_ids[item.node_index.to_index()] = Some(id);
- },
- ClipScrollNodeType::ScrollFrame(scroll_sensitivity, external_id) => {
- let space_clip_info = builder.define_scroll_frame(
- &SpaceAndClipInfo {
- clip_id: parent_clip_id,
- spatial_id: parent_spatial_id,
- },
- Some(external_id),
- node.content_rect,
- node.clip.main,
- node.clip.complex.clone(),
- None,
- scroll_sensitivity,
- webrender_api::units::LayoutVector2D::zero(),
- );
-
- state.clip_ids[item.node_index.to_index()] = Some(space_clip_info.clip_id);
- state.spatial_ids[item.node_index.to_index()] =
- Some(space_clip_info.spatial_id);
- },
- ClipScrollNodeType::StickyFrame(ref sticky_data) => {
- // TODO: Add define_sticky_frame_with_parent to WebRender.
- let id = builder.define_sticky_frame(
- parent_spatial_id,
- item_rect,
- sticky_data.margins,
- sticky_data.vertical_offset_bounds,
- sticky_data.horizontal_offset_bounds,
- webrender_api::units::LayoutVector2D::zero(),
- );
-
- state.spatial_ids[item.node_index.to_index()] = Some(id);
- state.clip_ids[item.node_index.to_index()] = Some(parent_clip_id);
- },
- ClipScrollNodeType::Placeholder => {
- unreachable!("Found DefineClipScrollNode for Placeholder type node.");
- },
- };
- },
- }
- }
-}
-
-fn build_common_item_properties(
- base: &BaseDisplayItem,
- state: &ClipScrollState,
-) -> CommonItemProperties {
- let tag = match base.metadata.pointing {
- Some(cursor) => Some((base.metadata.node.0 as u64, cursor)),
- None => None,
- };
- CommonItemProperties {
- clip_rect: base.clip_rect,
- spatial_id: state.active_spatial_id,
- clip_id: state.active_clip_id,
- // TODO(gw): Make use of the WR backface visibility functionality.
- is_backface_visible: true,
- hit_info: tag,
- }
-}
diff --git a/components/layout_2020/flex.rs b/components/layout_2020/flex.rs
deleted file mode 100644
index e6aa2f7b2c8..00000000000
--- a/components/layout_2020/flex.rs
+++ /dev/null
@@ -1,1128 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Layout for elements with a CSS `display` property of `flex`.
-
-use crate::block::{AbsoluteAssignBSizesTraversal, BlockFlow, MarginsMayCollapseFlag};
-use crate::context::LayoutContext;
-use crate::display_list::{
- BorderPaintingMode, DisplayListBuildState, StackingContextCollectionState,
-};
-use crate::floats::FloatKind;
-use crate::flow::{Flow, FlowClass, FlowFlags, GetBaseFlow, ImmutableFlowUtils, OpaqueFlow};
-use crate::fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
-use crate::layout_debug;
-use crate::model::{self, AdjoiningMargins, CollapsibleMargins};
-use crate::model::{IntrinsicISizes, MaybeAuto, SizeConstraint};
-use crate::traversal::PreorderFlowTraversal;
-use app_units::{Au, MAX_AU};
-use euclid::Point2D;
-use std::cmp::{max, min};
-use std::ops::Range;
-use style::computed_values::align_content::T as AlignContent;
-use style::computed_values::align_self::T as AlignSelf;
-use style::computed_values::flex_direction::T as FlexDirection;
-use style::computed_values::flex_wrap::T as FlexWrap;
-use style::computed_values::justify_content::T as JustifyContent;
-use style::logical_geometry::{Direction, LogicalSize};
-use style::properties::ComputedValues;
-use style::servo::restyle_damage::ServoRestyleDamage;
-use style::values::computed::flex::FlexBasis;
-use style::values::computed::{MaxSize, Size};
-
-/// The size of an axis. May be a specified size, a min/max
-/// constraint, or an unlimited size
-#[derive(Debug, Serialize)]
-enum AxisSize {
- Definite(Au),
- MinMax(SizeConstraint),
- Infinite,
-}
-
-impl AxisSize {
- /// Generate a new available cross or main axis size from the specified size of the container,
- /// containing block size, min constraint, and max constraint
- pub fn new(size: Size, content_size: Option<Au>, min: Size, max: MaxSize) -> AxisSize {
- match size {
- Size::Auto => AxisSize::MinMax(SizeConstraint::new(content_size, min, max, None)),
- Size::LengthPercentage(ref lp) => match lp.maybe_to_used_value(content_size) {
- Some(length) => AxisSize::Definite(length),
- None => AxisSize::Infinite,
- },
- }
- }
-}
-
-/// This function accepts the flex-basis and the size property in main direction from style,
-/// and the container size, then return the used value of flex basis. it can be used to help
-/// determining the flex base size and to indicate whether the main size of the item
-/// is definite after flex size resolving.
-fn from_flex_basis(flex_basis: FlexBasis, main_length: Size, containing_length: Au) -> MaybeAuto {
- let width = match flex_basis {
- FlexBasis::Content => return MaybeAuto::Auto,
- FlexBasis::Size(width) => width,
- };
-
- let width = match width {
- Size::Auto => main_length,
- _ => width,
- };
-
- match width {
- Size::Auto => MaybeAuto::Auto,
- Size::LengthPercentage(ref lp) => MaybeAuto::Specified(lp.to_used_value(containing_length)),
- }
-}
-
-/// Represents a child in a flex container. Most fields here are used in
-/// flex size resolving, and items are sorted by the 'order' property.
-#[derive(Debug, Serialize)]
-struct FlexItem {
- /// Main size of a flex item, used to store results of flexible length calcuation.
- pub main_size: Au,
- /// Used flex base size.
- pub base_size: Au,
- /// The minimal size in main direction.
- pub min_size: Au,
- /// The maximal main size. If this property is not actually set by style
- /// It will be the largest size available for code reuse.
- pub max_size: Au,
- /// The index of the actual flow in our child list.
- pub index: usize,
- /// The 'flex-grow' property of this item.
- pub flex_grow: f32,
- /// The 'flex-shrink' property of this item.
- pub flex_shrink: f32,
- /// The 'order' property of this item.
- pub order: i32,
- /// Whether the main size has met its constraint.
- pub is_frozen: bool,
- /// True if this flow has property 'visibility::collapse'.
- pub is_strut: bool,
-}
-
-impl FlexItem {
- pub fn new(index: usize, flow: &dyn Flow) -> FlexItem {
- let style = &flow.as_block().fragment.style;
- let flex_grow = style.get_position().flex_grow;
- let flex_shrink = style.get_position().flex_shrink;
- let order = style.get_position().order;
- // TODO(stshine): for item with 'visibility:collapse', set is_strut to true.
-
- FlexItem {
- main_size: Au(0),
- base_size: Au(0),
- min_size: Au(0),
- max_size: MAX_AU,
- index: index,
- flex_grow: flex_grow.into(),
- flex_shrink: flex_shrink.into(),
- order: order,
- is_frozen: false,
- is_strut: false,
- }
- }
-
- /// Initialize the used flex base size, minimal main size and maximal main size.
- /// For block mode container this method should be called in assign_block_size()
- /// pass so that the item has already been layouted.
- pub fn init_sizes(&mut self, flow: &mut dyn Flow, containing_length: Au, direction: Direction) {
- let block = flow.as_mut_block();
- match direction {
- // TODO(stshine): the definition of min-{width, height} in style component
- // should change to LengthPercentageOrAuto for automatic implied minimal size.
- // https://drafts.csswg.org/css-flexbox-1/#min-size-auto
- Direction::Inline => {
- let basis = from_flex_basis(
- block.fragment.style.get_position().flex_basis,
- block.fragment.style.content_inline_size(),
- containing_length,
- );
-
- // These methods compute auto margins to zero length, which is exactly what we want.
- block.fragment.compute_border_and_padding(containing_length);
- block
- .fragment
- .compute_inline_direction_margins(containing_length);
- block
- .fragment
- .compute_block_direction_margins(containing_length);
-
- let (border_padding, margin) = block.fragment.surrounding_intrinsic_inline_size();
- let content_size = block.base.intrinsic_inline_sizes.preferred_inline_size -
- border_padding -
- margin +
- block.fragment.box_sizing_boundary(direction);
- self.base_size = basis.specified_or_default(content_size);
- self.max_size = block
- .fragment
- .style
- .max_inline_size()
- .to_used_value(containing_length)
- .unwrap_or(MAX_AU);
- self.min_size = block
- .fragment
- .style
- .min_inline_size()
- .to_used_value(containing_length)
- .unwrap_or(Au(0));
- },
- Direction::Block => {
- let basis = from_flex_basis(
- block.fragment.style.get_position().flex_basis,
- block.fragment.style.content_block_size(),
- containing_length,
- );
- let content_size = block.fragment.border_box.size.block -
- block.fragment.border_padding.block_start_end() +
- block.fragment.box_sizing_boundary(direction);
- self.base_size = basis.specified_or_default(content_size);
- self.max_size = block
- .fragment
- .style
- .max_block_size()
- .to_used_value(containing_length)
- .unwrap_or(MAX_AU);
- self.min_size = block
- .fragment
- .style
- .min_block_size()
- .to_used_value(containing_length)
- .unwrap_or(Au(0));
- },
- }
- }
-
- /// Returns the outer main size of the item, including paddings and margins,
- /// clamped by max and min size.
- pub fn outer_main_size(&self, flow: &dyn Flow, direction: Direction) -> Au {
- let ref fragment = flow.as_block().fragment;
- let outer_width = match direction {
- Direction::Inline => {
- fragment.border_padding.inline_start_end() + fragment.margin.inline_start_end()
- },
- Direction::Block => {
- fragment.border_padding.block_start_end() + fragment.margin.block_start_end()
- },
- };
- max(self.min_size, min(self.base_size, self.max_size)) -
- fragment.box_sizing_boundary(direction) +
- outer_width
- }
-
- /// Returns the number of auto margins in given direction.
- pub fn auto_margin_count(&self, flow: &dyn Flow, direction: Direction) -> i32 {
- let margin = flow.as_block().fragment.style.logical_margin();
- let mut margin_count = 0;
- match direction {
- Direction::Inline => {
- if margin.inline_start.is_auto() {
- margin_count += 1;
- }
- if margin.inline_end.is_auto() {
- margin_count += 1;
- }
- },
- Direction::Block => {
- if margin.block_start.is_auto() {
- margin_count += 1;
- }
- if margin.block_end.is_auto() {
- margin_count += 1;
- }
- },
- }
- margin_count
- }
-}
-
-/// A line in a flex container.
-// TODO(stshine): More fields are required to handle collapsed items and baseline alignment.
-#[derive(Debug, Serialize)]
-struct FlexLine {
- /// Range of items belong to this line in 'self.items'.
- pub range: Range<usize>,
- /// Remaining free space of this line, items will grow or shrink based on it being positive or negative.
- pub free_space: Au,
- /// The number of auto margins of items.
- pub auto_margin_count: i32,
- /// Line size in the block direction.
- pub cross_size: Au,
-}
-
-impl FlexLine {
- pub fn new(range: Range<usize>, free_space: Au, auto_margin_count: i32) -> FlexLine {
- FlexLine {
- range: range,
- auto_margin_count: auto_margin_count,
- free_space: free_space,
- cross_size: Au(0),
- }
- }
-
- /// This method implements the flexible lengths resolving algorithm.
- /// The 'collapse' parameter is used to indicate whether items with 'visibility: collapse'
- /// is included in length resolving. The result main size is stored in 'item.main_size'.
- /// <https://drafts.csswg.org/css-flexbox/#resolve-flexible-lengths>
- pub fn flex_resolve(&mut self, items: &mut [FlexItem], collapse: bool) {
- let mut total_grow = 0.0;
- let mut total_shrink = 0.0;
- let mut total_scaled = 0.0;
- let mut active_count = 0;
- // Iterate through items, collect total factors and freeze those that have already met
- // their constraints or won't grow/shrink in corresponding scenario.
- // https://drafts.csswg.org/css-flexbox/#resolve-flexible-lengths
- for item in items.iter_mut().filter(|i| !(i.is_strut && collapse)) {
- item.main_size = max(item.min_size, min(item.base_size, item.max_size));
- if (self.free_space > Au(0) &&
- (item.flex_grow == 0.0 || item.base_size >= item.max_size)) ||
- (self.free_space < Au(0) &&
- (item.flex_shrink == 0.0 || item.base_size <= item.min_size))
- {
- item.is_frozen = true;
- } else {
- item.is_frozen = false;
- total_grow += item.flex_grow;
- total_shrink += item.flex_shrink;
- // The scaled factor is used to calculate flex shrink
- total_scaled += item.flex_shrink * item.base_size.0 as f32;
- active_count += 1;
- }
- }
-
- let initial_free_space = self.free_space;
- let mut total_variation = Au(1);
- // If there is no remaining free space or all items are frozen, stop loop.
- while total_variation != Au(0) && self.free_space != Au(0) && active_count > 0 {
- self.free_space =
- // https://drafts.csswg.org/css-flexbox/#remaining-free-space
- if self.free_space > Au(0) {
- min(initial_free_space.scale_by(total_grow), self.free_space)
- } else {
- max(initial_free_space.scale_by(total_shrink), self.free_space)
- };
-
- total_variation = Au(0);
- for item in items
- .iter_mut()
- .filter(|i| !i.is_frozen)
- .filter(|i| !(i.is_strut && collapse))
- {
- // Use this and the 'abs()' below to make the code work in both grow and shrink scenarios.
- let (factor, end_size) = if self.free_space > Au(0) {
- (item.flex_grow / total_grow, item.max_size)
- } else {
- (
- item.flex_shrink * item.base_size.0 as f32 / total_scaled,
- item.min_size,
- )
- };
- let variation = self.free_space.scale_by(factor);
- if variation.0.abs() >= (end_size - item.main_size).0.abs() {
- // Use constraint as the target main size, and freeze item.
- total_variation += end_size - item.main_size;
- item.main_size = end_size;
- item.is_frozen = true;
- active_count -= 1;
- total_shrink -= item.flex_shrink;
- total_grow -= item.flex_grow;
- total_scaled -= item.flex_shrink * item.base_size.0 as f32;
- } else {
- total_variation += variation;
- item.main_size += variation;
- }
- }
- self.free_space -= total_variation;
- }
- }
-}
-
-#[allow(unsafe_code)]
-unsafe impl crate::flow::HasBaseFlow for FlexFlow {}
-
-/// A block with the CSS `display` property equal to `flex`.
-#[derive(Debug, Serialize)]
-#[repr(C)]
-pub struct FlexFlow {
- /// Data common to all block flows.
- block_flow: BlockFlow,
- /// The logical axis which the main axis will be parallel with.
- /// The cross axis will be parallel with the opposite logical axis.
- main_mode: Direction,
- /// The available main axis size
- available_main_size: AxisSize,
- /// The available cross axis size
- available_cross_size: AxisSize,
- /// List of flex lines in the container.
- lines: Vec<FlexLine>,
- /// List of flex-items that belong to this flex-container
- items: Vec<FlexItem>,
- /// True if the flex-direction is *-reversed
- main_reverse: bool,
- /// True if this flex container can be multiline.
- is_wrappable: bool,
- /// True if the cross direction is reversed.
- cross_reverse: bool,
-}
-
-impl FlexFlow {
- pub fn from_fragment(fragment: Fragment, flotation: Option<FloatKind>) -> FlexFlow {
- let main_mode;
- let main_reverse;
- let is_wrappable;
- let cross_reverse;
- {
- let style = fragment.style();
- let (mode, reverse) = match style.get_position().flex_direction {
- FlexDirection::Row => (Direction::Inline, false),
- FlexDirection::RowReverse => (Direction::Inline, true),
- FlexDirection::Column => (Direction::Block, false),
- FlexDirection::ColumnReverse => (Direction::Block, true),
- };
- main_mode = mode;
- main_reverse = reverse == style.writing_mode.is_bidi_ltr();
- let (wrappable, reverse) = match fragment.style.get_position().flex_wrap {
- FlexWrap::Nowrap => (false, false),
- FlexWrap::Wrap => (true, false),
- FlexWrap::WrapReverse => (true, true),
- };
- is_wrappable = wrappable;
- // TODO(stshine): Handle vertical writing mode.
- cross_reverse = reverse;
- }
-
- FlexFlow {
- block_flow: BlockFlow::from_fragment_and_float_kind(fragment, flotation),
- main_mode: main_mode,
- available_main_size: AxisSize::Infinite,
- available_cross_size: AxisSize::Infinite,
- lines: Vec::new(),
- items: Vec::new(),
- main_reverse: main_reverse,
- is_wrappable: is_wrappable,
- cross_reverse: cross_reverse,
- }
- }
-
- pub fn main_mode(&self) -> Direction {
- self.main_mode
- }
-
- /// Returns a line start after the last item that is already in a line.
- /// Note that when the container main size is infinite(i.e. A column flexbox with auto height),
- /// we do not need to do flex resolving and this can be considered as a fast-path, so the
- /// 'container_size' param does not need to be 'None'. A line has to contain at least one item;
- /// (except this) if the container can be multi-line the sum of outer main size of items should
- /// be less than the container size; a line should be filled by items as much as possible.
- /// After been collected in a line a item should have its main sizes initialized.
- fn get_flex_line(&mut self, container_size: Au) -> Option<FlexLine> {
- let start = self.lines.last().map(|line| line.range.end).unwrap_or(0);
- if start == self.items.len() {
- return None;
- }
- let mut end = start;
- let mut total_line_size = Au(0);
- let mut margin_count = 0;
-
- let items = &mut self.items[start..];
- let mut children = self.block_flow.base.children.random_access_mut();
- for item in items {
- let kid = children.get(item.index);
- item.init_sizes(kid, container_size, self.main_mode);
- let outer_main_size = item.outer_main_size(kid, self.main_mode);
- if total_line_size + outer_main_size > container_size &&
- end != start &&
- self.is_wrappable
- {
- break;
- }
- margin_count += item.auto_margin_count(kid, self.main_mode);
- total_line_size += outer_main_size;
- end += 1;
- }
-
- let line = FlexLine::new(start..end, container_size - total_line_size, margin_count);
- Some(line)
- }
-
- // TODO(zentner): This function should use flex-basis.
- // Currently, this is the core of BlockFlow::bubble_inline_sizes() with all float logic
- // stripped out, and max replaced with union_nonbreaking_inline.
- fn inline_mode_bubble_inline_sizes(&mut self) {
- // FIXME(emilio): This doesn't handle at all writing-modes.
- let fixed_width =
- !model::style_length(self.block_flow.fragment.style().get_position().width, None)
- .is_auto();
-
- let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
- if !fixed_width {
- for kid in self.block_flow.base.children.iter_mut() {
- let base = kid.mut_base();
- let is_absolutely_positioned =
- base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
- if !is_absolutely_positioned {
- let flex_item_inline_sizes = IntrinsicISizes {
- minimum_inline_size: base.intrinsic_inline_sizes.minimum_inline_size,
- preferred_inline_size: base.intrinsic_inline_sizes.preferred_inline_size,
- };
- computation.union_nonbreaking_inline(&flex_item_inline_sizes);
- }
- }
- }
- self.block_flow.base.intrinsic_inline_sizes = computation.finish();
- }
-
- // TODO(zentner): This function should use flex-basis.
- // Currently, this is the core of BlockFlow::bubble_inline_sizes() with all float logic
- // stripped out.
- fn block_mode_bubble_inline_sizes(&mut self) {
- let fixed_width =
- !model::style_length(self.block_flow.fragment.style().get_position().width, None)
- .is_auto();
-
- let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
- if !fixed_width {
- for kid in self.block_flow.base.children.iter_mut() {
- let base = kid.mut_base();
- let is_absolutely_positioned =
- base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
- if !is_absolutely_positioned {
- computation.content_intrinsic_sizes.minimum_inline_size = max(
- computation.content_intrinsic_sizes.minimum_inline_size,
- base.intrinsic_inline_sizes.minimum_inline_size,
- );
-
- computation.content_intrinsic_sizes.preferred_inline_size = max(
- computation.content_intrinsic_sizes.preferred_inline_size,
- base.intrinsic_inline_sizes.preferred_inline_size,
- );
- }
- }
- }
- self.block_flow.base.intrinsic_inline_sizes = computation.finish();
- }
-
- // TODO(zentner): This function needs to be radically different for multi-line flexbox.
- // Currently, this is the core of BlockFlow::propagate_assigned_inline_size_to_children() with
- // all float and table logic stripped out.
- fn block_mode_assign_inline_sizes(
- &mut self,
- _layout_context: &LayoutContext,
- inline_start_content_edge: Au,
- inline_end_content_edge: Au,
- content_inline_size: Au,
- ) {
- let _scope = layout_debug_scope!("flex::block_mode_assign_inline_sizes");
- debug!("flex::block_mode_assign_inline_sizes");
-
- // FIXME (mbrubeck): Get correct mode for absolute containing block
- let containing_block_mode = self.block_flow.base.writing_mode;
-
- let container_block_size = match self.available_main_size {
- AxisSize::Definite(length) => Some(length),
- _ => None,
- };
- let container_inline_size = match self.available_cross_size {
- AxisSize::Definite(length) => length,
- AxisSize::MinMax(ref constraint) => constraint.clamp(content_inline_size),
- AxisSize::Infinite => content_inline_size,
- };
-
- let mut children = self.block_flow.base.children.random_access_mut();
- for kid in &mut self.items {
- let kid_base = children.get(kid.index).mut_base();
- kid_base.block_container_explicit_block_size = container_block_size;
- if kid_base
- .flags
- .contains(FlowFlags::INLINE_POSITION_IS_STATIC)
- {
- // The inline-start margin edge of the child flow is at our inline-start content
- // edge, and its inline-size is our content inline-size.
- kid_base.position.start.i =
- if kid_base.writing_mode.is_bidi_ltr() == containing_block_mode.is_bidi_ltr() {
- inline_start_content_edge
- } else {
- // The kid's inline 'start' is at the parent's 'end'
- inline_end_content_edge
- };
- }
- kid_base.block_container_inline_size = container_inline_size;
- kid_base.block_container_writing_mode = containing_block_mode;
- kid_base.position.start.i = inline_start_content_edge;
- }
- }
-
- fn inline_mode_assign_inline_sizes(
- &mut self,
- layout_context: &LayoutContext,
- inline_start_content_edge: Au,
- _inline_end_content_edge: Au,
- content_inline_size: Au,
- ) {
- let _scope = layout_debug_scope!("flex::inline_mode_assign_inline_sizes");
- debug!("inline_mode_assign_inline_sizes");
-
- debug!("content_inline_size = {:?}", content_inline_size);
-
- let child_count = ImmutableFlowUtils::child_count(self as &dyn Flow) as i32;
- debug!("child_count = {:?}", child_count);
- if child_count == 0 {
- return;
- }
-
- let inline_size = match self.available_main_size {
- AxisSize::Definite(length) => length,
- AxisSize::MinMax(ref constraint) => constraint.clamp(content_inline_size),
- AxisSize::Infinite => content_inline_size,
- };
-
- let container_mode = self.block_flow.base.block_container_writing_mode;
- self.block_flow.base.position.size.inline = inline_size;
-
- // Calculate non-auto block size to pass to children.
- let box_border = self
- .block_flow
- .fragment
- .box_sizing_boundary(Direction::Block);
-
- let parent_container_size = self
- .block_flow
- .explicit_block_containing_size(layout_context.shared_context());
- // https://drafts.csswg.org/css-ui-3/#box-sizing
- let explicit_content_size = self
- .block_flow
- .explicit_block_size(parent_container_size)
- .map(|x| max(x - box_border, Au(0)));
- let containing_block_text_align = self
- .block_flow
- .fragment
- .style()
- .get_inherited_text()
- .text_align;
-
- while let Some(mut line) = self.get_flex_line(inline_size) {
- let items = &mut self.items[line.range.clone()];
- line.flex_resolve(items, false);
- // TODO(stshine): if this flex line contain children that have
- // property visibility:collapse, exclude them and resolve again.
-
- let item_count = items.len() as i32;
- let mut cur_i = inline_start_content_edge;
- let item_interval = if line.free_space >= Au(0) && line.auto_margin_count == 0 {
- match self
- .block_flow
- .fragment
- .style()
- .get_position()
- .justify_content
- {
- JustifyContent::SpaceBetween => {
- if item_count == 1 {
- Au(0)
- } else {
- line.free_space / (item_count - 1)
- }
- },
- JustifyContent::SpaceAround => line.free_space / item_count,
- _ => Au(0),
- }
- } else {
- Au(0)
- };
-
- match self
- .block_flow
- .fragment
- .style()
- .get_position()
- .justify_content
- {
- // Overflow equally in both ends of line.
- JustifyContent::Center | JustifyContent::SpaceAround => {
- cur_i += (line.free_space - item_interval * (item_count - 1)) / 2;
- },
- JustifyContent::FlexEnd => {
- cur_i += line.free_space;
- },
- _ => {},
- }
-
- let mut children = self.block_flow.base.children.random_access_mut();
- for item in items.iter_mut() {
- let block = children.get(item.index).as_mut_block();
-
- block.base.block_container_writing_mode = container_mode;
- block.base.block_container_inline_size = inline_size;
- block.base.block_container_explicit_block_size = explicit_content_size;
- // Per CSS 2.1 § 16.3.1, text alignment propagates to all children in flow.
- //
- // TODO(#2265, pcwalton): Do this in the cascade instead.
- block.base.flags.set_text_align(containing_block_text_align);
-
- let margin = block.fragment.style().logical_margin();
- let auto_len = if line.auto_margin_count == 0 || line.free_space <= Au(0) {
- Au(0)
- } else {
- line.free_space / line.auto_margin_count
- };
- let margin_inline_start = MaybeAuto::from_style(margin.inline_start, inline_size)
- .specified_or_default(auto_len);
- let margin_inline_end = MaybeAuto::from_style(margin.inline_end, inline_size)
- .specified_or_default(auto_len);
- let item_inline_size = item.main_size -
- block.fragment.box_sizing_boundary(self.main_mode) +
- block.fragment.border_padding.inline_start_end();
- let item_outer_size = item_inline_size + block.fragment.margin.inline_start_end();
-
- block.fragment.margin.inline_start = margin_inline_start;
- block.fragment.margin.inline_end = margin_inline_end;
- block.fragment.border_box.start.i = margin_inline_start;
- block.fragment.border_box.size.inline = item_inline_size;
- block.base.position.start.i = if !self.main_reverse {
- cur_i
- } else {
- inline_start_content_edge * 2 + content_inline_size - cur_i - item_outer_size
- };
- block.base.position.size.inline = item_outer_size;
- cur_i += item_outer_size + item_interval;
- }
- self.lines.push(line);
- }
- }
-
- // TODO(zentner): This function should actually flex elements!
- fn block_mode_assign_block_size(&mut self) {
- let mut cur_b = if !self.main_reverse {
- self.block_flow.fragment.border_padding.block_start
- } else {
- self.block_flow.fragment.border_box.size.block
- };
-
- let mut children = self.block_flow.base.children.random_access_mut();
- for item in &mut self.items {
- let base = children.get(item.index).mut_base();
- if !self.main_reverse {
- base.position.start.b = cur_b;
- cur_b = cur_b + base.position.size.block;
- } else {
- cur_b = cur_b - base.position.size.block;
- base.position.start.b = cur_b;
- }
- }
- }
-
- fn inline_mode_assign_block_size(&mut self, layout_context: &LayoutContext) {
- let _scope = layout_debug_scope!("flex::inline_mode_assign_block_size");
-
- let line_count = self.lines.len() as i32;
- let line_align = self
- .block_flow
- .fragment
- .style()
- .get_position()
- .align_content;
- let mut cur_b = self.block_flow.fragment.border_padding.block_start;
- let mut total_cross_size = Au(0);
- let mut line_interval = Au(0);
-
- {
- let mut children = self.block_flow.base.children.random_access_mut();
- for line in self.lines.iter_mut() {
- for item in &self.items[line.range.clone()] {
- let fragment = &children.get(item.index).as_block().fragment;
- line.cross_size = max(
- line.cross_size,
- fragment.border_box.size.block + fragment.margin.block_start_end(),
- );
- }
- total_cross_size += line.cross_size;
- }
- }
-
- let box_border = self
- .block_flow
- .fragment
- .box_sizing_boundary(Direction::Block);
- let parent_container_size = self
- .block_flow
- .explicit_block_containing_size(layout_context.shared_context());
- // https://drafts.csswg.org/css-ui-3/#box-sizing
- let explicit_content_size = self
- .block_flow
- .explicit_block_size(parent_container_size)
- .map(|x| max(x - box_border, Au(0)));
-
- if let Some(container_block_size) = explicit_content_size {
- let free_space = container_block_size - total_cross_size;
- total_cross_size = container_block_size;
-
- if line_align == AlignContent::Stretch && free_space > Au(0) {
- for line in self.lines.iter_mut() {
- line.cross_size += free_space / line_count;
- }
- }
-
- line_interval = match line_align {
- AlignContent::SpaceBetween => {
- if line_count <= 1 {
- Au(0)
- } else {
- free_space / (line_count - 1)
- }
- },
- AlignContent::SpaceAround => {
- if line_count == 0 {
- Au(0)
- } else {
- free_space / line_count
- }
- },
- _ => Au(0),
- };
-
- match line_align {
- AlignContent::Center | AlignContent::SpaceAround => {
- cur_b += (free_space - line_interval * (line_count - 1)) / 2;
- },
- AlignContent::FlexEnd => {
- cur_b += free_space;
- },
- _ => {},
- }
- }
-
- let mut children = self.block_flow.base.children.random_access_mut();
- for line in &self.lines {
- for item in self.items[line.range.clone()].iter_mut() {
- let block = children.get(item.index).as_mut_block();
- let auto_margin_count = item.auto_margin_count(block, Direction::Block);
- let margin = block.fragment.style().logical_margin();
-
- let mut margin_block_start = block.fragment.margin.block_start;
- let mut margin_block_end = block.fragment.margin.block_end;
- let mut free_space = line.cross_size -
- block.base.position.size.block -
- block.fragment.margin.block_start_end();
-
- // The spec is a little vague here, but if I understand it correctly, the outer
- // cross size of item should equal to the line size if any auto margin exists.
- // https://drafts.csswg.org/css-flexbox/#algo-cross-margins
- if auto_margin_count > 0 {
- if margin.block_start.is_auto() {
- margin_block_start = if free_space < Au(0) {
- Au(0)
- } else {
- free_space / auto_margin_count
- };
- }
- margin_block_end =
- line.cross_size - margin_block_start - block.base.position.size.block;
- free_space = Au(0);
- }
-
- let self_align = block.fragment.style().get_position().align_self;
- if self_align == AlignSelf::Stretch &&
- block.fragment.style().content_block_size().is_auto()
- {
- free_space = Au(0);
- block.base.block_container_explicit_block_size = Some(line.cross_size);
- block.base.position.size.block =
- line.cross_size - margin_block_start - margin_block_end;
- block.fragment.border_box.size.block = block.base.position.size.block;
- // FIXME(stshine): item with 'align-self: stretch' and auto cross size should act
- // as if it has a fixed cross size, all child blocks should resolve against it.
- // block.assign_block_size(layout_context);
- }
- block.base.position.start.b = margin_block_start +
- if !self.cross_reverse {
- cur_b
- } else {
- self.block_flow.fragment.border_padding.block_start * 2 + total_cross_size -
- cur_b -
- line.cross_size
- };
- // TODO(stshine): support baseline alignment.
- if free_space != Au(0) {
- let flex_cross = match self_align {
- AlignSelf::FlexEnd => free_space,
- AlignSelf::Center => free_space / 2,
- _ => Au(0),
- };
- block.base.position.start.b += if !self.cross_reverse {
- flex_cross
- } else {
- free_space - flex_cross
- };
- }
- }
- cur_b += line_interval + line.cross_size;
- }
- let total_block_size =
- total_cross_size + self.block_flow.fragment.border_padding.block_start_end();
- self.block_flow.fragment.border_box.size.block = total_block_size;
- self.block_flow.base.position.size.block = total_block_size;
- }
-}
-
-impl Flow for FlexFlow {
- fn class(&self) -> FlowClass {
- FlowClass::Flex
- }
-
- fn as_flex(&self) -> &FlexFlow {
- self
- }
-
- fn as_block(&self) -> &BlockFlow {
- &self.block_flow
- }
-
- fn as_mut_block(&mut self) -> &mut BlockFlow {
- &mut self.block_flow
- }
-
- fn mark_as_root(&mut self) {
- self.block_flow.mark_as_root();
- }
-
- fn bubble_inline_sizes(&mut self) {
- let _scope = layout_debug_scope!(
- "flex::bubble_inline_sizes {:x}",
- self.block_flow.base.debug_id()
- );
-
- // Flexbox Section 9.0: Generate anonymous flex items:
- // This part was handled in the flow constructor.
-
- // Flexbox Section 9.1: Re-order flex items according to their order.
- // FIXME(stshine): This should be done during flow construction.
- let mut items: Vec<FlexItem> = self
- .block_flow
- .base
- .children
- .iter()
- .enumerate()
- .filter(|&(_, flow)| {
- !flow
- .as_block()
- .base
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- })
- .map(|(index, flow)| FlexItem::new(index, flow))
- .collect();
-
- items.sort_by_key(|item| item.order);
- self.items = items;
-
- match self.main_mode {
- Direction::Inline => self.inline_mode_bubble_inline_sizes(),
- Direction::Block => self.block_mode_bubble_inline_sizes(),
- }
- }
-
- fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
- let _scope = layout_debug_scope!(
- "flex::assign_inline_sizes {:x}",
- self.block_flow.base.debug_id()
- );
- debug!("assign_inline_sizes");
-
- if !self
- .block_flow
- .base
- .restyle_damage
- .intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW)
- {
- return;
- }
-
- self.block_flow
- .initialize_container_size_for_root(layout_context.shared_context());
-
- // Our inline-size was set to the inline-size of the containing block by the flow's parent.
- // Now compute the real value.
- let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
- self.block_flow.compute_used_inline_size(
- layout_context.shared_context(),
- containing_block_inline_size,
- );
- if self.block_flow.base.flags.is_float() {
- self.block_flow
- .float
- .as_mut()
- .unwrap()
- .containing_inline_size = containing_block_inline_size
- }
-
- let (available_block_size, available_inline_size) = {
- let style = &self.block_flow.fragment.style;
- let (specified_block_size, specified_inline_size) = if style.writing_mode.is_vertical()
- {
- (style.get_position().width, style.get_position().height)
- } else {
- (style.get_position().height, style.get_position().width)
- };
-
- let available_inline_size = AxisSize::new(
- specified_inline_size,
- Some(self.block_flow.base.block_container_inline_size),
- style.min_inline_size(),
- style.max_inline_size(),
- );
-
- let available_block_size = AxisSize::new(
- specified_block_size,
- self.block_flow.base.block_container_explicit_block_size,
- style.min_block_size(),
- style.max_block_size(),
- );
- (available_block_size, available_inline_size)
- };
-
- // Move in from the inline-start border edge.
- let inline_start_content_edge = self.block_flow.fragment.border_box.start.i +
- self.block_flow.fragment.border_padding.inline_start;
-
- debug!(
- "inline_start_content_edge = {:?}",
- inline_start_content_edge
- );
-
- let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
-
- // Distance from the inline-end margin edge to the inline-end content edge.
- let inline_end_content_edge = self.block_flow.fragment.margin.inline_end +
- self.block_flow.fragment.border_padding.inline_end;
-
- debug!("padding_and_borders = {:?}", padding_and_borders);
- debug!(
- "self.block_flow.fragment.border_box.size.inline = {:?}",
- self.block_flow.fragment.border_box.size.inline
- );
- let content_inline_size =
- self.block_flow.fragment.border_box.size.inline - padding_and_borders;
-
- match self.main_mode {
- Direction::Inline => {
- self.available_main_size = available_inline_size;
- self.available_cross_size = available_block_size;
- self.inline_mode_assign_inline_sizes(
- layout_context,
- inline_start_content_edge,
- inline_end_content_edge,
- content_inline_size,
- )
- },
- Direction::Block => {
- self.available_main_size = available_block_size;
- self.available_cross_size = available_inline_size;
- self.block_mode_assign_inline_sizes(
- layout_context,
- inline_start_content_edge,
- inline_end_content_edge,
- content_inline_size,
- )
- },
- }
- }
-
- fn assign_block_size(&mut self, layout_context: &LayoutContext) {
- match self.main_mode {
- Direction::Inline => {
- self.inline_mode_assign_block_size(layout_context);
- let block_start =
- AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_start);
- let block_end =
- AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_end);
- self.block_flow.base.collapsible_margins =
- CollapsibleMargins::Collapse(block_start, block_end);
-
- // TODO(stshine): assign proper static position for absolute descendants.
- if (&*self as &dyn Flow).contains_roots_of_absolute_flow_tree() {
- // Assign block-sizes for all flows in this absolute flow tree.
- // This is preorder because the block-size of an absolute flow may depend on
- // the block-size of its containing block, which may also be an absolute flow.
- let assign_abs_b_sizes =
- AbsoluteAssignBSizesTraversal(layout_context.shared_context());
- assign_abs_b_sizes.traverse_absolute_flows(&mut *self);
- }
- },
- Direction::Block => {
- self.block_flow.assign_block_size_block_base(
- layout_context,
- None,
- MarginsMayCollapseFlag::MarginsMayNotCollapse,
- );
- self.block_mode_assign_block_size();
- },
- }
- }
-
- fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
- self.block_flow
- .compute_stacking_relative_position(layout_context)
- }
-
- fn place_float_if_applicable<'a>(&mut self) {
- self.block_flow.place_float_if_applicable()
- }
-
- fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
- self.block_flow
- .update_late_computed_inline_position_if_necessary(inline_position)
- }
-
- fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
- self.block_flow
- .update_late_computed_block_position_if_necessary(block_position)
- }
-
- fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
- // Draw the rest of the block.
- self.as_mut_block()
- .build_display_list_for_block(state, BorderPaintingMode::Separate)
- }
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
- self.block_flow.collect_stacking_contexts(state);
- }
-
- fn repair_style(&mut self, new_style: &crate::ServoArc<ComputedValues>) {
- self.block_flow.repair_style(new_style)
- }
-
- fn compute_overflow(&self) -> Overflow {
- self.block_flow.compute_overflow()
- }
-
- fn contains_roots_of_absolute_flow_tree(&self) -> bool {
- self.block_flow.contains_roots_of_absolute_flow_tree()
- }
-
- fn is_absolute_containing_block(&self) -> bool {
- self.block_flow.is_absolute_containing_block()
- }
-
- fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
- self.block_flow.generated_containing_block_size(flow)
- }
-
- fn iterate_through_fragment_border_boxes(
- &self,
- iterator: &mut dyn FragmentBorderBoxIterator,
- level: i32,
- stacking_context_position: &Point2D<Au>,
- ) {
- self.block_flow.iterate_through_fragment_border_boxes(
- iterator,
- level,
- stacking_context_position,
- );
- }
-
- fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
- self.block_flow.mutate_fragments(mutator);
- }
-}
diff --git a/components/layout_2020/floats.rs b/components/layout_2020/floats.rs
deleted file mode 100644
index 42df4e07dfd..00000000000
--- a/components/layout_2020/floats.rs
+++ /dev/null
@@ -1,605 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::block::FormattingContextType;
-use crate::flow::{Flow, FlowFlags, GetBaseFlow, ImmutableFlowUtils};
-use crate::persistent_list::PersistentList;
-use app_units::{Au, MAX_AU};
-use std::cmp::{max, min};
-use std::fmt;
-use style::computed_values::float::T as StyleFloat;
-use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
-use style::values::computed::Size;
-
-/// The kind of float: left or right.
-#[derive(Clone, Copy, Debug, Serialize)]
-pub enum FloatKind {
- Left,
- Right,
-}
-
-impl FloatKind {
- pub fn from_property(property: StyleFloat) -> Option<FloatKind> {
- match property {
- StyleFloat::None => None,
- StyleFloat::Left => Some(FloatKind::Left),
- StyleFloat::Right => Some(FloatKind::Right),
- }
- }
-}
-
-/// The kind of clearance: left, right, or both.
-#[derive(Clone, Copy)]
-pub enum ClearType {
- Left,
- Right,
- Both,
-}
-
-/// Information about a single float.
-#[derive(Clone, Copy)]
-struct Float {
- /// The boundaries of this float.
- bounds: LogicalRect<Au>,
- /// The kind of float: left or right.
- kind: FloatKind,
-}
-
-impl fmt::Debug for Float {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "bounds={:?} kind={:?}", self.bounds, self.kind)
- }
-}
-
-/// Information about the floats next to a flow.
-#[derive(Clone)]
-struct FloatList {
- /// Information about each of the floats here.
- floats: PersistentList<Float>,
- /// Cached copy of the maximum block-start offset of the float.
- max_block_start: Option<Au>,
-}
-
-impl FloatList {
- fn new() -> FloatList {
- FloatList {
- floats: PersistentList::new(),
- max_block_start: None,
- }
- }
-
- /// Returns true if the list is allocated and false otherwise. If false, there are guaranteed
- /// not to be any floats.
- fn is_present(&self) -> bool {
- self.floats.len() > 0
- }
-}
-
-impl fmt::Debug for FloatList {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "max_block_start={:?} floats={}",
- self.max_block_start,
- self.floats.len()
- )?;
- for float in self.floats.iter() {
- write!(f, " {:?}", float)?;
- }
- Ok(())
- }
-}
-
-/// All the information necessary to place a float.
-pub struct PlacementInfo {
- /// The dimensions of the float.
- pub size: LogicalSize<Au>,
- /// The minimum block-start of the float, as determined by earlier elements.
- pub ceiling: Au,
- /// The maximum inline-end position of the float, generally determined by the containing block.
- pub max_inline_size: Au,
- /// The kind of float.
- pub kind: FloatKind,
-}
-
-impl fmt::Debug for PlacementInfo {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "size={:?} ceiling={:?} max_inline_size={:?} kind={:?}",
- self.size, self.ceiling, self.max_inline_size, self.kind
- )
- }
-}
-
-fn range_intersect(
- block_start_1: Au,
- block_end_1: Au,
- block_start_2: Au,
- block_end_2: Au,
-) -> (Au, Au) {
- (
- max(block_start_1, block_start_2),
- min(block_end_1, block_end_2),
- )
-}
-
-/// Encapsulates information about floats. This is optimized to avoid allocation if there are
-/// no floats, and to avoid copying when translating the list of floats downward.
-#[derive(Clone)]
-pub struct Floats {
- /// The list of floats.
- list: FloatList,
- /// The offset of the flow relative to the first float.
- offset: LogicalSize<Au>,
- /// The writing mode of these floats.
- pub writing_mode: WritingMode,
-}
-
-impl fmt::Debug for Floats {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if !self.list.is_present() {
- write!(f, "[empty]")
- } else {
- write!(f, "offset={:?} floats={:?}", self.offset, self.list)
- }
- }
-}
-
-impl Floats {
- /// Creates a new `Floats` object.
- pub fn new(writing_mode: WritingMode) -> Floats {
- Floats {
- list: FloatList::new(),
- offset: LogicalSize::zero(writing_mode),
- writing_mode: writing_mode,
- }
- }
-
- /// Adjusts the recorded offset of the flow relative to the first float.
- pub fn translate(&mut self, delta: LogicalSize<Au>) {
- self.offset = self.offset + delta
- }
-
- /// Returns the position of the last float in flow coordinates.
- pub fn last_float_pos(&self) -> Option<LogicalRect<Au>> {
- match self.list.floats.front() {
- None => None,
- Some(float) => Some(float.bounds.translate_by_size(self.offset)),
- }
- }
-
- /// Returns a rectangle that encloses the region from block-start to block-start + block-size,
- /// with inline-size small enough that it doesn't collide with any floats. max_x is the
- /// inline-size beyond which floats have no effect. (Generally this is the containing block
- /// inline-size.)
- pub fn available_rect(
- &self,
- block_start: Au,
- block_size: Au,
- max_x: Au,
- ) -> Option<LogicalRect<Au>> {
- let list = &self.list;
- let block_start = block_start - self.offset.block;
-
- debug!("available_rect: trying to find space at {:?}", block_start);
-
- // Relevant dimensions for the inline-end-most inline-start float
- let mut max_inline_start = Au(0) - self.offset.inline;
- let mut l_block_start = None;
- let mut l_block_end = None;
- // Relevant dimensions for the inline-start-most inline-end float
- let mut min_inline_end = max_x - self.offset.inline;
- let mut r_block_start = None;
- let mut r_block_end = None;
-
- // Find the float collisions for the given range in the block direction.
- for float in list.floats.iter() {
- debug!("available_rect: Checking for collision against float");
- let float_pos = float.bounds.start;
- let float_size = float.bounds.size;
-
- debug!("float_pos: {:?}, float_size: {:?}", float_pos, float_size);
- match float.kind {
- FloatKind::Left
- if float_pos.i + float_size.inline > max_inline_start &&
- float_pos.b + float_size.block > block_start &&
- float_pos.b < block_start + block_size =>
- {
- max_inline_start = float_pos.i + float_size.inline;
-
- l_block_start = Some(float_pos.b);
- l_block_end = Some(float_pos.b + float_size.block);
-
- debug!(
- "available_rect: collision with inline_start float: new \
- max_inline_start is {:?}",
- max_inline_start
- );
- }
- FloatKind::Right
- if float_pos.i < min_inline_end &&
- float_pos.b + float_size.block > block_start &&
- float_pos.b < block_start + block_size =>
- {
- min_inline_end = float_pos.i;
-
- r_block_start = Some(float_pos.b);
- r_block_end = Some(float_pos.b + float_size.block);
- debug!(
- "available_rect: collision with inline_end float: new min_inline_end \
- is {:?}",
- min_inline_end
- );
- }
- FloatKind::Left | FloatKind::Right => {},
- }
- }
-
- // Extend the vertical range of the rectangle to the closest floats.
- // If there are floats on both sides, take the intersection of the
- // two areas. Also make sure we never return a block-start smaller than the
- // given upper bound.
- let (block_start, block_end) =
- match (r_block_start, r_block_end, l_block_start, l_block_end) {
- (
- Some(r_block_start),
- Some(r_block_end),
- Some(l_block_start),
- Some(l_block_end),
- ) => range_intersect(
- max(block_start, r_block_start),
- r_block_end,
- max(block_start, l_block_start),
- l_block_end,
- ),
- (None, None, Some(l_block_start), Some(l_block_end)) => {
- (max(block_start, l_block_start), l_block_end)
- },
- (Some(r_block_start), Some(r_block_end), None, None) => {
- (max(block_start, r_block_start), r_block_end)
- },
- (None, None, None, None) => return None,
- _ => panic!("Reached unreachable state when computing float area"),
- };
-
- // FIXME(eatkinson): This assertion is too strong and fails in some cases. It is OK to
- // return negative inline-sizes since we check against that inline-end away, but we should
- // still understand why they occur and add a stronger assertion here.
- // assert!(max_inline-start < min_inline-end);
-
- assert!(block_start <= block_end, "Float position error");
-
- Some(LogicalRect::new(
- self.writing_mode,
- max_inline_start + self.offset.inline,
- block_start + self.offset.block,
- min_inline_end - max_inline_start,
- block_end - block_start,
- ))
- }
-
- /// Adds a new float to the list.
- pub fn add_float(&mut self, info: &PlacementInfo) {
- let new_info = PlacementInfo {
- size: info.size,
- ceiling: match self.list.max_block_start {
- None => info.ceiling,
- Some(max_block_start) => max(info.ceiling, max_block_start + self.offset.block),
- },
- max_inline_size: info.max_inline_size,
- kind: info.kind,
- };
-
- debug!("add_float: added float with info {:?}", new_info);
-
- let new_float = Float {
- bounds: LogicalRect::from_point_size(
- self.writing_mode,
- self.place_between_floats(&new_info).start - self.offset,
- info.size,
- ),
- kind: info.kind,
- };
-
- self.list.floats = self.list.floats.prepend_elem(new_float);
- self.list.max_block_start = match self.list.max_block_start {
- None => Some(new_float.bounds.start.b),
- Some(max_block_start) => Some(max(max_block_start, new_float.bounds.start.b)),
- }
- }
-
- /// Given the three sides of the bounding rectangle in the block-start direction, finds the
- /// largest block-size that will result in the rectangle not colliding with any floats. Returns
- /// `None` if that block-size is infinite.
- fn max_block_size_for_bounds(
- &self,
- inline_start: Au,
- block_start: Au,
- inline_size: Au,
- ) -> Option<Au> {
- let list = &self.list;
-
- let block_start = block_start - self.offset.block;
- let inline_start = inline_start - self.offset.inline;
- let mut max_block_size = None;
-
- for float in list.floats.iter() {
- if float.bounds.start.b + float.bounds.size.block > block_start &&
- float.bounds.start.i + float.bounds.size.inline > inline_start &&
- float.bounds.start.i < inline_start + inline_size
- {
- let new_y = float.bounds.start.b;
- max_block_size = Some(min(max_block_size.unwrap_or(new_y), new_y));
- }
- }
-
- max_block_size.map(|h| h + self.offset.block)
- }
-
- /// Given placement information, finds the closest place a fragment can be positioned without
- /// colliding with any floats.
- pub fn place_between_floats(&self, info: &PlacementInfo) -> LogicalRect<Au> {
- debug!("place_between_floats: Placing object with {:?}", info.size);
-
- // If no floats, use this fast path.
- if !self.list.is_present() {
- match info.kind {
- FloatKind::Left => {
- return LogicalRect::new(
- self.writing_mode,
- Au(0),
- info.ceiling,
- info.max_inline_size,
- MAX_AU,
- );
- },
- FloatKind::Right => {
- return LogicalRect::new(
- self.writing_mode,
- info.max_inline_size - info.size.inline,
- info.ceiling,
- info.max_inline_size,
- MAX_AU,
- );
- },
- }
- }
-
- // Can't go any higher than previous floats or previous elements in the document.
- let mut float_b = info.ceiling;
- loop {
- let maybe_location =
- self.available_rect(float_b, info.size.block, info.max_inline_size);
- debug!(
- "place_float: got available rect: {:?} for block-pos: {:?}",
- maybe_location, float_b
- );
- match maybe_location {
- // If there are no floats blocking us, return the current location
- // TODO(eatkinson): integrate with overflow
- None => {
- return match info.kind {
- FloatKind::Left => LogicalRect::new(
- self.writing_mode,
- Au(0),
- float_b,
- info.max_inline_size,
- MAX_AU,
- ),
- FloatKind::Right => LogicalRect::new(
- self.writing_mode,
- info.max_inline_size - info.size.inline,
- float_b,
- info.max_inline_size,
- MAX_AU,
- ),
- };
- },
- Some(rect) => {
- assert_ne!(
- rect.start.b + rect.size.block,
- float_b,
- "Non-terminating float placement"
- );
-
- // Place here if there is enough room
- if rect.size.inline >= info.size.inline {
- let block_size = self.max_block_size_for_bounds(
- rect.start.i,
- rect.start.b,
- rect.size.inline,
- );
- let block_size = block_size.unwrap_or(MAX_AU);
- return match info.kind {
- FloatKind::Left => LogicalRect::new(
- self.writing_mode,
- rect.start.i,
- float_b,
- rect.size.inline,
- block_size,
- ),
- FloatKind::Right => LogicalRect::new(
- self.writing_mode,
- rect.start.i + rect.size.inline - info.size.inline,
- float_b,
- rect.size.inline,
- block_size,
- ),
- };
- }
-
- // Try to place at the next-lowest location.
- // Need to be careful of fencepost errors.
- float_b = rect.start.b + rect.size.block;
- },
- }
- }
- }
-
- pub fn clearance(&self, clear: ClearType) -> Au {
- let list = &self.list;
- let mut clearance = Au(0);
- for float in list.floats.iter() {
- match (clear, float.kind) {
- (ClearType::Left, FloatKind::Left) |
- (ClearType::Right, FloatKind::Right) |
- (ClearType::Both, _) => {
- let b = self.offset.block + float.bounds.start.b + float.bounds.size.block;
- clearance = max(clearance, b);
- },
- _ => {},
- }
- }
- clearance
- }
-
- pub fn is_present(&self) -> bool {
- self.list.is_present()
- }
-}
-
-/// The speculated inline sizes of floats flowing through or around a flow (depending on whether
-/// the flow is a block formatting context). These speculations are always *upper bounds*; the
-/// actual inline sizes might be less. Note that this implies that a speculated value of zero is a
-/// guarantee that there will be no floats on that side.
-///
-/// This is used for two purposes: (a) determining whether we can lay out blocks in parallel; (b)
-/// guessing the inline-sizes of block formatting contexts in an effort to lay them out in
-/// parallel.
-#[derive(Clone, Copy)]
-pub struct SpeculatedFloatPlacement {
- /// The estimated inline size (an upper bound) of the left floats flowing through this flow.
- pub left: Au,
- /// The estimated inline size (an upper bound) of the right floats flowing through this flow.
- pub right: Au,
-}
-
-impl fmt::Debug for SpeculatedFloatPlacement {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "L {:?} R {:?}", self.left, self.right)
- }
-}
-
-impl SpeculatedFloatPlacement {
- /// Returns a `SpeculatedFloatPlacement` objects with both left and right speculated inline
- /// sizes initialized to zero.
- pub fn zero() -> SpeculatedFloatPlacement {
- SpeculatedFloatPlacement {
- left: Au(0),
- right: Au(0),
- }
- }
-
- /// Given the speculated inline size of the floats out for the inorder predecessor of this
- /// flow, computes the speculated inline size of the floats flowing in.
- pub fn compute_floats_in(&mut self, flow: &mut dyn Flow) {
- let base_flow = flow.base();
- if base_flow.flags.contains(FlowFlags::CLEARS_LEFT) {
- self.left = Au(0)
- }
- if base_flow.flags.contains(FlowFlags::CLEARS_RIGHT) {
- self.right = Au(0)
- }
- }
-
- /// Given the speculated inline size of the floats out for this flow's last child, computes the
- /// speculated inline size of the floats out for this flow.
- pub fn compute_floats_out(&mut self, flow: &mut dyn Flow) {
- if flow.is_block_like() {
- let block_flow = flow.as_block();
- if block_flow.formatting_context_type() != FormattingContextType::None {
- *self = block_flow.base.speculated_float_placement_in;
- } else {
- if self.left > Au(0) || self.right > Au(0) {
- let speculated_inline_content_edge_offsets =
- block_flow.fragment.guess_inline_content_edge_offsets();
- if self.left > Au(0) && speculated_inline_content_edge_offsets.start > Au(0) {
- self.left = self.left + speculated_inline_content_edge_offsets.start
- }
- if self.right > Au(0) && speculated_inline_content_edge_offsets.end > Au(0) {
- self.right = self.right + speculated_inline_content_edge_offsets.end
- }
- }
-
- self.left = max(
- self.left,
- block_flow.base.speculated_float_placement_in.left,
- );
- self.right = max(
- self.right,
- block_flow.base.speculated_float_placement_in.right,
- );
- }
- }
-
- let base_flow = flow.base();
- if !base_flow.flags.is_float() {
- return;
- }
-
- let mut float_inline_size = base_flow.intrinsic_inline_sizes.preferred_inline_size;
- if float_inline_size == Au(0) {
- if flow.is_block_like() {
- // Hack: If the size of the float is not fixed, then there's no
- // way we can guess at its size now. So just pick an arbitrary
- // nonzero value (in this case, 1px) so that the layout
- // traversal logic will know that objects later in the document
- // might flow around this float.
- let inline_size = flow.as_block().fragment.style.content_inline_size();
- let fixed = match inline_size {
- Size::Auto => false,
- Size::LengthPercentage(ref lp) => {
- lp.0.is_definitely_zero() || lp.0.maybe_to_used_value(None).is_some()
- },
- };
- if !fixed {
- float_inline_size = Au::from_px(1)
- }
- }
- }
-
- match base_flow.flags.float_kind() {
- StyleFloat::None => {},
- StyleFloat::Left => self.left = self.left + float_inline_size,
- StyleFloat::Right => self.right = self.right + float_inline_size,
- }
- }
-
- /// Given a flow, computes the speculated inline size of the floats in of its first child.
- pub fn compute_floats_in_for_first_child(
- parent_flow: &mut dyn Flow,
- ) -> SpeculatedFloatPlacement {
- if !parent_flow.is_block_like() {
- return parent_flow.base().speculated_float_placement_in;
- }
-
- let parent_block_flow = parent_flow.as_block();
- if parent_block_flow.formatting_context_type() != FormattingContextType::None {
- return SpeculatedFloatPlacement::zero();
- }
-
- let mut placement = parent_block_flow.base.speculated_float_placement_in;
- let speculated_inline_content_edge_offsets = parent_block_flow
- .fragment
- .guess_inline_content_edge_offsets();
-
- if speculated_inline_content_edge_offsets.start > Au(0) {
- placement.left = if placement.left > speculated_inline_content_edge_offsets.start {
- placement.left - speculated_inline_content_edge_offsets.start
- } else {
- Au(0)
- }
- }
- if speculated_inline_content_edge_offsets.end > Au(0) {
- placement.right = if placement.right > speculated_inline_content_edge_offsets.end {
- placement.right - speculated_inline_content_edge_offsets.end
- } else {
- Au(0)
- }
- }
-
- placement
- }
-}
diff --git a/components/layout_2020/flow.rs b/components/layout_2020/flow.rs
index d7ab73cd8d3..4bf5e1e899f 100644
--- a/components/layout_2020/flow.rs
+++ b/components/layout_2020/flow.rs
@@ -2,80 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-//! Servo's experimental layout system builds a tree of `Flow` and `Fragment` objects and solves
-//! layout constraints to obtain positions and display attributes of tree nodes. Positions are
-//! computed in several tree traversals driven by the fundamental data dependencies required by
-//! inline and block layout.
-//!
-//! Flows are interior nodes in the layout tree and correspond closely to *flow contexts* in the
-//! CSS specification. Flows are responsible for positioning their child flow contexts and
-//! fragments. Flows have purpose-specific fields, such as auxiliary line structs, out-of-flow
-//! child lists, and so on.
-//!
-//! Currently, the important types of flows are:
-//!
-//! * `BlockFlow`: A flow that establishes a block context. It has several child flows, each of
-//! which are positioned according to block formatting context rules (CSS block boxes). Block
-//! flows also contain a single box to represent their rendered borders, padding, etc.
-//! The BlockFlow at the root of the tree has special behavior: it stretches to the boundaries of
-//! the viewport.
-//!
-//! * `InlineFlow`: A flow that establishes an inline context. It has a flat list of child
-//! fragments/flows that are subject to inline layout and line breaking and structs to represent
-//! line breaks and mapping to CSS boxes, for the purpose of handling `getClientRects()` and
-//! similar methods.
-
-use crate::block::{BlockFlow, FormattingContextType};
-use crate::context::LayoutContext;
-use crate::display_list::items::ClippingAndScrolling;
-use crate::display_list::{DisplayListBuildState, StackingContextCollectionState};
-use crate::flex::FlexFlow;
-use crate::floats::{Floats, SpeculatedFloatPlacement};
-use crate::flow_list::{FlowList, FlowListIterator, MutFlowListIterator};
-use crate::flow_ref::{FlowRef, WeakFlowRef};
-use crate::fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow};
-use crate::inline::InlineFlow;
-use crate::model::{CollapsibleMargins, IntrinsicISizes};
-use crate::parallel::FlowParallelInfo;
-use crate::table::TableFlow;
-use crate::table_cell::TableCellFlow;
-use crate::table_colgroup::TableColGroupFlow;
-use crate::table_row::TableRowFlow;
-use crate::table_rowgroup::TableRowGroupFlow;
-use crate::table_wrapper::TableWrapperFlow;
-use app_units::Au;
-use euclid::{Point2D, Rect, Size2D, Vector2D};
-use gfx_traits::print_tree::PrintTree;
-use gfx_traits::StackingContextId;
-use num_traits::cast::FromPrimitive;
-use serde::ser::{Serialize, SerializeStruct, Serializer};
-use servo_geometry::{au_rect_to_f32_rect, f32_rect_to_au_rect, MaxRect};
use std::fmt;
-use std::slice::IterMut;
-use std::sync::atomic::Ordering;
-use std::sync::Arc;
-use style::computed_values::clear::T as Clear;
-use style::computed_values::float::T as Float;
-use style::computed_values::overflow_x::T as StyleOverflow;
-use style::computed_values::position::T as Position;
-use style::computed_values::text_align::T as TextAlign;
-use style::context::SharedStyleContext;
-use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
-use style::properties::ComputedValues;
use style::selector_parser::RestyleDamage;
-use style::servo::restyle_damage::ServoRestyleDamage;
-use style::values::computed::LengthPercentageOrAuto;
-use webrender_api::units::LayoutTransform;
-/// This marker trait indicates that a type is a struct with `#[repr(C)]` whose first field
-/// is of type `BaseFlow` or some type that also implements this trait.
-///
-/// In other words, the memory representation of `BaseFlow` must be a prefix
-/// of the memory representation of types implementing `HasBaseFlow`.
#[allow(unsafe_code)]
pub unsafe trait HasBaseFlow {}
-/// Methods to get the `BaseFlow` from any `HasBaseFlow` type.
pub trait GetBaseFlow {
fn base(&self) -> &BaseFlow;
fn mut_base(&mut self) -> &mut BaseFlow;
@@ -99,1360 +31,8 @@ impl<T: HasBaseFlow + ?Sized> GetBaseFlow for T {
}
}
-/// Virtual methods that make up a float context.
-///
-/// Note that virtual methods have a cost; we should not overuse them in Servo. Consider adding
-/// methods to `ImmutableFlowUtils` or `MutableFlowUtils` before adding more methods here.
-pub trait Flow: HasBaseFlow + fmt::Debug + Sync + Send + 'static {
- // RTTI
- //
- // TODO(pcwalton): Use Rust's RTTI, once that works.
-
- /// Returns the class of flow that this is.
- fn class(&self) -> FlowClass;
-
- /// If this is a block flow, returns the underlying object. Fails otherwise.
- fn as_block(&self) -> &BlockFlow {
- panic!("called as_block() on a non-block flow")
- }
-
- /// If this is a block flow, returns the underlying object, borrowed mutably. Fails otherwise.
- fn as_mut_block(&mut self) -> &mut BlockFlow {
- debug!("called as_mut_block() on a flow of type {:?}", self.class());
- panic!("called as_mut_block() on a non-block flow")
- }
-
- /// If this is a flex flow, returns the underlying object. Fails otherwise.
- fn as_flex(&self) -> &FlexFlow {
- panic!("called as_flex() on a non-flex flow")
- }
-
- /// If this is an inline flow, returns the underlying object. Fails otherwise.
- fn as_inline(&self) -> &InlineFlow {
- panic!("called as_inline() on a non-inline flow")
- }
-
- /// If this is an inline flow, returns the underlying object, borrowed mutably. Fails
- /// otherwise.
- fn as_mut_inline(&mut self) -> &mut InlineFlow {
- panic!("called as_mut_inline() on a non-inline flow")
- }
-
- /// If this is a table wrapper flow, returns the underlying object. Fails otherwise.
- fn as_table_wrapper(&self) -> &TableWrapperFlow {
- panic!("called as_table_wrapper() on a non-tablewrapper flow")
- }
-
- /// If this is a table flow, returns the underlying object, borrowed mutably. Fails otherwise.
- fn as_mut_table(&mut self) -> &mut TableFlow {
- panic!("called as_mut_table() on a non-table flow")
- }
-
- /// If this is a table flow, returns the underlying object. Fails otherwise.
- fn as_table(&self) -> &TableFlow {
- panic!("called as_table() on a non-table flow")
- }
-
- /// If this is a table colgroup flow, returns the underlying object, borrowed mutably. Fails
- /// otherwise.
- fn as_mut_table_colgroup(&mut self) -> &mut TableColGroupFlow {
- panic!("called as_mut_table_colgroup() on a non-tablecolgroup flow")
- }
-
- /// If this is a table colgroup flow, returns the underlying object. Fails
- /// otherwise.
- fn as_table_colgroup(&self) -> &TableColGroupFlow {
- panic!("called as_table_colgroup() on a non-tablecolgroup flow")
- }
-
- /// If this is a table rowgroup flow, returns the underlying object, borrowed mutably. Fails
- /// otherwise.
- fn as_mut_table_rowgroup(&mut self) -> &mut TableRowGroupFlow {
- panic!("called as_mut_table_rowgroup() on a non-tablerowgroup flow")
- }
-
- /// If this is a table rowgroup flow, returns the underlying object. Fails otherwise.
- fn as_table_rowgroup(&self) -> &TableRowGroupFlow {
- panic!("called as_table_rowgroup() on a non-tablerowgroup flow")
- }
-
- /// If this is a table row flow, returns the underlying object, borrowed mutably. Fails
- /// otherwise.
- fn as_mut_table_row(&mut self) -> &mut TableRowFlow {
- panic!("called as_mut_table_row() on a non-tablerow flow")
- }
-
- /// If this is a table row flow, returns the underlying object. Fails otherwise.
- fn as_table_row(&self) -> &TableRowFlow {
- panic!("called as_table_row() on a non-tablerow flow")
- }
-
- /// If this is a table cell flow, returns the underlying object, borrowed mutably. Fails
- /// otherwise.
- fn as_mut_table_cell(&mut self) -> &mut TableCellFlow {
- panic!("called as_mut_table_cell() on a non-tablecell flow")
- }
-
- /// If this is a table cell flow, returns the underlying object. Fails otherwise.
- fn as_table_cell(&self) -> &TableCellFlow {
- panic!("called as_table_cell() on a non-tablecell flow")
- }
-
- // Main methods
-
- /// Pass 1 of reflow: computes minimum and preferred inline-sizes.
- ///
- /// Recursively (bottom-up) determine the flow's minimum and preferred inline-sizes. When
- /// called on this flow, all child flows have had their minimum and preferred inline-sizes set.
- /// This function must decide minimum/preferred inline-sizes based on its children's inline-
- /// sizes and the dimensions of any boxes it is responsible for flowing.
- fn bubble_inline_sizes(&mut self) {
- panic!("bubble_inline_sizes not yet implemented")
- }
-
- /// Pass 2 of reflow: computes inline-size.
- fn assign_inline_sizes(&mut self, _ctx: &LayoutContext) {
- panic!("assign_inline_sizes not yet implemented")
- }
-
- /// Pass 3a of reflow: computes block-size.
- fn assign_block_size(&mut self, _ctx: &LayoutContext) {
- panic!("assign_block_size not yet implemented")
- }
-
- /// Like `assign_block_size`, but is recurses explicitly into descendants.
- /// Fit as much content as possible within `available_block_size`.
- /// If that’s not all of it, truncate the contents of `self`
- /// and return a new flow similar to `self` with the rest of the content.
- ///
- /// The default is to make a flow "atomic": it can not be fragmented.
- fn fragment(
- &mut self,
- layout_context: &LayoutContext,
- _fragmentation_context: Option<FragmentationContext>,
- ) -> Option<Arc<dyn Flow>> {
- fn recursive_assign_block_size<F: ?Sized + Flow + GetBaseFlow>(
- flow: &mut F,
- ctx: &LayoutContext,
- ) {
- for child in flow.mut_base().child_iter_mut() {
- recursive_assign_block_size(child, ctx)
- }
- flow.assign_block_size(ctx);
- }
- recursive_assign_block_size(self, layout_context);
- None
- }
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState);
-
- /// If this is a float, places it. The default implementation does nothing.
- fn place_float_if_applicable<'a>(&mut self) {}
-
- /// Assigns block-sizes in-order; or, if this is a float, places the float. The default
- /// implementation simply assigns block-sizes if this flow might have floats in. Returns true
- /// if it was determined that this child might have had floats in or false otherwise.
- ///
- /// `parent_thread_id` is the thread ID of the parent. This is used for the layout tinting
- /// debug mode; if the block size of this flow was determined by its parent, we should treat
- /// it as laid out by its parent.
- fn assign_block_size_for_inorder_child_if_necessary(
- &mut self,
- layout_context: &LayoutContext,
- parent_thread_id: u8,
- _content_box: LogicalRect<Au>,
- ) -> bool {
- let might_have_floats_in_or_out =
- self.base().might_have_floats_in() || self.base().might_have_floats_out();
- if might_have_floats_in_or_out {
- self.mut_base().thread_id = parent_thread_id;
- self.assign_block_size(layout_context);
- self.mut_base()
- .restyle_damage
- .remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
- }
- 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 = self
- .base()
- .position
- .to_physical(self.base().writing_mode, container_size);
-
- let mut overflow = self.base().overflow;
-
- match self.class() {
- FlowClass::Block | FlowClass::TableCaption | FlowClass::TableCell => {},
- _ => {
- overflow.translate(&position.origin.to_vector());
- return overflow;
- },
- }
-
- let border_box = self.as_block().fragment.stacking_relative_border_box(
- &self.base().stacking_relative_position,
- &self
- .base()
- .early_absolute_position_info
- .relative_containing_block_size,
- self.base()
- .early_absolute_position_info
- .relative_containing_block_mode,
- CoordinateSystem::Own,
- );
- if StyleOverflow::Visible != self.as_block().fragment.style.get_box().overflow_x {
- overflow.paint.origin.x = Au(0);
- overflow.paint.size.width = border_box.size.width;
- overflow.scroll.origin.x = Au(0);
- overflow.scroll.size.width = border_box.size.width;
- }
- if StyleOverflow::Visible != self.as_block().fragment.style.get_box().overflow_y {
- overflow.paint.origin.y = Au(0);
- overflow.paint.size.height = border_box.size.height;
- overflow.scroll.origin.y = Au(0);
- overflow.scroll.size.height = border_box.size.height;
- }
-
- if !self.as_block().fragment.establishes_stacking_context() ||
- self.as_block()
- .fragment
- .style
- .get_box()
- .transform
- .0
- .is_empty()
- {
- overflow.translate(&position.origin.to_vector());
- 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)
- .unwrap_or(LayoutTransform::identity())
- .to_2d()
- .to_untyped();
- 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.to_vector());
- overflow
- }
-
- ///
- /// CSS Section 11.1
- /// This is the union of rectangles of the flows for which we define the
- /// Containing Block.
- ///
- /// FIXME(pcwalton): This should not be a virtual method, but currently is due to a compiler
- /// bug ("the trait `Sized` is not implemented for `self`").
- ///
- /// Assumption: This is called in a bottom-up traversal, so kids' overflows have
- /// already been set.
- /// Assumption: Absolute descendants have had their overflow calculated.
- fn store_overflow(&mut self, _: &LayoutContext) {
- // Calculate overflow on a per-fragment basis.
- let mut overflow = self.compute_overflow();
- match self.class() {
- FlowClass::Block | FlowClass::TableCaption | FlowClass::TableCell => {
- for kid in self.mut_base().children.iter_mut() {
- overflow.union(&kid.get_overflow_in_parent_coordinates());
- }
- },
- _ => {},
- }
- self.mut_base().overflow = overflow
- }
-
- /// Phase 4 of reflow: Compute the stacking-relative position (origin of the content box,
- /// in coordinates relative to the nearest ancestor stacking context).
- fn compute_stacking_relative_position(&mut self, _: &LayoutContext) {
- // The default implementation is a no-op.
- }
-
- /// Phase 5 of reflow: builds display lists.
- fn build_display_list(&mut self, state: &mut DisplayListBuildState);
-
- /// Returns the union of all overflow rects of all of this flow's fragments.
- fn compute_overflow(&self) -> Overflow;
-
- /// Iterates through border boxes of all of this flow's fragments.
- /// Level provides a zero based index indicating the current
- /// depth of the flow tree during fragment iteration.
- fn iterate_through_fragment_border_boxes(
- &self,
- iterator: &mut dyn FragmentBorderBoxIterator,
- level: i32,
- stacking_context_position: &Point2D<Au>,
- );
-
- /// Mutably iterates through fragments in this flow.
- fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment));
-
- /// Marks this flow as the root flow. The default implementation is a no-op.
- fn mark_as_root(&mut self) {
- debug!("called mark_as_root() on a flow of type {:?}", self.class());
- panic!("called mark_as_root() on an unhandled flow");
- }
-
- // Note that the following functions are mostly called using static method
- // dispatch, so it's ok to have them in this trait. Plus, they have
- // different behaviour for different types of Flow, so they can't go into
- // the Immutable / Mutable Flow Utils traits without additional casts.
-
- fn is_root(&self) -> bool {
- false
- }
-
- /// The 'position' property of this flow.
- fn positioning(&self) -> Position {
- Position::Static
- }
-
- /// Return true if this flow has position 'fixed'.
- fn is_fixed(&self) -> bool {
- self.positioning() == Position::Fixed
- }
-
- fn contains_positioned_fragments(&self) -> bool {
- self.contains_relatively_positioned_fragments() ||
- self.base()
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- }
-
- fn contains_relatively_positioned_fragments(&self) -> bool {
- self.positioning() == Position::Relative
- }
-
- /// Returns true if this is an absolute containing block.
- fn is_absolute_containing_block(&self) -> bool {
- self.contains_positioned_fragments()
- }
-
- /// Returns true if this flow contains fragments that are roots of an absolute flow tree.
- fn contains_roots_of_absolute_flow_tree(&self) -> bool {
- self.contains_relatively_positioned_fragments() || self.is_root()
- }
-
- /// Updates the inline position of a child flow during the assign-height traversal. At present,
- /// this is only used for absolutely-positioned inline-blocks.
- fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au);
-
- /// Updates the block position of a child flow during the assign-height traversal. At present,
- /// this is only used for absolutely-positioned inline-blocks.
- fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au);
-
- /// Return the size of the containing block generated by this flow for the absolutely-
- /// positioned descendant referenced by `for_flow`. For block flows, this is the padding box.
- ///
- /// NB: Do not change this `&self` to `&mut self` under any circumstances! It has security
- /// implications because this can be called on parents concurrently from descendants!
- fn generated_containing_block_size(&self, _: OpaqueFlow) -> LogicalSize<Au>;
-
- /// Attempts to perform incremental fixup of this flow by replacing its fragment's style with
- /// the new style. This can only succeed if the flow has exactly one fragment.
- fn repair_style(&mut self, new_style: &crate::ServoArc<ComputedValues>);
-
- /// Print any extra children (such as fragments) contained in this Flow
- /// for debugging purposes. Any items inserted into the tree will become
- /// children of this flow.
- fn print_extra_flow_children(&self, _: &mut PrintTree) {}
-
- fn clipping_and_scrolling(&self) -> ClippingAndScrolling {
- match self.base().clipping_and_scrolling {
- Some(info) => info,
- None => unreachable!("Tried to access scroll root id on Flow before assignment"),
- }
- }
-}
-
-pub trait ImmutableFlowUtils {
- // Convenience functions
-
- /// Returns true if this flow is a block flow or subclass thereof.
- fn is_block_like(self) -> bool;
-
- /// Returns true if this flow is a table flow.
- fn is_table(self) -> bool;
-
- /// Returns true if this flow is a table caption flow.
- fn is_table_caption(self) -> bool;
-
- /// Returns true if this flow is a table row flow.
- fn is_table_row(self) -> bool;
-
- /// Returns true if this flow is a table cell flow.
- fn is_table_cell(self) -> bool;
-
- /// Returns true if this flow is a table colgroup flow.
- fn is_table_colgroup(self) -> bool;
-
- /// Returns true if this flow is a table rowgroup flow.
- fn is_table_rowgroup(self) -> bool;
-
- /// Returns the number of children that this flow possesses.
- fn child_count(self) -> usize;
-
- /// Returns true if this flow is a block flow.
- fn is_block_flow(self) -> bool;
-
- /// Returns true if this flow is an inline flow.
- fn is_inline_flow(self) -> bool;
-
- /// Dumps the flow tree for debugging.
- fn print(self, title: String);
-
- /// Dumps the flow tree for debugging into the given PrintTree.
- fn print_with_tree(self, print_tree: &mut PrintTree);
-
- /// Returns true if floats might flow through this flow, as determined by the float placement
- /// speculation pass.
- fn floats_might_flow_through(self) -> bool;
-
- fn baseline_offset_of_last_line_box_in_flow(self) -> Option<Au>;
-}
-
-pub trait MutableFlowUtils {
- /// Calls `repair_style` and `bubble_inline_sizes`. You should use this method instead of
- /// calling them individually, since there is no reason not to perform both operations.
- fn repair_style_and_bubble_inline_sizes(self, style: &crate::ServoArc<ComputedValues>);
-}
-
-pub trait MutableOwnedFlowUtils {
- /// Set absolute descendants for this flow.
- ///
- /// Set this flow as the Containing Block for all the absolute descendants.
- fn set_absolute_descendants(&mut self, abs_descendants: AbsoluteDescendants);
-
- /// Sets the flow as the containing block for all absolute descendants that have been marked
- /// as having reached their containing block. This is needed in order to handle cases like:
- ///
- /// ```html
- /// <div>
- /// <span style="position: relative">
- /// <span style="position: absolute; ..."></span>
- /// </span>
- /// </div>
- /// ```
- fn take_applicable_absolute_descendants(
- &mut self,
- absolute_descendants: &mut AbsoluteDescendants,
- );
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
-pub enum FlowClass {
- Block,
- Inline,
- ListItem,
- TableWrapper,
- Table,
- TableColGroup,
- TableRowGroup,
- TableRow,
- TableCaption,
- TableCell,
- Multicol,
- MulticolColumn,
- Flex,
-}
-
-impl FlowClass {
- fn is_block_like(self) -> bool {
- match self {
- FlowClass::Block |
- FlowClass::ListItem |
- FlowClass::Table |
- FlowClass::TableRowGroup |
- FlowClass::TableRow |
- FlowClass::TableCaption |
- FlowClass::TableCell |
- FlowClass::TableWrapper |
- FlowClass::Flex => true,
- _ => false,
- }
- }
-}
-
-bitflags! {
- #[doc = "Flags used in flows."]
- pub struct FlowFlags: u32 {
- // text align flags
- #[doc = "Whether this flow is absolutely positioned. This is checked all over layout, so a"]
- #[doc = "virtual call is too expensive."]
- const IS_ABSOLUTELY_POSITIONED = 0b0000_0000_0000_0000_0100_0000;
- #[doc = "Whether this flow clears to the left. This is checked all over layout, so a"]
- #[doc = "virtual call is too expensive."]
- const CLEARS_LEFT = 0b0000_0000_0000_0000_1000_0000;
- #[doc = "Whether this flow clears to the right. This is checked all over layout, so a"]
- #[doc = "virtual call is too expensive."]
- const CLEARS_RIGHT = 0b0000_0000_0000_0001_0000_0000;
- #[doc = "Whether this flow is left-floated. This is checked all over layout, so a"]
- #[doc = "virtual call is too expensive."]
- const FLOATS_LEFT = 0b0000_0000_0000_0010_0000_0000;
- #[doc = "Whether this flow is right-floated. This is checked all over layout, so a"]
- #[doc = "virtual call is too expensive."]
- const FLOATS_RIGHT = 0b0000_0000_0000_0100_0000_0000;
- #[doc = "Text alignment. \
-
- NB: If you update this, update `TEXT_ALIGN_SHIFT` below."]
- const TEXT_ALIGN = 0b0000_0000_0111_1000_0000_0000;
- #[doc = "Whether this flow has a fragment with `counter-reset` or `counter-increment` \
- styles."]
- const AFFECTS_COUNTERS = 0b0000_0000_1000_0000_0000_0000;
- #[doc = "Whether this flow's descendants have fragments that affect `counter-reset` or \
- `counter-increment` styles."]
- const HAS_COUNTER_AFFECTING_CHILDREN = 0b0000_0001_0000_0000_0000_0000;
- #[doc = "Whether this flow behaves as though it had `position: static` for the purposes \
- of positioning in the inline direction. This is set for flows with `position: \
- static` and `position: relative` as well as absolutely-positioned flows with \
- unconstrained positions in the inline direction."]
- const INLINE_POSITION_IS_STATIC = 0b0000_0010_0000_0000_0000_0000;
- #[doc = "Whether this flow behaves as though it had `position: static` for the purposes \
- of positioning in the block direction. This is set for flows with `position: \
- static` and `position: relative` as well as absolutely-positioned flows with \
- unconstrained positions in the block direction."]
- const BLOCK_POSITION_IS_STATIC = 0b0000_0100_0000_0000_0000_0000;
-
- /// Whether any ancestor is a fragmentation container
- const CAN_BE_FRAGMENTED = 0b0000_1000_0000_0000_0000_0000;
-
- /// Whether this flow contains any text and/or replaced fragments.
- const CONTAINS_TEXT_OR_REPLACED_FRAGMENTS = 0b0001_0000_0000_0000_0000_0000;
-
- /// Whether margins are prohibited from collapsing with this flow.
- const MARGINS_CANNOT_COLLAPSE = 0b0010_0000_0000_0000_0000_0000;
- }
-}
-
-/// The number of bits we must shift off to handle the text alignment field.
-///
-/// NB: If you update this, update `TEXT_ALIGN` above.
-static TEXT_ALIGN_SHIFT: usize = 11;
-
-impl FlowFlags {
- #[inline]
- pub fn text_align(self) -> TextAlign {
- TextAlign::from_u32((self & FlowFlags::TEXT_ALIGN).bits() >> TEXT_ALIGN_SHIFT).unwrap()
- }
-
- #[inline]
- pub fn set_text_align(&mut self, value: TextAlign) {
- *self = (*self & !FlowFlags::TEXT_ALIGN) |
- FlowFlags::from_bits((value as u32) << TEXT_ALIGN_SHIFT).unwrap();
- }
-
- #[inline]
- pub fn float_kind(&self) -> Float {
- if self.contains(FlowFlags::FLOATS_LEFT) {
- Float::Left
- } else if self.contains(FlowFlags::FLOATS_RIGHT) {
- Float::Right
- } else {
- Float::None
- }
- }
-
- #[inline]
- pub fn is_float(&self) -> bool {
- self.contains(FlowFlags::FLOATS_LEFT) || self.contains(FlowFlags::FLOATS_RIGHT)
- }
-
- #[inline]
- pub fn clears_floats(&self) -> bool {
- self.contains(FlowFlags::CLEARS_LEFT) || self.contains(FlowFlags::CLEARS_RIGHT)
- }
-}
-
-/// Absolutely-positioned descendants of this flow.
-#[derive(Clone)]
-pub struct AbsoluteDescendants {
- /// Links to every descendant. This must be private because it is unsafe to leak `FlowRef`s to
- /// layout.
- descendant_links: Vec<AbsoluteDescendantInfo>,
-}
-
-impl AbsoluteDescendants {
- pub fn new() -> AbsoluteDescendants {
- AbsoluteDescendants {
- descendant_links: Vec::new(),
- }
- }
-
- pub fn len(&self) -> usize {
- self.descendant_links.len()
- }
-
- pub fn is_empty(&self) -> bool {
- self.descendant_links.is_empty()
- }
-
- pub fn push(&mut self, given_descendant: FlowRef) {
- self.descendant_links.push(AbsoluteDescendantInfo {
- flow: given_descendant,
- has_reached_containing_block: false,
- });
- }
-
- /// Push the given descendants on to the existing descendants.
- ///
- /// Ignore any static y offsets, because they are None before layout.
- pub fn push_descendants(&mut self, given_descendants: AbsoluteDescendants) {
- for elem in given_descendants.descendant_links {
- self.descendant_links.push(elem);
- }
- }
-
- /// Return an iterator over the descendant flows.
- pub fn iter(&mut self) -> AbsoluteDescendantIter {
- AbsoluteDescendantIter {
- iter: self.descendant_links.iter_mut(),
- }
- }
-
- /// Mark these descendants as having reached their containing block.
- pub fn mark_as_having_reached_containing_block(&mut self) {
- for descendant_info in self.descendant_links.iter_mut() {
- descendant_info.has_reached_containing_block = true
- }
- }
-}
-
-/// Information about each absolutely-positioned descendant of the given flow.
-#[derive(Clone)]
-pub struct AbsoluteDescendantInfo {
- /// The absolute descendant flow in question.
- flow: FlowRef,
-
- /// Whether the absolute descendant has reached its containing block. This exists so that we
- /// can handle cases like the following:
- ///
- /// ```html
- /// <div>
- /// <span id=a style="position: absolute; ...">foo</span>
- /// <span style="position: relative">
- /// <span id=b style="position: absolute; ...">bar</span>
- /// </span>
- /// </div>
- /// ```
- ///
- /// When we go to create the `InlineFlow` for the outer `div`, our absolute descendants will
- /// be `a` and `b`. At this point, we need a way to distinguish between the two, because the
- /// containing block for `a` will be different from the containing block for `b`. Specifically,
- /// the latter's containing block is the inline flow itself, while the former's containing
- /// block is going to be some parent of the outer `div`. Hence we need this flag as a way to
- /// distinguish the two; it will be false for `a` and true for `b`.
- has_reached_containing_block: bool,
-}
-
-pub struct AbsoluteDescendantIter<'a> {
- iter: IterMut<'a, AbsoluteDescendantInfo>,
-}
-
-impl<'a> Iterator for AbsoluteDescendantIter<'a> {
- type Item = &'a mut dyn Flow;
- fn next(&mut self) -> Option<&'a mut dyn Flow> {
- self.iter
- .next()
- .map(|info| FlowRef::deref_mut(&mut info.flow))
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
- }
-}
-
-/// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
-/// confused with absolutely-positioned flows) that is computed during block-size assignment.
-#[derive(Clone, Copy)]
-pub struct EarlyAbsolutePositionInfo {
- /// The size of the containing block for relatively-positioned descendants.
- pub relative_containing_block_size: LogicalSize<Au>,
-
- /// The writing mode for `relative_containing_block_size`.
- pub relative_containing_block_mode: WritingMode,
-}
+pub trait Flow: HasBaseFlow + fmt::Debug + Sync + Send + 'static {}
-impl EarlyAbsolutePositionInfo {
- pub fn new(writing_mode: WritingMode) -> EarlyAbsolutePositionInfo {
- // FIXME(pcwalton): The initial relative containing block-size should be equal to the size
- // of the root layer.
- EarlyAbsolutePositionInfo {
- relative_containing_block_size: LogicalSize::zero(writing_mode),
- relative_containing_block_mode: writing_mode,
- }
- }
-}
-
-/// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
-/// confused with absolutely-positioned flows) that is computed during final position assignment.
-#[derive(Clone, Copy, Serialize)]
-pub struct LateAbsolutePositionInfo {
- /// The position of the absolute containing block relative to the nearest ancestor stacking
- /// context. If the absolute containing block establishes the stacking context for this flow,
- /// and this flow is not itself absolutely-positioned, then this is (0, 0).
- pub stacking_relative_position_of_absolute_containing_block: Point2D<Au>,
-}
-
-impl LateAbsolutePositionInfo {
- pub fn new() -> LateAbsolutePositionInfo {
- LateAbsolutePositionInfo {
- stacking_relative_position_of_absolute_containing_block: Point2D::zero(),
- }
- }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub struct FragmentationContext {
- pub available_block_size: Au,
- pub this_fragment_is_empty: bool,
-}
-
-/// Data common to all flows.
pub struct BaseFlow {
pub restyle_damage: RestyleDamage,
-
- /// The children of this flow.
- pub children: FlowList,
-
- /// Intrinsic inline sizes for this flow.
- pub intrinsic_inline_sizes: IntrinsicISizes,
-
- /// The upper left corner of the box representing this flow, relative to the box representing
- /// its parent flow.
- ///
- /// For absolute flows, this represents the position with respect to its *containing block*.
- ///
- /// This does not include margins in the block flow direction, because those can collapse. So
- /// for the block direction (usually vertical), this represents the *border box*. For the
- /// inline direction (usually horizontal), this represents the *margin box*.
- pub position: LogicalRect<Au>,
-
- /// The amount of overflow of this flow, relative to the containing block. Must include all the
- /// pixels of all the display list items for correct invalidation.
- pub overflow: Overflow,
-
- /// Data used during parallel traversals.
- ///
- /// TODO(pcwalton): Group with other transient data to save space.
- pub parallel: FlowParallelInfo,
-
- /// The floats next to this flow.
- pub floats: Floats,
-
- /// Metrics for floats in computed during the float metrics speculation phase.
- pub speculated_float_placement_in: SpeculatedFloatPlacement,
-
- /// Metrics for floats out computed during the float metrics speculation phase.
- pub speculated_float_placement_out: SpeculatedFloatPlacement,
-
- /// The collapsible margins for this flow, if any.
- pub collapsible_margins: CollapsibleMargins,
-
- /// The position of this flow relative to the start of the nearest ancestor stacking context.
- /// This is computed during the top-down pass of display list construction.
- pub stacking_relative_position: Vector2D<Au>,
-
- /// Details about descendants with position 'absolute' or 'fixed' for which we are the
- /// containing block. This is in tree order. This includes any direct children.
- pub abs_descendants: AbsoluteDescendants,
-
- /// The inline-size of the block container of this flow. Used for computing percentage and
- /// automatic values for `width`.
- pub block_container_inline_size: Au,
-
- /// The writing mode of the block container of this flow.
- ///
- /// FIXME (mbrubeck): Combine this and block_container_inline_size and maybe
- /// block_container_explicit_block_size into a struct, to guarantee they are set at the same
- /// time? Or just store a link to the containing block flow.
- pub block_container_writing_mode: WritingMode,
-
- /// The block-size of the block container of this flow, if it is an explicit size (does not
- /// depend on content heights). Used for computing percentage values for `height`.
- pub block_container_explicit_block_size: Option<Au>,
-
- /// Reference to the Containing Block, if this flow is absolutely positioned.
- pub absolute_cb: ContainingBlockLink,
-
- /// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
- /// confused with absolutely-positioned flows) that is computed during block-size assignment.
- pub early_absolute_position_info: EarlyAbsolutePositionInfo,
-
- /// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
- /// confused with absolutely-positioned flows) that is computed during final position
- /// assignment.
- pub late_absolute_position_info: LateAbsolutePositionInfo,
-
- /// The clipping rectangle for this flow and its descendants, in the coordinate system of the
- /// nearest ancestor stacking context. If this flow itself represents a stacking context, then
- /// this is in the flow's own coordinate system.
- pub clip: Rect<Au>,
-
- /// The writing mode for this flow.
- pub writing_mode: WritingMode,
-
- /// For debugging and profiling, the identifier of the thread that laid out this fragment.
- pub thread_id: u8,
-
- /// Various flags for flows, tightly packed to save space.
- pub flags: FlowFlags,
-
- /// The ID of the StackingContext that contains this flow. This is initialized
- /// to 0, but it assigned during the collect_stacking_contexts phase of display
- /// list construction.
- pub stacking_context_id: StackingContextId,
-
- /// The indices of this Flow's ClipScrollNode. This is used to place the node's
- /// display items into scrolling frames and clipping nodes.
- pub clipping_and_scrolling: Option<ClippingAndScrolling>,
-}
-
-impl fmt::Debug for BaseFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let child_count = self.parallel.children_count.load(Ordering::SeqCst);
- let child_count_string = if child_count > 0 {
- format!("\nchildren={}", child_count)
- } else {
- "".to_owned()
- };
-
- let absolute_descendants_string = if self.abs_descendants.len() > 0 {
- format!("\nabs-descendents={}", self.abs_descendants.len())
- } else {
- "".to_owned()
- };
-
- let damage_string = if self.restyle_damage != RestyleDamage::empty() {
- format!("\ndamage={:?}", self.restyle_damage)
- } else {
- "".to_owned()
- };
-
- write!(
- f,
- "\nsc={:?}\
- \npos={:?}{}{}\
- \nfloatspec-in={:?}\
- \nfloatspec-out={:?}\
- \noverflow={:?}{}{}{}",
- self.stacking_context_id,
- self.position,
- if self.flags.contains(FlowFlags::FLOATS_LEFT) {
- "FL"
- } else {
- ""
- },
- if self.flags.contains(FlowFlags::FLOATS_RIGHT) {
- "FR"
- } else {
- ""
- },
- self.speculated_float_placement_in,
- self.speculated_float_placement_out,
- self.overflow,
- child_count_string,
- absolute_descendants_string,
- damage_string
- )
- }
-}
-
-impl Serialize for BaseFlow {
- fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
- let mut serializer = serializer.serialize_struct("base", 5)?;
- serializer.serialize_field("id", &self.debug_id())?;
- serializer.serialize_field(
- "stacking_relative_position",
- &self.stacking_relative_position,
- )?;
- serializer.serialize_field("intrinsic_inline_sizes", &self.intrinsic_inline_sizes)?;
- serializer.serialize_field("position", &self.position)?;
- serializer.serialize_field("children", &self.children)?;
- serializer.end()
- }
-}
-
-/// Whether a base flow should be forced to be nonfloated. This can affect e.g. `TableFlow`, which
-/// is never floated because the table wrapper flow is the floated one.
-#[derive(Clone, PartialEq)]
-pub enum ForceNonfloatedFlag {
- /// The flow should be floated if the node has a `float` property.
- FloatIfNecessary,
- /// The flow should be forced to be nonfloated.
- ForceNonfloated,
-}
-
-impl BaseFlow {
- #[inline]
- pub fn new(
- style: Option<&ComputedValues>,
- writing_mode: WritingMode,
- force_nonfloated: ForceNonfloatedFlag,
- ) -> BaseFlow {
- let mut flags = FlowFlags::empty();
- match style {
- Some(style) => {
- if style.can_be_fragmented() {
- flags.insert(FlowFlags::CAN_BE_FRAGMENTED);
- }
-
- match style.get_box().position {
- Position::Absolute | Position::Fixed => {
- flags.insert(FlowFlags::IS_ABSOLUTELY_POSITIONED);
-
- let logical_position = style.logical_position();
- if logical_position.inline_start == LengthPercentageOrAuto::Auto &&
- logical_position.inline_end == LengthPercentageOrAuto::Auto
- {
- flags.insert(FlowFlags::INLINE_POSITION_IS_STATIC);
- }
- if logical_position.block_start == LengthPercentageOrAuto::Auto &&
- logical_position.block_end == LengthPercentageOrAuto::Auto
- {
- flags.insert(FlowFlags::BLOCK_POSITION_IS_STATIC);
- }
- },
- _ => flags.insert(
- FlowFlags::BLOCK_POSITION_IS_STATIC | FlowFlags::INLINE_POSITION_IS_STATIC,
- ),
- }
-
- if force_nonfloated == ForceNonfloatedFlag::FloatIfNecessary {
- match style.get_box().float {
- Float::None => {},
- Float::Left => flags.insert(FlowFlags::FLOATS_LEFT),
- Float::Right => flags.insert(FlowFlags::FLOATS_RIGHT),
- }
- }
-
- match style.get_box().clear {
- Clear::None => {},
- Clear::Left => flags.insert(FlowFlags::CLEARS_LEFT),
- Clear::Right => flags.insert(FlowFlags::CLEARS_RIGHT),
- Clear::Both => {
- flags.insert(FlowFlags::CLEARS_LEFT);
- flags.insert(FlowFlags::CLEARS_RIGHT);
- },
- }
-
- if !style.get_counters().counter_reset.is_empty() ||
- !style.get_counters().counter_increment.is_empty()
- {
- flags.insert(FlowFlags::AFFECTS_COUNTERS)
- }
- },
- None => flags
- .insert(FlowFlags::BLOCK_POSITION_IS_STATIC | FlowFlags::INLINE_POSITION_IS_STATIC),
- }
-
- // New flows start out as fully damaged.
- let mut damage = RestyleDamage::rebuild_and_reflow();
- damage.remove(ServoRestyleDamage::RECONSTRUCT_FLOW);
-
- BaseFlow {
- restyle_damage: damage,
- children: FlowList::new(),
- intrinsic_inline_sizes: IntrinsicISizes::new(),
- position: LogicalRect::zero(writing_mode),
- overflow: Overflow::new(),
- parallel: FlowParallelInfo::new(),
- floats: Floats::new(writing_mode),
- collapsible_margins: CollapsibleMargins::new(),
- stacking_relative_position: Vector2D::zero(),
- abs_descendants: AbsoluteDescendants::new(),
- speculated_float_placement_in: SpeculatedFloatPlacement::zero(),
- speculated_float_placement_out: SpeculatedFloatPlacement::zero(),
- block_container_inline_size: Au(0),
- block_container_writing_mode: writing_mode,
- block_container_explicit_block_size: None,
- absolute_cb: ContainingBlockLink::new(),
- early_absolute_position_info: EarlyAbsolutePositionInfo::new(writing_mode),
- late_absolute_position_info: LateAbsolutePositionInfo::new(),
- clip: MaxRect::max_rect(),
- flags: flags,
- writing_mode: writing_mode,
- thread_id: 0,
- stacking_context_id: StackingContextId::root(),
- clipping_and_scrolling: None,
- }
- }
-
- /// Update the 'flags' field when computed styles have changed.
- ///
- /// These flags are initially set during flow construction. They only need to be updated here
- /// if they are based on properties that can change without triggering `RECONSTRUCT_FLOW`.
- pub fn update_flags_if_needed(&mut self, style: &ComputedValues) {
- // For absolutely-positioned flows, changes to top/bottom/left/right can cause these flags
- // to get out of date:
- if self
- .restyle_damage
- .contains(ServoRestyleDamage::REFLOW_OUT_OF_FLOW)
- {
- // Note: We don't need to check whether IS_ABSOLUTELY_POSITIONED has changed, because
- // changes to the 'position' property trigger flow reconstruction.
- if self.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
- let logical_position = style.logical_position();
- self.flags.set(
- FlowFlags::INLINE_POSITION_IS_STATIC,
- logical_position.inline_start == LengthPercentageOrAuto::Auto &&
- logical_position.inline_end == LengthPercentageOrAuto::Auto,
- );
- self.flags.set(
- FlowFlags::BLOCK_POSITION_IS_STATIC,
- logical_position.block_start == LengthPercentageOrAuto::Auto &&
- logical_position.block_end == LengthPercentageOrAuto::Auto,
- );
- }
- }
- }
-
- /// Return a new BaseFlow like this one but with the given children list
- pub fn clone_with_children(&self, children: FlowList) -> BaseFlow {
- BaseFlow {
- children: children,
- restyle_damage: self.restyle_damage |
- ServoRestyleDamage::REPAINT |
- ServoRestyleDamage::REFLOW_OUT_OF_FLOW |
- ServoRestyleDamage::REFLOW,
- parallel: FlowParallelInfo::new(),
- floats: self.floats.clone(),
- abs_descendants: self.abs_descendants.clone(),
- absolute_cb: self.absolute_cb.clone(),
- clip: self.clip.clone(),
-
- ..*self
- }
- }
-
- /// Iterates over the children of this immutable flow.
- pub fn child_iter(&self) -> FlowListIterator {
- self.children.iter()
- }
-
- pub fn child_iter_mut(&mut self) -> MutFlowListIterator {
- self.children.iter_mut()
- }
-
- pub fn debug_id(&self) -> usize {
- let p = self as *const _;
- p as usize
- }
-
- pub fn collect_stacking_contexts_for_children(
- &mut self,
- state: &mut StackingContextCollectionState,
- ) {
- for kid in self.children.iter_mut() {
- kid.collect_stacking_contexts(state);
- }
- }
-
- #[inline]
- pub fn might_have_floats_in(&self) -> bool {
- self.speculated_float_placement_in.left > Au(0) ||
- self.speculated_float_placement_in.right > Au(0)
- }
-
- #[inline]
- pub fn might_have_floats_out(&self) -> bool {
- self.speculated_float_placement_out.left > Au(0) ||
- self.speculated_float_placement_out.right > Au(0)
- }
-
- /// Compute the fragment position relative to the parent stacking context. If the fragment
- /// itself establishes a stacking context, then the origin of its position will be (0, 0)
- /// for the purposes of this computation.
- pub fn stacking_relative_border_box_for_display_list(&self, fragment: &Fragment) -> Rect<Au> {
- fragment.stacking_relative_border_box(
- &self.stacking_relative_position,
- &self
- .early_absolute_position_info
- .relative_containing_block_size,
- self.early_absolute_position_info
- .relative_containing_block_mode,
- CoordinateSystem::Own,
- )
- }
-}
-
-impl<'a> ImmutableFlowUtils for &'a dyn Flow {
- /// Returns true if this flow is a block flow or subclass thereof.
- fn is_block_like(self) -> bool {
- self.class().is_block_like()
- }
-
- /// Returns true if this flow is a table row flow.
- fn is_table_row(self) -> bool {
- match self.class() {
- FlowClass::TableRow => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is a table cell flow.
- fn is_table_cell(self) -> bool {
- match self.class() {
- FlowClass::TableCell => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is a table colgroup flow.
- fn is_table_colgroup(self) -> bool {
- match self.class() {
- FlowClass::TableColGroup => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is a table flow.
- fn is_table(self) -> bool {
- match self.class() {
- FlowClass::Table => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is a table caption flow.
- fn is_table_caption(self) -> bool {
- match self.class() {
- FlowClass::TableCaption => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is a table rowgroup flow.
- fn is_table_rowgroup(self) -> bool {
- match self.class() {
- FlowClass::TableRowGroup => true,
- _ => false,
- }
- }
-
- /// Returns the number of children that this flow possesses.
- fn child_count(self) -> usize {
- self.base().children.len()
- }
-
- /// Returns true if this flow is a block flow.
- fn is_block_flow(self) -> bool {
- match self.class() {
- FlowClass::Block => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is an inline flow.
- fn is_inline_flow(self) -> bool {
- match self.class() {
- FlowClass::Inline => true,
- _ => false,
- }
- }
-
- /// Dumps the flow tree for debugging.
- fn print(self, title: String) {
- let mut print_tree = PrintTree::new(title);
- self.print_with_tree(&mut print_tree);
- }
-
- /// Dumps the flow tree for debugging into the given PrintTree.
- fn print_with_tree(self, print_tree: &mut PrintTree) {
- print_tree.new_level(format!("{:?}", self));
- self.print_extra_flow_children(print_tree);
- for kid in self.base().child_iter() {
- kid.print_with_tree(print_tree);
- }
- print_tree.end_level();
- }
-
- fn floats_might_flow_through(self) -> bool {
- if !self.base().might_have_floats_in() && !self.base().might_have_floats_out() {
- return false;
- }
- if self.is_root() {
- return false;
- }
- if !self.is_block_like() {
- return true;
- }
- self.as_block().formatting_context_type() == FormattingContextType::None
- }
-
- fn baseline_offset_of_last_line_box_in_flow(self) -> Option<Au> {
- for kid in self.base().children.iter().rev() {
- if kid.is_inline_flow() {
- if let Some(baseline_offset) = kid.as_inline().baseline_offset_of_last_line() {
- return Some(kid.base().position.start.b + baseline_offset);
- }
- }
- if kid.is_block_like() &&
- !kid.base()
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- if let Some(baseline_offset) = kid.baseline_offset_of_last_line_box_in_flow() {
- return Some(kid.base().position.start.b + baseline_offset);
- }
- }
- }
- None
- }
-}
-
-impl<'a> MutableFlowUtils for &'a mut dyn Flow {
- /// Calls `repair_style` and `bubble_inline_sizes`. You should use this method instead of
- /// calling them individually, since there is no reason not to perform both operations.
- fn repair_style_and_bubble_inline_sizes(self, style: &crate::ServoArc<ComputedValues>) {
- self.repair_style(style);
- self.mut_base().update_flags_if_needed(style);
- self.bubble_inline_sizes();
- }
-}
-
-impl MutableOwnedFlowUtils for FlowRef {
- /// Set absolute descendants for this flow.
- ///
- /// Set yourself as the Containing Block for all the absolute descendants.
- ///
- /// This is called during flow construction, so nothing else can be accessing the descendant
- /// flows. This is enforced by the fact that we have a mutable `FlowRef`, which only flow
- /// construction is allowed to possess.
- fn set_absolute_descendants(&mut self, abs_descendants: AbsoluteDescendants) {
- let this = self.clone();
- let base = FlowRef::deref_mut(self).mut_base();
- base.abs_descendants = abs_descendants;
- for descendant_link in base.abs_descendants.descendant_links.iter_mut() {
- debug_assert!(!descendant_link.has_reached_containing_block);
- let descendant_base = FlowRef::deref_mut(&mut descendant_link.flow).mut_base();
- descendant_base.absolute_cb.set(this.clone());
- }
- }
-
- /// Sets the flow as the containing block for all absolute descendants that have been marked
- /// as having reached their containing block. This is needed in order to handle cases like:
- ///
- /// ```html
- /// <div>
- /// <span style="position: relative">
- /// <span style="position: absolute; ..."></span>
- /// </span>
- /// </div>
- /// ```
- fn take_applicable_absolute_descendants(
- &mut self,
- absolute_descendants: &mut AbsoluteDescendants,
- ) {
- let mut applicable_absolute_descendants = AbsoluteDescendants::new();
- for absolute_descendant in absolute_descendants.descendant_links.iter() {
- if absolute_descendant.has_reached_containing_block {
- applicable_absolute_descendants.push(absolute_descendant.flow.clone());
- }
- }
- absolute_descendants
- .descendant_links
- .retain(|descendant| !descendant.has_reached_containing_block);
-
- let this = self.clone();
- let base = FlowRef::deref_mut(self).mut_base();
- base.abs_descendants = applicable_absolute_descendants;
- for descendant_link in base.abs_descendants.iter() {
- let descendant_base = descendant_link.mut_base();
- descendant_base.absolute_cb.set(this.clone());
- }
- }
-}
-
-/// A link to a flow's containing block.
-///
-/// This cannot safely be a `Flow` pointer because this is a pointer *up* the tree, not *down* the
-/// tree. A pointer up the tree is unsafe during layout because it can be used to access a node
-/// with an immutable reference while that same node is being laid out, causing possible iterator
-/// invalidation and use-after-free.
-///
-/// FIXME(pcwalton): I think this would be better with a borrow flag instead of `unsafe`.
-#[derive(Clone)]
-pub struct ContainingBlockLink {
- /// The pointer up to the containing block.
- link: Option<WeakFlowRef>,
-}
-
-impl ContainingBlockLink {
- fn new() -> ContainingBlockLink {
- ContainingBlockLink { link: None }
- }
-
- fn set(&mut self, link: FlowRef) {
- self.link = Some(FlowRef::downgrade(&link))
- }
-
- #[inline]
- pub fn generated_containing_block_size(&self, for_flow: OpaqueFlow) -> LogicalSize<Au> {
- match self.link {
- None => panic!(
- "Link to containing block not established; perhaps you forgot to call \
- `set_absolute_descendants`?"
- ),
- Some(ref link) => {
- let flow = link.upgrade().unwrap();
- flow.generated_containing_block_size(for_flow)
- },
- }
- }
-
- #[inline]
- pub fn explicit_block_containing_size(
- &self,
- shared_context: &SharedStyleContext,
- ) -> Option<Au> {
- match self.link {
- None => panic!(
- "Link to containing block not established; perhaps you forgot to call \
- `set_absolute_descendants`?"
- ),
- Some(ref link) => {
- let flow = link.upgrade().unwrap();
- if flow.is_block_like() {
- flow.as_block()
- .explicit_block_containing_size(shared_context)
- } else if flow.is_inline_flow() {
- Some(flow.as_inline().minimum_line_metrics.space_above_baseline)
- } else {
- None
- }
- },
- }
- }
-}
-
-/// A wrapper for the pointer address of a flow. These pointer addresses may only be compared for
-/// equality with other such pointer addresses, never dereferenced.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub struct OpaqueFlow(pub usize);
-
-impl OpaqueFlow {
- pub fn from_flow(flow: &dyn Flow) -> OpaqueFlow {
- let object_ptr: *const dyn Flow = flow;
- let data_ptr = object_ptr as *const ();
- OpaqueFlow(data_ptr as usize)
- }
}
diff --git a/components/layout_2020/flow_list.rs b/components/layout_2020/flow_list.rs
deleted file mode 100644
index a27db03920a..00000000000
--- a/components/layout_2020/flow_list.rs
+++ /dev/null
@@ -1,197 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::flow::{Flow, FlowClass};
-use crate::flow_ref::FlowRef;
-use serde::ser::{Serialize, SerializeSeq, Serializer};
-use serde_json::{to_value, Map, Value};
-use std::collections::{linked_list, LinkedList};
-use std::ops::Deref;
-use std::sync::Arc;
-
-/// This needs to be reworked now that we have dynamically-sized types in Rust.
-/// Until then, it's just a wrapper around LinkedList.
-///
-/// SECURITY-NOTE(pcwalton): It is very important that `FlowRef` values not leak directly to
-/// layout. Layout code must only interact with `&Flow` or `&mut Flow` values. Otherwise, layout
-/// could stash `FlowRef` values in random places unknown to the system and thereby cause data
-/// races. Those data races can lead to memory safety problems, potentially including arbitrary
-/// remote code execution! In general, do not add new methods to this file (e.g. new ways of
-/// iterating over flows) unless you are *very* sure of what you are doing.
-pub struct FlowList {
- flows: LinkedList<FlowRef>,
-}
-
-impl Serialize for FlowList {
- fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
- let mut serializer = serializer.serialize_seq(Some(self.len()))?;
- for f in self.iter() {
- let mut flow_val = Map::new();
- flow_val.insert("class".to_owned(), to_value(f.class()).unwrap());
- let data = match f.class() {
- FlowClass::Block => to_value(f.as_block()).unwrap(),
- FlowClass::Inline => to_value(f.as_inline()).unwrap(),
- FlowClass::Table => to_value(f.as_table()).unwrap(),
- FlowClass::TableWrapper => to_value(f.as_table_wrapper()).unwrap(),
- FlowClass::TableRowGroup => to_value(f.as_table_rowgroup()).unwrap(),
- FlowClass::TableRow => to_value(f.as_table_row()).unwrap(),
- FlowClass::TableCell => to_value(f.as_table_cell()).unwrap(),
- FlowClass::Flex => to_value(f.as_flex()).unwrap(),
- FlowClass::ListItem |
- FlowClass::TableColGroup |
- FlowClass::TableCaption |
- FlowClass::Multicol |
- FlowClass::MulticolColumn => {
- Value::Null // Not implemented yet
- },
- };
- flow_val.insert("data".to_owned(), data);
- serializer.serialize_element(&flow_val)?;
- }
- serializer.end()
- }
-}
-
-pub struct MutFlowListIterator<'a> {
- it: linked_list::IterMut<'a, FlowRef>,
-}
-
-pub struct FlowListIterator<'a> {
- it: linked_list::Iter<'a, FlowRef>,
-}
-
-impl FlowList {
- /// Add an element last in the list
- ///
- /// O(1)
- pub fn push_back(&mut self, new_tail: FlowRef) {
- self.flows.push_back(new_tail);
- }
-
- pub fn push_back_arc(&mut self, new_head: Arc<dyn Flow>) {
- self.flows.push_back(FlowRef::new(new_head));
- }
-
- pub fn push_front_arc(&mut self, new_head: Arc<dyn Flow>) {
- self.flows.push_front(FlowRef::new(new_head));
- }
-
- pub fn pop_front_arc(&mut self) -> Option<Arc<dyn Flow>> {
- self.flows.pop_front().map(FlowRef::into_arc)
- }
-
- /// Create an empty list
- #[inline]
- pub fn new() -> FlowList {
- FlowList {
- flows: LinkedList::new(),
- }
- }
-
- /// Provide a forward iterator.
- ///
- /// SECURITY-NOTE(pcwalton): This does not hand out `FlowRef`s by design. Do not add a method
- /// to do so! See the comment above in `FlowList`.
- #[inline]
- pub fn iter<'a>(&'a self) -> FlowListIterator {
- FlowListIterator {
- it: self.flows.iter(),
- }
- }
-
- /// Provide a forward iterator with mutable references
- ///
- /// SECURITY-NOTE(pcwalton): This does not hand out `FlowRef`s by design. Do not add a method
- /// to do so! See the comment above in `FlowList`.
- #[inline]
- pub fn iter_mut(&mut self) -> MutFlowListIterator {
- MutFlowListIterator {
- it: self.flows.iter_mut(),
- }
- }
-
- /// Provides a caching random-access iterator that yields mutable references. This is
- /// guaranteed to perform no more than O(n) pointer chases.
- ///
- /// SECURITY-NOTE(pcwalton): This does not hand out `FlowRef`s by design. Do not add a method
- /// to do so! See the comment above in `FlowList`.
- #[inline]
- pub fn random_access_mut(&mut self) -> FlowListRandomAccessMut {
- let length = self.flows.len();
- FlowListRandomAccessMut {
- iterator: self.flows.iter_mut(),
- cache: Vec::with_capacity(length),
- }
- }
-
- /// O(1)
- #[inline]
- pub fn len(&self) -> usize {
- self.flows.len()
- }
-
- #[inline]
- pub fn split_off(&mut self, i: usize) -> Self {
- FlowList {
- flows: self.flows.split_off(i),
- }
- }
-}
-
-impl<'a> DoubleEndedIterator for FlowListIterator<'a> {
- fn next_back(&mut self) -> Option<&'a dyn Flow> {
- self.it.next_back().map(Deref::deref)
- }
-}
-
-impl<'a> DoubleEndedIterator for MutFlowListIterator<'a> {
- fn next_back(&mut self) -> Option<&'a mut dyn Flow> {
- self.it.next_back().map(FlowRef::deref_mut)
- }
-}
-
-impl<'a> Iterator for FlowListIterator<'a> {
- type Item = &'a dyn Flow;
- #[inline]
- fn next(&mut self) -> Option<&'a dyn Flow> {
- self.it.next().map(Deref::deref)
- }
-
- #[inline]
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.it.size_hint()
- }
-}
-
-impl<'a> Iterator for MutFlowListIterator<'a> {
- type Item = &'a mut dyn Flow;
- #[inline]
- fn next(&mut self) -> Option<&'a mut dyn Flow> {
- self.it.next().map(FlowRef::deref_mut)
- }
-
- #[inline]
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.it.size_hint()
- }
-}
-
-/// A caching random-access iterator that yields mutable references. This is guaranteed to perform
-/// no more than O(n) pointer chases.
-pub struct FlowListRandomAccessMut<'a> {
- iterator: linked_list::IterMut<'a, FlowRef>,
- cache: Vec<FlowRef>,
-}
-
-impl<'a> FlowListRandomAccessMut<'a> {
- pub fn get<'b>(&'b mut self, index: usize) -> &'b mut dyn Flow {
- while index >= self.cache.len() {
- match self.iterator.next() {
- None => panic!("Flow index out of range!"),
- Some(next_flow) => self.cache.push((*next_flow).clone()),
- }
- }
- FlowRef::deref_mut(&mut self.cache[index])
- }
-}
diff --git a/components/layout_2020/flow_ref.rs b/components/layout_2020/flow_ref.rs
index f00740aa4ed..f13a562927c 100644
--- a/components/layout_2020/flow_ref.rs
+++ b/components/layout_2020/flow_ref.rs
@@ -2,15 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-//! Reference-counted pointers to flows.
-//!
-//! Eventually, with dynamically sized types in Rust, much of this code will
-//! be superfluous. This design is largely duplicating logic of Arc<T> and
-//! Weak<T>; please see comments there for details.
-
use crate::flow::Flow;
use std::ops::Deref;
-use std::sync::{Arc, Weak};
+use std::sync::Arc;
#[derive(Clone, Debug)]
pub struct FlowRef(Arc<dyn Flow>);
@@ -23,42 +17,14 @@ impl Deref for FlowRef {
}
impl FlowRef {
- /// `FlowRef`s can only be made available to the traversal code.
- /// See https://github.com/servo/servo/issues/14014 for more details.
pub fn new(mut r: Arc<dyn Flow>) -> Self {
- // This assertion checks that this `FlowRef` does not alias normal `Arc`s.
- // If that happens, we're in trouble.
assert!(Arc::get_mut(&mut r).is_some());
FlowRef(r)
}
- pub fn get_mut(this: &mut FlowRef) -> Option<&mut dyn Flow> {
- Arc::get_mut(&mut this.0)
- }
- pub fn downgrade(this: &FlowRef) -> WeakFlowRef {
- WeakFlowRef(Arc::downgrade(&this.0))
- }
- pub fn into_arc(mut this: FlowRef) -> Arc<dyn Flow> {
- // This assertion checks that this `FlowRef` does not alias normal `Arc`s.
- // If that happens, we're in trouble.
- assert!(FlowRef::get_mut(&mut this).is_some());
- this.0
- }
- /// WARNING: This should only be used when there is no aliasing:
- /// when the traversal ensures that no other threads accesses the same flow at the same time.
- /// See https://github.com/servo/servo/issues/6503
- /// Use Arc::get_mut instead when possible (e.g. on an Arc that was just created).
+
#[allow(unsafe_code)]
pub fn deref_mut(this: &mut FlowRef) -> &mut dyn Flow {
let ptr: *const dyn Flow = &*this.0;
unsafe { &mut *(ptr as *mut dyn Flow) }
}
}
-
-#[derive(Clone, Debug)]
-pub struct WeakFlowRef(Weak<dyn Flow>);
-
-impl WeakFlowRef {
- pub fn upgrade(&self) -> Option<FlowRef> {
- self.0.upgrade().map(FlowRef)
- }
-}
diff --git a/components/layout_2020/fragment.rs b/components/layout_2020/fragment.rs
index 879e2af2f64..b48aa196785 100644
--- a/components/layout_2020/fragment.rs
+++ b/components/layout_2020/fragment.rs
@@ -4,662 +4,48 @@
//! The `Fragment` type, which represents the leaves of the layout tree.
-use crate::context::{with_thread_local_font_context, LayoutContext};
-use crate::display_list::items::{ClipScrollNodeIndex, OpaqueNode, BLUR_INFLATION_FACTOR};
-use crate::display_list::ToLayout;
-use crate::floats::ClearType;
-use crate::flow::{GetBaseFlow, ImmutableFlowUtils};
-use crate::flow_ref::FlowRef;
-use crate::inline::{InlineFragmentContext, InlineFragmentNodeFlags, InlineFragmentNodeInfo};
-use crate::inline::{InlineMetrics, LineMetrics};
-#[cfg(debug_assertions)]
-use crate::layout_debug;
-use crate::model::style_length;
-use crate::model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeConstraint};
-use crate::text;
-use crate::text::TextRunScanner;
-use crate::wrapper::ThreadSafeLayoutNodeHelpers;
+use crate::context::LayoutContext;
+use crate::display_list::items::OpaqueNode;
use crate::ServoArc;
use app_units::Au;
-use canvas_traits::canvas::{CanvasId, CanvasMsg};
-use euclid::{Point2D, Rect, Size2D, Vector2D};
-use gfx::text::glyph::ByteIndex;
-use gfx::text::text_run::{TextRun, TextRunSlice};
-use gfx_traits::StackingContextId;
-use ipc_channel::ipc::IpcSender;
-use msg::constellation_msg::{BrowsingContextId, PipelineId};
-use net_traits::image::base::{Image, ImageMetadata};
-use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
-use range::*;
-use script_layout_interface::wrapper_traits::{
- PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
-};
-use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource, HTMLMediaData, SVGSVGData};
+use euclid::Rect;
+use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutNode};
use serde::ser::{Serialize, SerializeStruct, Serializer};
-use servo_url::ServoUrl;
-use std::borrow::ToOwned;
-use std::cmp::{max, min, Ordering};
-use std::collections::LinkedList;
-use std::sync::{Arc, Mutex};
-use std::{f32, fmt};
-use style::computed_values::border_collapse::T as BorderCollapse;
-use style::computed_values::box_sizing::T as BoxSizing;
-use style::computed_values::clear::T as Clear;
-use style::computed_values::color::T as Color;
-use style::computed_values::display::T as Display;
-use style::computed_values::mix_blend_mode::T as MixBlendMode;
-use style::computed_values::overflow_wrap::T as OverflowWrap;
-use style::computed_values::overflow_x::T as StyleOverflow;
-use style::computed_values::position::T as Position;
-use style::computed_values::text_decoration_line::T as TextDecorationLine;
-use style::computed_values::transform_style::T as TransformStyle;
-use style::computed_values::white_space::T as WhiteSpace;
-use style::computed_values::word_break::T as WordBreak;
-use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode};
+use style::logical_geometry::{LogicalMargin, LogicalRect};
use style::properties::ComputedValues;
use style::selector_parser::RestyleDamage;
use style::servo::restyle_damage::ServoRestyleDamage;
-use style::str::char_is_whitespace;
-use style::values::computed::counters::ContentItem;
-use style::values::computed::{LengthPercentage, LengthPercentageOrAuto, Size, VerticalAlign};
-use style::values::generics::box_::{Perspective, VerticalAlignKeyword};
-use style::values::generics::transform;
-use style::Zero;
-use webrender_api;
-use webrender_api::units::LayoutTransform;
-// From gfxFontConstants.h in Firefox.
-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:
-///
-/// * Several fragments may correspond to the same CSS box or DOM node. For example, a CSS text box
-/// broken across two lines is represented by two fragments.
-///
-/// * Some CSS fragments are not created at all, such as some anonymous block fragments induced by
-/// inline fragments with block-level sibling fragments. In that case, Servo uses an `InlineFlow`
-/// with `BlockFlow` siblings; the `InlineFlow` is block-level, but not a block container. It is
-/// positioned as if it were a block fragment, but its children are positioned according to
-/// inline flow.
-///
-/// A `SpecificFragmentInfo::Generic` is an empty fragment that contributes only borders, margins,
-/// padding, and backgrounds. It is analogous to a CSS nonreplaced content box.
-///
-/// A fragment's type influences how its styles are interpreted during layout. For example,
-/// replaced content such as images are resized differently from tables, text, or other content.
-/// Different types of fragments may also contain custom data; for example, text fragments contain
-/// text.
-///
-/// Do not add fields to this structure unless they're really really mega necessary! Fragments get
-/// moved around a lot and thus their size impacts performance of layout quite a bit.
-///
-/// FIXME(#2260, pcwalton): This can be slimmed down some by (at least) moving `inline_context`
-/// to be on `InlineFlow` only.
#[derive(Clone)]
pub struct Fragment {
- /// An opaque reference to the DOM node that this `Fragment` originates from.
pub node: OpaqueNode,
-
- /// The CSS style of this fragment.
pub style: ServoArc<ComputedValues>,
-
- /// The CSS style of this fragment when it's selected
- pub selected_style: ServoArc<ComputedValues>,
-
- /// The position of this fragment relative to its owning flow. The size includes padding and
- /// border, but not margin.
- ///
- /// NB: This does not account for relative positioning.
- /// NB: Collapsed borders are not included in this.
pub border_box: LogicalRect<Au>,
-
- /// The sum of border and padding; i.e. the distance from the edge of the border box to the
- /// content edge of the fragment.
pub border_padding: LogicalMargin<Au>,
-
- /// The margin of the content box.
pub margin: LogicalMargin<Au>,
-
- /// Info specific to the kind of fragment. Keep this enum small.
pub specific: SpecificFragmentInfo,
-
- /// Holds the style context information for fragments that are part of an inline formatting
- /// context.
- pub inline_context: Option<InlineFragmentContext>,
-
- /// How damaged this fragment is since last reflow.
pub restyle_damage: RestyleDamage,
-
- /// The pseudo-element that this fragment represents.
pub pseudo: PseudoElementType,
-
- /// Various flags for this fragment.
- pub flags: FragmentFlags,
-
- /// A debug ID that is consistent for the life of this fragment (via transform etc).
- /// This ID should not be considered stable across multiple layouts or fragment
- /// manipulations.
- debug_id: DebugId,
-
- /// The ID of the StackingContext that contains this fragment. This is initialized
- /// to 0, but it assigned during the collect_stacking_contexts phase of display
- /// list construction.
- pub stacking_context_id: StackingContextId,
-
- /// The indices of this Fragment's ClipScrollNode. If this fragment doesn't have a
- /// `established_reference_frame` assigned, it will use the `clipping_and_scrolling` of the
- /// parent block.
- pub established_reference_frame: Option<ClipScrollNodeIndex>,
}
impl Serialize for Fragment {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut serializer = serializer.serialize_struct("fragment", 3)?;
- serializer.serialize_field("id", &self.debug_id)?;
serializer.serialize_field("border_box", &self.border_box)?;
serializer.serialize_field("margin", &self.margin)?;
serializer.end()
}
}
-/// Info specific to the kind of fragment.
-///
-/// Keep this enum small. As in, no more than one word. Or pcwalton will yell at you.
#[derive(Clone)]
pub enum SpecificFragmentInfo {
Generic,
-
- /// A piece of generated content that cannot be resolved into `ScannedText` until the generated
- /// content resolution phase (e.g. an ordered list item marker).
- GeneratedContent(Box<GeneratedContentInfo>),
-
- Iframe(IframeFragmentInfo),
- Image(Box<ImageFragmentInfo>),
- Media(Box<MediaFragmentInfo>),
- Canvas(Box<CanvasFragmentInfo>),
- Svg(Box<SvgFragmentInfo>),
-
- /// A hypothetical box (see CSS 2.1 § 10.3.7) for an absolutely-positioned block that was
- /// declared with `display: inline;`.
- InlineAbsoluteHypothetical(InlineAbsoluteHypotheticalFragmentInfo),
-
- InlineBlock(InlineBlockFragmentInfo),
-
- /// An inline fragment that establishes an absolute containing block for its descendants (i.e.
- /// a positioned inline fragment).
- InlineAbsolute(InlineAbsoluteFragmentInfo),
-
- ScannedText(Box<ScannedTextFragmentInfo>),
- Table,
- TableCell,
- TableColumn(TableColumnFragmentInfo),
- TableRow,
- TableWrapper,
- Multicol,
- MulticolColumn,
- UnscannedText(Box<UnscannedTextFragmentInfo>),
-
- /// A container for a fragment that got truncated by text-overflow.
- /// "Totally truncated fragments" are not rendered at all.
- /// Text fragments may be partially truncated (in which case this renders like a text fragment).
- /// Other fragments can only be totally truncated or not truncated at all.
- TruncatedFragment(Box<TruncatedFragmentInfo>),
}
impl SpecificFragmentInfo {
fn restyle_damage(&self) -> RestyleDamage {
- let flow = match *self {
- SpecificFragmentInfo::Canvas(_) |
- SpecificFragmentInfo::GeneratedContent(_) |
- SpecificFragmentInfo::Iframe(_) |
- SpecificFragmentInfo::Image(_) |
- SpecificFragmentInfo::Media(_) |
- SpecificFragmentInfo::ScannedText(_) |
- SpecificFragmentInfo::Svg(_) |
- SpecificFragmentInfo::Table |
- SpecificFragmentInfo::TableCell |
- SpecificFragmentInfo::TableColumn(_) |
- SpecificFragmentInfo::TableRow |
- SpecificFragmentInfo::TableWrapper |
- SpecificFragmentInfo::Multicol |
- SpecificFragmentInfo::MulticolColumn |
- SpecificFragmentInfo::UnscannedText(_) |
- SpecificFragmentInfo::TruncatedFragment(_) |
- SpecificFragmentInfo::Generic => return RestyleDamage::empty(),
- SpecificFragmentInfo::InlineAbsoluteHypothetical(ref info) => &info.flow_ref,
- SpecificFragmentInfo::InlineAbsolute(ref info) => &info.flow_ref,
- SpecificFragmentInfo::InlineBlock(ref info) => &info.flow_ref,
- };
-
- flow.base().restyle_damage
- }
-
- pub fn get_type(&self) -> &'static str {
- match *self {
- SpecificFragmentInfo::Canvas(_) => "SpecificFragmentInfo::Canvas",
- SpecificFragmentInfo::Media(_) => "SpecificFragmentInfo::Media",
- SpecificFragmentInfo::Generic => "SpecificFragmentInfo::Generic",
- SpecificFragmentInfo::GeneratedContent(_) => "SpecificFragmentInfo::GeneratedContent",
- SpecificFragmentInfo::Iframe(_) => "SpecificFragmentInfo::Iframe",
- SpecificFragmentInfo::Image(_) => "SpecificFragmentInfo::Image",
- SpecificFragmentInfo::InlineAbsolute(_) => "SpecificFragmentInfo::InlineAbsolute",
- SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => {
- "SpecificFragmentInfo::InlineAbsoluteHypothetical"
- },
- SpecificFragmentInfo::InlineBlock(_) => "SpecificFragmentInfo::InlineBlock",
- SpecificFragmentInfo::ScannedText(_) => "SpecificFragmentInfo::ScannedText",
- SpecificFragmentInfo::Svg(_) => "SpecificFragmentInfo::Svg",
- SpecificFragmentInfo::Table => "SpecificFragmentInfo::Table",
- SpecificFragmentInfo::TableCell => "SpecificFragmentInfo::TableCell",
- SpecificFragmentInfo::TableColumn(_) => "SpecificFragmentInfo::TableColumn",
- SpecificFragmentInfo::TableRow => "SpecificFragmentInfo::TableRow",
- SpecificFragmentInfo::TableWrapper => "SpecificFragmentInfo::TableWrapper",
- SpecificFragmentInfo::Multicol => "SpecificFragmentInfo::Multicol",
- SpecificFragmentInfo::MulticolColumn => "SpecificFragmentInfo::MulticolColumn",
- SpecificFragmentInfo::UnscannedText(_) => "SpecificFragmentInfo::UnscannedText",
- SpecificFragmentInfo::TruncatedFragment(_) => "SpecificFragmentInfo::TruncatedFragment",
- }
- }
-}
-
-impl fmt::Debug for SpecificFragmentInfo {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- SpecificFragmentInfo::ScannedText(ref info) => write!(f, "{:?}", info.text()),
- SpecificFragmentInfo::UnscannedText(ref info) => write!(f, "{:?}", info.text),
- _ => Ok(()),
- }
- }
-}
-
-/// Information for generated content.
-#[derive(Clone)]
-pub enum GeneratedContentInfo {
- ListItem,
- ContentItem(ContentItem),
- /// Placeholder for elements with generated content that did not generate any fragments.
- Empty,
-}
-
-/// A hypothetical box (see CSS 2.1 § 10.3.7) for an absolutely-positioned block that was declared
-/// with `display: inline;`.
-///
-/// FIXME(pcwalton): Stop leaking this `FlowRef` to layout; that is not memory safe because layout
-/// can clone it.
-#[derive(Clone)]
-pub struct InlineAbsoluteHypotheticalFragmentInfo {
- pub flow_ref: FlowRef,
-}
-
-impl InlineAbsoluteHypotheticalFragmentInfo {
- pub fn new(flow_ref: FlowRef) -> InlineAbsoluteHypotheticalFragmentInfo {
- InlineAbsoluteHypotheticalFragmentInfo { flow_ref: flow_ref }
- }
-}
-
-/// A fragment that represents an inline-block element.
-///
-/// FIXME(pcwalton): Stop leaking this `FlowRef` to layout; that is not memory safe because layout
-/// can clone it.
-#[derive(Clone)]
-pub struct InlineBlockFragmentInfo {
- pub flow_ref: FlowRef,
-}
-
-impl InlineBlockFragmentInfo {
- pub fn new(flow_ref: FlowRef) -> InlineBlockFragmentInfo {
- InlineBlockFragmentInfo { flow_ref: flow_ref }
- }
-}
-
-/// An inline fragment that establishes an absolute containing block for its descendants (i.e.
-/// a positioned inline fragment).
-///
-/// FIXME(pcwalton): Stop leaking this `FlowRef` to layout; that is not memory safe because layout
-/// can clone it.
-#[derive(Clone)]
-pub struct InlineAbsoluteFragmentInfo {
- pub flow_ref: FlowRef,
-}
-
-impl InlineAbsoluteFragmentInfo {
- pub fn new(flow_ref: FlowRef) -> InlineAbsoluteFragmentInfo {
- InlineAbsoluteFragmentInfo { flow_ref: flow_ref }
- }
-}
-
-#[derive(Clone)]
-pub enum CanvasFragmentSource {
- WebGL(webrender_api::ImageKey),
- Image(Option<Arc<Mutex<IpcSender<CanvasMsg>>>>),
-}
-
-#[derive(Clone)]
-pub struct CanvasFragmentInfo {
- pub source: CanvasFragmentSource,
- pub dom_width: Au,
- pub dom_height: Au,
- pub canvas_id: CanvasId,
-}
-
-impl CanvasFragmentInfo {
- pub fn new(data: HTMLCanvasData) -> CanvasFragmentInfo {
- let source = match data.source {
- HTMLCanvasDataSource::WebGL(texture_id) => CanvasFragmentSource::WebGL(texture_id),
- HTMLCanvasDataSource::Image(ipc_sender) => CanvasFragmentSource::Image(
- ipc_sender.map(|renderer| Arc::new(Mutex::new(renderer))),
- ),
- };
-
- CanvasFragmentInfo {
- source: source,
- dom_width: Au::from_px(data.width as i32),
- dom_height: Au::from_px(data.height as i32),
- canvas_id: data.canvas_id,
- }
- }
-}
-
-#[derive(Clone)]
-pub struct MediaFragmentInfo {
- pub current_frame: Option<(webrender_api::ImageKey, i32, i32)>,
-}
-
-impl MediaFragmentInfo {
- pub fn new(data: HTMLMediaData) -> MediaFragmentInfo {
- MediaFragmentInfo {
- current_frame: data.current_frame,
- }
- }
-}
-
-#[derive(Clone)]
-pub struct SvgFragmentInfo {
- pub dom_width: Au,
- pub dom_height: Au,
-}
-
-impl SvgFragmentInfo {
- pub fn new(data: SVGSVGData) -> SvgFragmentInfo {
- SvgFragmentInfo {
- dom_width: Au::from_px(data.width as i32),
- dom_height: Au::from_px(data.height as i32),
- }
- }
-}
-
-/// A fragment that represents a replaced content image and its accompanying borders, shadows, etc.
-#[derive(Clone)]
-pub struct ImageFragmentInfo {
- pub image: Option<Arc<Image>>,
- pub metadata: Option<ImageMetadata>,
-}
-
-enum ImageOrMetadata {
- Image(Arc<Image>),
- Metadata(ImageMetadata),
-}
-
-impl ImageFragmentInfo {
- /// Creates a new image fragment from the given URL and local image cache.
- ///
- /// FIXME(pcwalton): The fact that image fragments store the cache in the fragment makes little
- /// sense to me.
- pub fn new<N: ThreadSafeLayoutNode>(
- url: Option<ServoUrl>,
- density: Option<f64>,
- node: &N,
- layout_context: &LayoutContext,
- ) -> ImageFragmentInfo {
- // First use any image data present in the element...
- let image_or_metadata = node
- .image_data()
- .and_then(|(image, metadata)| match (image, metadata) {
- (Some(image), _) => Some(ImageOrMetadata::Image(image)),
- (None, Some(metadata)) => Some(ImageOrMetadata::Metadata(metadata)),
- _ => None,
- })
- .or_else(|| {
- url.and_then(|url| {
- // Otherwise query the image cache for anything known about the associated source URL.
- layout_context
- .get_or_request_image_or_meta(node.opaque(), url, UsePlaceholder::Yes)
- .map(|result| match result {
- ImageOrMetadataAvailable::ImageAvailable(i, _) => {
- ImageOrMetadata::Image(i)
- },
- ImageOrMetadataAvailable::MetadataAvailable(m) => {
- ImageOrMetadata::Metadata(m)
- },
- })
- })
- });
-
- let current_pixel_density = density.unwrap_or(1f64);
-
- let (image, metadata) = match image_or_metadata {
- Some(ImageOrMetadata::Image(i)) => {
- let height = (i.height as f64 / current_pixel_density) as u32;
- let width = (i.width as f64 / current_pixel_density) as u32;
- (
- Some(Arc::new(Image {
- height: height,
- width: width,
- ..(*i).clone()
- })),
- Some(ImageMetadata {
- height: height,
- width: width,
- }),
- )
- },
- Some(ImageOrMetadata::Metadata(m)) => (
- None,
- Some(ImageMetadata {
- height: (m.height as f64 / current_pixel_density) as u32,
- width: (m.width as f64 / current_pixel_density) as u32,
- }),
- ),
- None => (None, None),
- };
-
- ImageFragmentInfo {
- image: image,
- metadata: metadata,
- }
- }
-}
-
-/// A fragment that represents an inline frame (iframe). This stores the frame ID so that the
-/// size of this iframe can be communicated via the constellation to the iframe's own layout thread.
-#[derive(Clone)]
-pub struct IframeFragmentInfo {
- /// The frame ID of this iframe. None if there is no nested browsing context.
- pub browsing_context_id: Option<BrowsingContextId>,
- /// The pipelineID of this iframe. None if there is no nested browsing context.
- pub pipeline_id: Option<PipelineId>,
-}
-
-impl IframeFragmentInfo {
- /// Creates the information specific to an iframe fragment.
- pub fn new<N: ThreadSafeLayoutNode>(node: &N) -> IframeFragmentInfo {
- let browsing_context_id = node.iframe_browsing_context_id();
- let pipeline_id = node.iframe_pipeline_id();
- IframeFragmentInfo {
- browsing_context_id: browsing_context_id,
- pipeline_id: pipeline_id,
- }
- }
-}
-
-/// A scanned text fragment represents a single run of text with a distinct style. A `TextFragment`
-/// may be split into two or more fragments across line breaks. Several `TextFragment`s may
-/// correspond to a single DOM text node. Split text fragments are implemented by referring to
-/// subsets of a single `TextRun` object.
-#[derive(Clone)]
-pub struct ScannedTextFragmentInfo {
- /// The text run that this represents.
- pub run: Arc<TextRun>,
-
- /// The intrinsic size of the text fragment.
- pub content_size: LogicalSize<Au>,
-
- /// The byte offset of the insertion point, if any.
- pub insertion_point: Option<ByteIndex>,
-
- /// The range within the above text run that this represents.
- pub range: Range<ByteIndex>,
-
- /// The endpoint of the above range, including whitespace that was stripped out. This exists
- /// so that we can restore the range to its original value (before line breaking occurred) when
- /// performing incremental reflow.
- pub range_end_including_stripped_whitespace: ByteIndex,
-
- pub flags: ScannedTextFlags,
-}
-
-bitflags! {
- pub struct ScannedTextFlags: u8 {
- /// Whether a line break is required after this fragment if wrapping on newlines (e.g. if
- /// `white-space: pre` is in effect).
- const REQUIRES_LINE_BREAK_AFTERWARD_IF_WRAPPING_ON_NEWLINES = 0x01;
-
- /// Is this fragment selected?
- const SELECTED = 0x02;
-
- /// Suppress line breaking between this and the previous fragment
- ///
- /// This handles cases like Foo<span>bar</span>
- const SUPPRESS_LINE_BREAK_BEFORE = 0x04;
- }
-}
-
-impl ScannedTextFragmentInfo {
- /// Creates the information specific to a scanned text fragment from a range and a text run.
- pub fn new(
- run: Arc<TextRun>,
- range: Range<ByteIndex>,
- content_size: LogicalSize<Au>,
- insertion_point: Option<ByteIndex>,
- flags: ScannedTextFlags,
- ) -> ScannedTextFragmentInfo {
- ScannedTextFragmentInfo {
- run: run,
- range: range,
- insertion_point: insertion_point,
- content_size: content_size,
- range_end_including_stripped_whitespace: range.end(),
- flags: flags,
- }
- }
-
- pub fn text(&self) -> &str {
- &self.run.text[self.range.begin().to_usize()..self.range.end().to_usize()]
+ RestyleDamage::empty()
}
-
- pub fn requires_line_break_afterward_if_wrapping_on_newlines(&self) -> bool {
- self.flags
- .contains(ScannedTextFlags::REQUIRES_LINE_BREAK_AFTERWARD_IF_WRAPPING_ON_NEWLINES)
- }
-
- pub fn selected(&self) -> bool {
- self.flags.contains(ScannedTextFlags::SELECTED)
- }
-}
-
-/// Describes how to split a fragment. This is used during line breaking as part of the return
-/// value of `find_split_info_for_inline_size()`.
-#[derive(Clone, Debug)]
-pub struct SplitInfo {
- // TODO(bjz): this should only need to be a single character index, but both values are
- // currently needed for splitting in the `inline::try_append_*` functions.
- pub range: Range<ByteIndex>,
- pub inline_size: Au,
-}
-
-impl SplitInfo {
- fn new(range: Range<ByteIndex>, info: &ScannedTextFragmentInfo) -> SplitInfo {
- let inline_size = info.run.advance_for_range(&range);
- SplitInfo {
- range: range,
- inline_size: inline_size,
- }
- }
-}
-
-/// Describes how to split a fragment into two. This contains up to two `SplitInfo`s.
-pub struct SplitResult {
- /// The part of the fragment that goes on the first line.
- pub inline_start: Option<SplitInfo>,
- /// The part of the fragment that goes on the second line.
- pub inline_end: Option<SplitInfo>,
- /// The text run which is being split.
- pub text_run: Arc<TextRun>,
-}
-
-/// Describes how a fragment should be truncated.
-struct TruncationResult {
- /// The part of the fragment remaining after truncation.
- split: SplitInfo,
- /// The text run which is being truncated.
- text_run: Arc<TextRun>,
-}
-
-/// Data for an unscanned text fragment. Unscanned text fragments are the results of flow
-/// construction that have not yet had their inline-size determined.
-#[derive(Clone)]
-pub struct UnscannedTextFragmentInfo {
- /// The text inside the fragment.
- pub text: Box<str>,
-
- /// The selected text range. An empty range represents the insertion point.
- pub selection: Option<Range<ByteIndex>>,
-}
-
-impl UnscannedTextFragmentInfo {
- /// Creates a new instance of `UnscannedTextFragmentInfo` from the given text.
- #[inline]
- pub fn new(text: Box<str>, selection: Option<Range<ByteIndex>>) -> UnscannedTextFragmentInfo {
- UnscannedTextFragmentInfo {
- text: text,
- selection: selection,
- }
- }
-}
-
-/// A fragment that represents a table column.
-#[derive(Clone, Copy)]
-pub struct TableColumnFragmentInfo {
- /// the number of columns a <col> element should span
- pub span: u32,
-}
-
-impl TableColumnFragmentInfo {
- /// Create the information specific to an table column fragment.
- pub fn new<N: ThreadSafeLayoutNode>(node: &N) -> TableColumnFragmentInfo {
- let element = node.as_element().unwrap();
- let span = element
- .get_attr(&ns!(), &local_name!("span"))
- .and_then(|string| string.parse().ok())
- .unwrap_or(0);
- TableColumnFragmentInfo { span: span }
- }
-}
-
-/// A wrapper for fragments that have been truncated by the `text-overflow` property.
-/// This may have an associated text node, or, if the fragment was completely truncated,
-/// it may act as an invisible marker for incremental reflow.
-#[derive(Clone)]
-pub struct TruncatedFragmentInfo {
- pub text_info: Option<ScannedTextFragmentInfo>,
- pub full: Fragment,
}
impl Fragment {
@@ -673,836 +59,34 @@ impl Fragment {
let style = node.style(shared_context);
let writing_mode = style.writing_mode;
- let mut restyle_damage = node.restyle_damage();
+ let mut restyle_damage = RestyleDamage::rebuild_and_reflow();
restyle_damage.remove(ServoRestyleDamage::RECONSTRUCT_FLOW);
Fragment {
node: node.opaque(),
style: style,
- selected_style: node.selected_style(),
restyle_damage: restyle_damage,
border_box: LogicalRect::zero(writing_mode),
border_padding: LogicalMargin::zero(writing_mode),
margin: LogicalMargin::zero(writing_mode),
specific: specific,
- inline_context: None,
pseudo: node.get_pseudo_element_type(),
- flags: FragmentFlags::empty(),
- debug_id: DebugId::new(),
- stacking_context_id: StackingContextId::root(),
- established_reference_frame: None,
}
}
- /// Constructs a new `Fragment` instance from an opaque node.
- pub fn from_opaque_node_and_style(
- node: OpaqueNode,
- pseudo: PseudoElementType,
- style: ServoArc<ComputedValues>,
- selected_style: ServoArc<ComputedValues>,
- mut restyle_damage: RestyleDamage,
- specific: SpecificFragmentInfo,
- ) -> Fragment {
- let writing_mode = style.writing_mode;
-
- restyle_damage.remove(ServoRestyleDamage::RECONSTRUCT_FLOW);
-
- Fragment {
- node: node,
- style: style,
- selected_style: selected_style,
- restyle_damage: restyle_damage,
- border_box: LogicalRect::zero(writing_mode),
- border_padding: LogicalMargin::zero(writing_mode),
- margin: LogicalMargin::zero(writing_mode),
- specific: specific,
- inline_context: None,
- pseudo: pseudo,
- flags: FragmentFlags::empty(),
- debug_id: DebugId::new(),
- stacking_context_id: StackingContextId::root(),
- established_reference_frame: None,
- }
- }
-
- /// Creates an anonymous fragment just like this one but with the given style and fragment
- /// type. For the new anonymous fragment, layout-related values (border box, etc.) are reset to
- /// initial values.
- pub fn create_similar_anonymous_fragment(
- &self,
- style: ServoArc<ComputedValues>,
- specific: SpecificFragmentInfo,
- ) -> Fragment {
- let writing_mode = style.writing_mode;
- Fragment {
- node: self.node,
- style: style,
- selected_style: self.selected_style.clone(),
- restyle_damage: self.restyle_damage,
- border_box: LogicalRect::zero(writing_mode),
- border_padding: LogicalMargin::zero(writing_mode),
- margin: LogicalMargin::zero(writing_mode),
- specific: specific,
- inline_context: None,
- pseudo: self.pseudo,
- flags: FragmentFlags::empty(),
- debug_id: DebugId::new(),
- stacking_context_id: StackingContextId::root(),
- established_reference_frame: None,
- }
- }
-
- /// Transforms this fragment into another fragment of the given type, with the given size,
- /// preserving all the other data.
- pub fn transform(&self, size: LogicalSize<Au>, info: SpecificFragmentInfo) -> Fragment {
- let new_border_box =
- LogicalRect::from_point_size(self.style.writing_mode, self.border_box.start, size);
-
- let mut restyle_damage = RestyleDamage::rebuild_and_reflow();
- restyle_damage.remove(ServoRestyleDamage::RECONSTRUCT_FLOW);
-
- Fragment {
- node: self.node,
- style: self.style.clone(),
- selected_style: self.selected_style.clone(),
- restyle_damage: restyle_damage,
- border_box: new_border_box,
- border_padding: self.border_padding,
- margin: self.margin,
- specific: info,
- inline_context: self.inline_context.clone(),
- pseudo: self.pseudo.clone(),
- flags: FragmentFlags::empty(),
- debug_id: self.debug_id.clone(),
- stacking_context_id: StackingContextId::root(),
- established_reference_frame: None,
- }
- }
-
- /// Transforms this fragment using the given `SplitInfo`, preserving all the other data.
- ///
- /// If this is the first half of a split, `first` is true
- pub fn transform_with_split_info(
- &self,
- split: &SplitInfo,
- text_run: Arc<TextRun>,
- first: bool,
- ) -> Fragment {
- let size = LogicalSize::new(
- self.style.writing_mode,
- split.inline_size,
- self.border_box.size.block,
- );
- // Preserve the insertion point if it is in this fragment's range or it is at line end.
- let (mut flags, insertion_point) = match self.specific {
- SpecificFragmentInfo::ScannedText(ref info) => match info.insertion_point {
- Some(index) if split.range.contains(index) => (info.flags, info.insertion_point),
- Some(index)
- if index == ByteIndex(text_run.text.chars().count() as isize - 1) &&
- index == split.range.end() =>
- {
- (info.flags, info.insertion_point)
- },
- _ => (info.flags, None),
- },
- _ => (ScannedTextFlags::empty(), None),
- };
-
- if !first {
- flags.set(ScannedTextFlags::SUPPRESS_LINE_BREAK_BEFORE, false);
- }
-
- let info = Box::new(ScannedTextFragmentInfo::new(
- text_run,
- split.range,
- size,
- insertion_point,
- flags,
- ));
- self.transform(size, SpecificFragmentInfo::ScannedText(info))
- }
-
- /// Transforms this fragment into an ellipsis fragment, preserving all the other data.
- pub fn transform_into_ellipsis(
- &self,
- layout_context: &LayoutContext,
- text_overflow_string: String,
- ) -> Fragment {
- let mut unscanned_ellipsis_fragments = LinkedList::new();
- let mut ellipsis_fragment = self.transform(
- self.border_box.size,
- SpecificFragmentInfo::UnscannedText(Box::new(UnscannedTextFragmentInfo::new(
- text_overflow_string.into_boxed_str(),
- None,
- ))),
- );
- unscanned_ellipsis_fragments.push_back(ellipsis_fragment);
- let ellipsis_fragments = with_thread_local_font_context(layout_context, |font_context| {
- TextRunScanner::new().scan_for_runs(font_context, unscanned_ellipsis_fragments)
- });
- debug_assert_eq!(ellipsis_fragments.len(), 1);
- ellipsis_fragment = ellipsis_fragments.fragments.into_iter().next().unwrap();
- ellipsis_fragment.flags |= FragmentFlags::IS_ELLIPSIS;
- ellipsis_fragment
- }
-
pub fn restyle_damage(&self) -> RestyleDamage {
self.restyle_damage | self.specific.restyle_damage()
}
pub fn contains_node(&self, node_address: OpaqueNode) -> bool {
- node_address == self.node ||
- self.inline_context
- .as_ref()
- .map_or(false, |ctx| ctx.contains_node(node_address))
- }
-
- /// Adds a style to the inline context for this fragment. If the inline context doesn't exist
- /// yet, it will be created.
- pub fn add_inline_context_style(&mut self, node_info: InlineFragmentNodeInfo) {
- if self.inline_context.is_none() {
- self.inline_context = Some(InlineFragmentContext::new());
- }
- self.inline_context.as_mut().unwrap().nodes.push(node_info);
- }
-
- /// Determines which quantities (border/padding/margin/specified) should be included in the
- /// intrinsic inline size of this fragment.
- fn quantities_included_in_intrinsic_inline_size(
- &self,
- ) -> QuantitiesIncludedInIntrinsicInlineSizes {
- match self.specific {
- SpecificFragmentInfo::Canvas(_) |
- SpecificFragmentInfo::Media(_) |
- SpecificFragmentInfo::Generic |
- SpecificFragmentInfo::GeneratedContent(_) |
- SpecificFragmentInfo::Iframe(_) |
- SpecificFragmentInfo::Image(_) |
- SpecificFragmentInfo::InlineAbsolute(_) |
- SpecificFragmentInfo::Multicol |
- SpecificFragmentInfo::Svg(_) => {
- QuantitiesIncludedInIntrinsicInlineSizes::all()
- }
- SpecificFragmentInfo::Table => {
- QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED |
- QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_PADDING |
- QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_BORDER
- }
- SpecificFragmentInfo::TableCell => {
- let base_quantities = QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_PADDING |
- QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED;
- if self.style.get_inherited_table().border_collapse ==
- BorderCollapse::Separate {
- base_quantities | QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_BORDER
- } else {
- base_quantities
- }
- }
- SpecificFragmentInfo::TableWrapper => {
- let base_quantities = QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_MARGINS |
- QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED;
- if self.style.get_inherited_table().border_collapse ==
- BorderCollapse::Separate {
- base_quantities | QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_BORDER
- } else {
- base_quantities
- }
- }
- SpecificFragmentInfo::TableRow => {
- let base_quantities =
- QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED;
- if self.style.get_inherited_table().border_collapse ==
- BorderCollapse::Separate {
- base_quantities | QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_BORDER
- } else {
- base_quantities
- }
- }
- SpecificFragmentInfo::TruncatedFragment(_) |
- SpecificFragmentInfo::ScannedText(_) |
- SpecificFragmentInfo::TableColumn(_) |
- SpecificFragmentInfo::UnscannedText(_) |
- SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
- SpecificFragmentInfo::InlineBlock(_) |
- SpecificFragmentInfo::MulticolColumn => {
- QuantitiesIncludedInIntrinsicInlineSizes::empty()
- }
- }
- }
-
- /// Returns the portion of the intrinsic inline-size that consists of borders/padding and
- /// margins, respectively.
- ///
- /// FIXME(#2261, pcwalton): This won't work well for inlines: is this OK?
- pub fn surrounding_intrinsic_inline_size(&self) -> (Au, Au) {
- let flags = self.quantities_included_in_intrinsic_inline_size();
- let style = self.style();
-
- // FIXME(pcwalton): Percentages should be relative to any definite size per CSS-SIZING.
- // This will likely need to be done by pushing down definite sizes during selector
- // cascading.
- let margin = if flags.contains(
- QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_MARGINS,
- ) {
- let margin = style.logical_margin();
- (MaybeAuto::from_style(margin.inline_start, Au(0)).specified_or_zero() +
- MaybeAuto::from_style(margin.inline_end, Au(0)).specified_or_zero())
- } else {
- Au(0)
- };
-
- // FIXME(pcwalton): Percentages should be relative to any definite size per CSS-SIZING.
- // This will likely need to be done by pushing down definite sizes during selector
- // cascading.
- let padding = if flags.contains(
- QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_PADDING,
- ) {
- let padding = style.logical_padding();
- (padding.inline_start.to_used_value(Au(0)) + padding.inline_end.to_used_value(Au(0)))
- } else {
- Au(0)
- };
-
- let border = if flags.contains(
- QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_BORDER,
- ) {
- self.border_width().inline_start_end()
- } else {
- Au(0)
- };
-
- (border + padding, margin)
- }
-
- /// Uses the style only to estimate the intrinsic inline-sizes. These may be modified for text
- /// or replaced elements.
- pub fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizesContribution {
- let flags = self.quantities_included_in_intrinsic_inline_size();
- let style = self.style();
-
- // FIXME(#2261, pcwalton): This won't work well for inlines: is this OK?
- let (border_padding, margin) = self.surrounding_intrinsic_inline_size();
-
- let mut specified = Au(0);
- if flags.contains(
- QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED,
- ) {
- specified = style
- .content_inline_size()
- .to_used_value(Au(0))
- .unwrap_or(Au(0));
- specified = max(
- style
- .min_inline_size()
- .to_used_value(Au(0))
- .unwrap_or(Au(0)),
- specified,
- );
- if let Some(max) = style.max_inline_size().to_used_value(Au(0)) {
- specified = min(specified, max)
- }
-
- if self.style.get_position().box_sizing == BoxSizing::BorderBox {
- specified = max(Au(0), specified - border_padding);
- }
- }
-
- IntrinsicISizesContribution {
- content_intrinsic_sizes: IntrinsicISizes {
- minimum_inline_size: specified,
- preferred_inline_size: specified,
- },
- surrounding_size: border_padding + margin,
- }
- }
-
- /// 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::Media(ref info) => {
- if let Some((_, width, _)) = info.current_frame {
- Au::from_px(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::Media(ref info) => {
- if let Some((_, _, height)) = info.current_frame {
- Au::from_px(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(_) |
- SpecificFragmentInfo::Media(_) |
- // 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.
- Au::new(
- (inline_size.0 as i64 * intrinsic_block_size.0 as i64 /
- intrinsic_inline_size.0 as i64) as i32,
- )
- } 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() {
- Au::new(
- (block_size.0 as i64 * intrinsic_inline_size.0 as i64 /
- intrinsic_block_size.0 as i64) as i32,
- )
- } 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 = Au::new(
- (first_isize.0 as i64 * intrinsic_block_size.0 as i64 /
- intrinsic_inline_size.0 as i64) as i32,
- );
- let second_bsize = block_constraint.clamp(intrinsic_block_size);
- let second_isize = Au::new(
- (second_bsize.0 as i64 * intrinsic_inline_size.0 as i64 /
- intrinsic_block_size.0 as i64) as i32,
- );
- 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 == BoxSizing::BorderBox {
- 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.
- ///
- /// This is used for the float placement speculation logic.
- pub fn guess_inline_content_edge_offsets(&self) -> SpeculatedInlineContentEdgeOffsets {
- let logical_margin = self.style.logical_margin();
- let logical_padding = self.style.logical_padding();
- let border_width = self.border_width();
- SpeculatedInlineContentEdgeOffsets {
- start: MaybeAuto::from_style(logical_margin.inline_start, Au(0)).specified_or_zero() +
- logical_padding.inline_start.to_used_value(Au(0)) +
- border_width.inline_start,
- end: MaybeAuto::from_style(logical_margin.inline_end, Au(0)).specified_or_zero() +
- logical_padding.inline_end.to_used_value(Au(0)) +
- border_width.inline_end,
- }
+ node_address == self.node
}
/// Returns the sum of the inline-sizes of all the borders of this fragment. Note that this
/// can be expensive to compute, so if possible use the `border_padding` field instead.
#[inline]
pub fn border_width(&self) -> LogicalMargin<Au> {
- let style_border_width = self.style().logical_border_width();
-
- // NOTE: We can have nodes with different writing mode inside
- // the inline fragment context, so we need to overwrite the
- // writing mode to compute the child logical sizes.
- let writing_mode = self.style.writing_mode;
- let context_border = match self.inline_context {
- None => LogicalMargin::zero(writing_mode),
- Some(ref inline_fragment_context) => inline_fragment_context.nodes.iter().fold(
- style_border_width,
- |accumulator, node| {
- let mut this_border_width =
- node.style.border_width_for_writing_mode(writing_mode);
- if !node
- .flags
- .contains(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT)
- {
- this_border_width.inline_start = Au(0)
- }
- if !node
- .flags
- .contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT)
- {
- this_border_width.inline_end = Au(0)
- }
- accumulator + this_border_width
- },
- ),
- };
- style_border_width + context_border
- }
-
- /// Returns the border width in given direction if this fragment has property
- /// '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) {
- (BoxSizing::BorderBox, Direction::Inline) => self.border_padding.inline_start_end(),
- (BoxSizing::BorderBox, Direction::Block) => self.border_padding.block_start_end(),
- _ => Au(0),
- }
- }
-
- /// Computes the margins in the inline direction from the containing block inline-size and the
- /// style. After this call, the inline direction of the `margin` field will be correct.
- ///
- /// Do not use this method if the inline direction margins are to be computed some other way
- /// (for example, via constraint solving for blocks).
- pub fn compute_inline_direction_margins(&mut self, containing_block_inline_size: Au) {
- match self.specific {
- SpecificFragmentInfo::Table |
- SpecificFragmentInfo::TableCell |
- SpecificFragmentInfo::TableRow |
- SpecificFragmentInfo::TableColumn(_) |
- SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => {
- self.margin.inline_start = Au(0);
- self.margin.inline_end = Au(0);
- return;
- },
- _ => {
- let margin = self.style().logical_margin();
- self.margin.inline_start =
- MaybeAuto::from_style(margin.inline_start, containing_block_inline_size)
- .specified_or_zero();
- self.margin.inline_end =
- MaybeAuto::from_style(margin.inline_end, containing_block_inline_size)
- .specified_or_zero();
- },
- }
-
- if let Some(ref inline_context) = self.inline_context {
- for node in &inline_context.nodes {
- let margin = node.style.logical_margin();
- let this_inline_start_margin = if !node
- .flags
- .contains(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT)
- {
- Au(0)
- } else {
- MaybeAuto::from_style(margin.inline_start, containing_block_inline_size)
- .specified_or_zero()
- };
- let this_inline_end_margin = if !node
- .flags
- .contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT)
- {
- Au(0)
- } else {
- MaybeAuto::from_style(margin.inline_end, containing_block_inline_size)
- .specified_or_zero()
- };
-
- self.margin.inline_start += this_inline_start_margin;
- self.margin.inline_end += this_inline_end_margin;
- }
- }
- }
-
- /// Computes the margins in the block direction from the containing block inline-size and the
- /// style. After this call, the block direction of the `margin` field will be correct.
- ///
- /// Do not use this method if the block direction margins are to be computed some other way
- /// (for example, via constraint solving for absolutely-positioned flows).
- pub fn compute_block_direction_margins(&mut self, containing_block_inline_size: Au) {
- match self.specific {
- SpecificFragmentInfo::Table |
- SpecificFragmentInfo::TableCell |
- SpecificFragmentInfo::TableRow |
- SpecificFragmentInfo::TableColumn(_) => {
- self.margin.block_start = Au(0);
- self.margin.block_end = Au(0)
- },
- _ => {
- // NB: Percentages are relative to containing block inline-size (not block-size)
- // per CSS 2.1.
- let margin = self.style().logical_margin();
- self.margin.block_start =
- MaybeAuto::from_style(margin.block_start, containing_block_inline_size)
- .specified_or_zero();
- self.margin.block_end =
- MaybeAuto::from_style(margin.block_end, containing_block_inline_size)
- .specified_or_zero();
- },
- }
- }
-
- /// Computes the border and padding in both inline and block directions from the containing
- /// block inline-size and the style. After this call, the `border_padding` field will be
- /// correct.
- pub fn compute_border_and_padding(&mut self, containing_block_inline_size: Au) {
- // Compute border.
- let border = match self.style.get_inherited_table().border_collapse {
- BorderCollapse::Separate => self.border_width(),
- BorderCollapse::Collapse => LogicalMargin::zero(self.style.writing_mode),
- };
-
- // Compute padding from the fragment's style.
- let padding_from_style = match self.specific {
- SpecificFragmentInfo::TableColumn(_) |
- SpecificFragmentInfo::TableRow |
- SpecificFragmentInfo::TableWrapper => LogicalMargin::zero(self.style.writing_mode),
- _ => model::padding_from_style(
- self.style(),
- containing_block_inline_size,
- self.style().writing_mode,
- ),
- };
-
- // Compute padding from the inline fragment context.
- let padding_from_inline_fragment_context = match (&self.specific, &self.inline_context) {
- (_, &None) |
- (&SpecificFragmentInfo::TableColumn(_), _) |
- (&SpecificFragmentInfo::TableRow, _) |
- (&SpecificFragmentInfo::TableWrapper, _) => {
- LogicalMargin::zero(self.style.writing_mode)
- },
- (_, &Some(ref inline_fragment_context)) => {
- let writing_mode = self.style.writing_mode;
- let zero_padding = LogicalMargin::zero(writing_mode);
- inline_fragment_context
- .nodes
- .iter()
- .fold(zero_padding, |accumulator, node| {
- let mut padding =
- model::padding_from_style(&*node.style, Au(0), writing_mode);
- if !node
- .flags
- .contains(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT)
- {
- padding.inline_start = Au(0)
- }
- if !node
- .flags
- .contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT)
- {
- padding.inline_end = Au(0)
- }
- accumulator + padding
- })
- },
- };
-
- self.border_padding = border + padding_from_style + padding_from_inline_fragment_context
- }
-
- // Return offset from original position because of `position: relative`.
- pub fn relative_position(&self, containing_block_size: &LogicalSize<Au>) -> LogicalSize<Au> {
- fn from_style(style: &ComputedValues, container_size: &LogicalSize<Au>) -> LogicalSize<Au> {
- let offsets = style.logical_position();
- let offset_i = if offsets.inline_start != LengthPercentageOrAuto::Auto {
- MaybeAuto::from_style(offsets.inline_start, container_size.inline)
- .specified_or_zero()
- } else {
- -MaybeAuto::from_style(offsets.inline_end, container_size.inline)
- .specified_or_zero()
- };
- let offset_b = if offsets.block_start != LengthPercentageOrAuto::Auto {
- MaybeAuto::from_style(offsets.block_start, container_size.block).specified_or_zero()
- } else {
- -MaybeAuto::from_style(offsets.block_end, container_size.block).specified_or_zero()
- };
- LogicalSize::new(style.writing_mode, offset_i, offset_b)
- }
-
- // Go over the ancestor fragments and add all relative offsets (if any).
- let mut rel_pos = if self.style().get_box().position == Position::Relative {
- from_style(self.style(), containing_block_size)
- } else {
- LogicalSize::zero(self.style.writing_mode)
- };
-
- if let Some(ref inline_fragment_context) = self.inline_context {
- for node in &inline_fragment_context.nodes {
- if node.style.get_box().position == Position::Relative {
- rel_pos = rel_pos + from_style(&*node.style, containing_block_size);
- }
- }
- }
-
- rel_pos
- }
-
- /// Always inline for SCCP.
- ///
- /// FIXME(pcwalton): Just replace with the clear type from the style module for speed?
- #[inline(always)]
- pub fn clear(&self) -> Option<ClearType> {
- let style = self.style();
- match style.get_box().clear {
- Clear::None => None,
- Clear::Left => Some(ClearType::Left),
- Clear::Right => Some(ClearType::Right),
- Clear::Both => Some(ClearType::Both),
- }
+ self.style().logical_border_width()
}
#[inline(always)]
@@ -1510,1767 +94,8 @@ impl Fragment {
&*self.style
}
- #[inline(always)]
- pub fn selected_style(&self) -> &ComputedValues {
- &*self.selected_style
- }
-
- pub fn white_space(&self) -> WhiteSpace {
- self.style().get_inherited_text().white_space
- }
-
- pub fn color(&self) -> Color {
- self.style().get_inherited_text().color
- }
-
- /// Returns the text decoration line of this fragment, according to the style of the nearest ancestor
- /// element.
- ///
- /// NB: This may not be the actual text decoration line, because of the override rules specified in
- /// CSS 2.1 § 16.3.1. Unfortunately, computing this properly doesn't really fit into Servo's
- /// model. Therefore, this is a best lower bound approximation, but the end result may actually
- /// have the various decoration flags turned on afterward.
- pub fn text_decoration_line(&self) -> TextDecorationLine {
- self.style().get_text().text_decoration_line
- }
-
- /// Returns the inline-start offset from margin edge to content edge.
- ///
- /// FIXME(#2262, pcwalton): I think this method is pretty bogus, because it won't work for
- /// inlines.
- pub fn inline_start_offset(&self) -> Au {
- match self.specific {
- SpecificFragmentInfo::TableWrapper => self.margin.inline_start,
- SpecificFragmentInfo::Table |
- SpecificFragmentInfo::TableCell |
- SpecificFragmentInfo::TableRow => self.border_padding.inline_start,
- SpecificFragmentInfo::TableColumn(_) => Au(0),
- _ => self.margin.inline_start + self.border_padding.inline_start,
- }
- }
-
- /// If this is a Column fragment, get the col span
- ///
- /// Panics for non-column fragments
- pub fn column_span(&self) -> u32 {
- match self.specific {
- SpecificFragmentInfo::TableColumn(col_fragment) => max(col_fragment.span, 1),
- _ => panic!("non-table-column fragment inside table column?!"),
- }
- }
-
- /// Returns true if this element can be split. This is true for text fragments, unless
- /// `white-space: pre` or `white-space: nowrap` is set.
- pub fn can_split(&self) -> bool {
- self.is_scanned_text_fragment() && self.white_space().allow_wrap()
- }
-
- /// Returns true if and only if this fragment is a generated content fragment.
- pub fn is_unscanned_generated_content(&self) -> bool {
- match self.specific {
- SpecificFragmentInfo::GeneratedContent(ref content) => match **content {
- GeneratedContentInfo::Empty => false,
- _ => true,
- },
- _ => false,
- }
- }
-
- /// Returns true if and only if this is a scanned text fragment.
- pub fn is_scanned_text_fragment(&self) -> bool {
- match self.specific {
- SpecificFragmentInfo::ScannedText(..) => true,
- _ => false,
- }
- }
-
- pub fn suppress_line_break_before(&self) -> bool {
- match self.specific {
- SpecificFragmentInfo::ScannedText(ref st) => st
- .flags
- .contains(ScannedTextFlags::SUPPRESS_LINE_BREAK_BEFORE),
- _ => false,
- }
- }
-
- /// Computes the intrinsic inline-sizes of this fragment.
- pub fn compute_intrinsic_inline_sizes(&mut self) -> IntrinsicISizesContribution {
- let mut result = self.style_specified_intrinsic_inline_size();
- match self.specific {
- SpecificFragmentInfo::Generic |
- SpecificFragmentInfo::GeneratedContent(_) |
- SpecificFragmentInfo::Table |
- SpecificFragmentInfo::TableCell |
- SpecificFragmentInfo::TableColumn(_) |
- SpecificFragmentInfo::TableRow |
- SpecificFragmentInfo::TableWrapper |
- SpecificFragmentInfo::Multicol |
- SpecificFragmentInfo::MulticolColumn |
- SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => {},
- SpecificFragmentInfo::InlineBlock(ref info) => {
- let block_flow = info.flow_ref.as_block();
- result.union_block(&block_flow.base.intrinsic_inline_sizes)
- },
- SpecificFragmentInfo::InlineAbsolute(ref info) => {
- let block_flow = info.flow_ref.as_block();
- result.union_block(&block_flow.base.intrinsic_inline_sizes)
- },
- SpecificFragmentInfo::Image(_) |
- SpecificFragmentInfo::Media(_) |
- SpecificFragmentInfo::Canvas(_) |
- SpecificFragmentInfo::Iframe(_) |
- SpecificFragmentInfo::Svg(_) => {
- let inline_size = match self.style.content_inline_size() {
- Size::Auto => None,
- Size::LengthPercentage(ref lp) => lp.maybe_to_used_value(None),
- };
-
- let mut inline_size = inline_size.unwrap_or_else(|| {
- // 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 = padding.inline_start.to_used_value(Au(0));
- self.border_padding.inline_end = padding.inline_end.to_used_value(Au(0));
- self.border_padding.block_start = padding.block_start.to_used_value(Au(0));
- self.border_padding.block_end = padding.block_end.to_used_value(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
- });
-
- let size_constraint = self.size_constraint(None, Direction::Inline);
- inline_size = size_constraint.clamp(inline_size);
-
- result.union_block(&IntrinsicISizes {
- minimum_inline_size: inline_size,
- preferred_inline_size: inline_size,
- });
- },
-
- SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_some() => {
- let text_fragment_info = t.text_info.as_ref().unwrap();
- handle_text(text_fragment_info, self, &mut result)
- },
- SpecificFragmentInfo::ScannedText(ref text_fragment_info) => {
- handle_text(text_fragment_info, self, &mut result)
- },
-
- SpecificFragmentInfo::TruncatedFragment(_) => {
- return IntrinsicISizesContribution::new()
- },
-
- SpecificFragmentInfo::UnscannedText(..) => {
- panic!("Unscanned text fragments should have been scanned by now!")
- },
- };
-
- fn handle_text(
- text_fragment_info: &ScannedTextFragmentInfo,
- self_: &Fragment,
- result: &mut IntrinsicISizesContribution,
- ) {
- let range = &text_fragment_info.range;
-
- // See http://dev.w3.org/csswg/css-sizing/#max-content-inline-size.
- // TODO: Account for soft wrap opportunities.
- let max_line_inline_size = text_fragment_info
- .run
- .metrics_for_range(range)
- .advance_width;
-
- let min_line_inline_size = if self_.white_space().allow_wrap() {
- text_fragment_info.run.min_width_for_range(range)
- } else {
- max_line_inline_size
- };
-
- result.union_block(&IntrinsicISizes {
- minimum_inline_size: min_line_inline_size,
- preferred_inline_size: max_line_inline_size,
- })
- }
-
- // Take borders and padding for parent inline fragments into account.
- let writing_mode = self.style.writing_mode;
- if let Some(ref context) = self.inline_context {
- for node in &context.nodes {
- let mut border_width = node.style.logical_border_width();
- let mut padding = model::padding_from_style(&*node.style, Au(0), writing_mode);
- let mut margin = model::specified_margin_from_style(&*node.style, writing_mode);
- if !node
- .flags
- .contains(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT)
- {
- border_width.inline_start = Au(0);
- padding.inline_start = Au(0);
- margin.inline_start = Au(0);
- }
- if !node
- .flags
- .contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT)
- {
- border_width.inline_end = Au(0);
- padding.inline_end = Au(0);
- margin.inline_end = Au(0);
- }
-
- result.surrounding_size = result.surrounding_size +
- border_width.inline_start_end() +
- padding.inline_start_end() +
- margin.inline_start_end();
- }
- }
-
- result
- }
-
- /// Returns the narrowest inline-size that the first splittable part of this fragment could
- /// possibly be split to. (In most cases, this returns the inline-size of the first word in
- /// this fragment.)
- pub fn minimum_splittable_inline_size(&self) -> Au {
- match self.specific {
- SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_some() => {
- let text = t.text_info.as_ref().unwrap();
- text.run.minimum_splittable_inline_size(&text.range)
- },
- SpecificFragmentInfo::ScannedText(ref text) => {
- text.run.minimum_splittable_inline_size(&text.range)
- },
- _ => Au(0),
- }
- }
-
- /// Returns the dimensions of the content box.
- ///
- /// This is marked `#[inline]` because it is frequently called when only one or two of the
- /// values are needed and that will save computation.
- #[inline]
- pub fn content_box(&self) -> LogicalRect<Au> {
- self.border_box - self.border_padding
- }
-
- /// Attempts to find the split positions of a text fragment so that its inline-size is no more
- /// than `max_inline_size`.
- ///
- /// A return value of `None` indicates that the fragment could not be split. Otherwise the
- /// information pertaining to the split is returned. The inline-start and inline-end split
- /// information are both optional due to the possibility of them being whitespace.
- pub fn calculate_split_position(
- &self,
- max_inline_size: Au,
- starts_line: bool,
- ) -> Option<SplitResult> {
- let text_fragment_info = match self.specific {
- SpecificFragmentInfo::ScannedText(ref text_fragment_info) => text_fragment_info,
- _ => return None,
- };
-
- let mut flags = SplitOptions::empty();
- if starts_line {
- flags.insert(SplitOptions::STARTS_LINE);
- if self.style().get_inherited_text().overflow_wrap == OverflowWrap::BreakWord {
- flags.insert(SplitOptions::RETRY_AT_CHARACTER_BOUNDARIES)
- }
- }
-
- match self.style().get_inherited_text().word_break {
- WordBreak::Normal | WordBreak::KeepAll => {
- // Break at normal word boundaries. keep-all forbids soft wrap opportunities.
- let natural_word_breaking_strategy = text_fragment_info
- .run
- .natural_word_slices_in_range(&text_fragment_info.range);
- self.calculate_split_position_using_breaking_strategy(
- natural_word_breaking_strategy,
- max_inline_size,
- flags,
- )
- },
- WordBreak::BreakAll => {
- // Break at character boundaries.
- let character_breaking_strategy = text_fragment_info
- .run
- .character_slices_in_range(&text_fragment_info.range);
- flags.remove(SplitOptions::RETRY_AT_CHARACTER_BOUNDARIES);
- self.calculate_split_position_using_breaking_strategy(
- character_breaking_strategy,
- max_inline_size,
- flags,
- )
- },
- }
- }
-
- /// Does this fragment start on a glyph run boundary?
- pub fn is_on_glyph_run_boundary(&self) -> bool {
- let text_fragment_info = match self.specific {
- SpecificFragmentInfo::ScannedText(ref text_fragment_info) => text_fragment_info,
- _ => return true,
- };
- text_fragment_info
- .run
- .on_glyph_run_boundary(text_fragment_info.range.begin())
- }
-
- /// Truncates this fragment to the given `max_inline_size`, using a character-based breaking
- /// strategy. The resulting fragment will have `SpecificFragmentInfo::TruncatedFragment`,
- /// preserving the original fragment for use in incremental reflow.
- ///
- /// This function will panic if self is already truncated.
- pub fn truncate_to_inline_size(self, max_inline_size: Au) -> Fragment {
- if let SpecificFragmentInfo::TruncatedFragment(_) = self.specific {
- panic!("Cannot truncate an already truncated fragment");
- }
- let info = self.calculate_truncate_to_inline_size(max_inline_size);
- let (size, text_info) = match info {
- Some(TruncationResult {
- split: SplitInfo { inline_size, range },
- text_run,
- }) => {
- let size = LogicalSize::new(
- self.style.writing_mode,
- inline_size,
- self.border_box.size.block,
- );
- // Preserve the insertion point if it is in this fragment's range or it is at line end.
- let (flags, insertion_point) = match self.specific {
- SpecificFragmentInfo::ScannedText(ref info) => match info.insertion_point {
- Some(index) if range.contains(index) => (info.flags, info.insertion_point),
- Some(index)
- if index == ByteIndex(text_run.text.chars().count() as isize - 1) &&
- index == range.end() =>
- {
- (info.flags, info.insertion_point)
- },
- _ => (info.flags, None),
- },
- _ => (ScannedTextFlags::empty(), None),
- };
- let text_info =
- ScannedTextFragmentInfo::new(text_run, range, size, insertion_point, flags);
- (size, Some(text_info))
- },
- None => (LogicalSize::zero(self.style.writing_mode), None),
- };
- let mut result = self.transform(size, SpecificFragmentInfo::Generic);
- result.specific =
- SpecificFragmentInfo::TruncatedFragment(Box::new(TruncatedFragmentInfo {
- text_info: text_info,
- full: self,
- }));
- result
- }
-
- /// Truncates this fragment to the given `max_inline_size`, using a character-based breaking
- /// strategy. If no characters could fit, returns `None`.
- fn calculate_truncate_to_inline_size(&self, max_inline_size: Au) -> Option<TruncationResult> {
- let text_fragment_info =
- if let SpecificFragmentInfo::ScannedText(ref text_fragment_info) = self.specific {
- text_fragment_info
- } else {
- return None;
- };
-
- let character_breaking_strategy = text_fragment_info
- .run
- .character_slices_in_range(&text_fragment_info.range);
-
- let split_info = self.calculate_split_position_using_breaking_strategy(
- character_breaking_strategy,
- max_inline_size,
- SplitOptions::empty(),
- )?;
-
- let split = split_info.inline_start?;
- Some(TruncationResult {
- split: split,
- text_run: split_info.text_run.clone(),
- })
- }
-
- /// A helper method that uses the breaking strategy described by `slice_iterator` (at present,
- /// either natural word breaking or character breaking) to split this fragment.
- fn calculate_split_position_using_breaking_strategy<'a, I>(
- &self,
- slice_iterator: I,
- max_inline_size: Au,
- flags: SplitOptions,
- ) -> Option<SplitResult>
- where
- I: Iterator<Item = TextRunSlice<'a>>,
- {
- let text_fragment_info = match self.specific {
- SpecificFragmentInfo::ScannedText(ref text_fragment_info) => text_fragment_info,
- _ => return None,
- };
-
- let mut remaining_inline_size = max_inline_size - self.border_padding.inline_start_end();
- let mut inline_start_range = Range::new(text_fragment_info.range.begin(), ByteIndex(0));
- let mut inline_end_range = None;
- let mut overflowing = false;
-
- debug!(
- "calculate_split_position_using_breaking_strategy: splitting text fragment \
- (strlen={}, range={:?}, max_inline_size={:?})",
- text_fragment_info.run.text.len(),
- text_fragment_info.range,
- max_inline_size
- );
-
- for slice in slice_iterator {
- debug!(
- "calculate_split_position_using_breaking_strategy: considering slice \
- (offset={:?}, slice range={:?}, remaining_inline_size={:?})",
- slice.offset, slice.range, remaining_inline_size
- );
-
- // Use the `remaining_inline_size` to find a split point if possible. If not, go around
- // the loop again with the next slice.
- let metrics = text_fragment_info
- .run
- .metrics_for_slice(slice.glyphs, &slice.range);
- let advance = metrics.advance_width;
-
- // Have we found the split point?
- if advance <= remaining_inline_size || slice.glyphs.is_whitespace() {
- // Keep going; we haven't found the split point yet.
- debug!("calculate_split_position_using_breaking_strategy: enlarging span");
- remaining_inline_size = remaining_inline_size - advance;
- inline_start_range.extend_by(slice.range.length());
- continue;
- }
-
- // The advance is more than the remaining inline-size, so split here. First, check to
- // see if we're going to overflow the line. If so, perform a best-effort split.
- let mut remaining_range = slice.text_run_range();
- let split_is_empty = inline_start_range.is_empty() &&
- !(self.requires_line_break_afterward_if_wrapping_on_newlines() &&
- !self.white_space().allow_wrap());
- if split_is_empty {
- // We're going to overflow the line.
- overflowing = true;
- inline_start_range = slice.text_run_range();
- remaining_range = Range::new(slice.text_run_range().end(), ByteIndex(0));
- remaining_range.extend_to(text_fragment_info.range.end());
- }
-
- // Check to see if we need to create an inline-end chunk.
- let slice_begin = remaining_range.begin();
- if slice_begin < text_fragment_info.range.end() {
- // There still some things left over at the end of the line, so create the
- // inline-end chunk.
- let mut inline_end = remaining_range;
- inline_end.extend_to(text_fragment_info.range.end());
- inline_end_range = Some(inline_end);
- debug!(
- "calculate_split_position: splitting remainder with inline-end range={:?}",
- inline_end
- );
- }
-
- // If we failed to find a suitable split point, we're on the verge of overflowing the
- // line.
- if split_is_empty || overflowing {
- // If we've been instructed to retry at character boundaries (probably via
- // `overflow-wrap: break-word`), do so.
- if flags.contains(SplitOptions::RETRY_AT_CHARACTER_BOUNDARIES) {
- let character_breaking_strategy = text_fragment_info
- .run
- .character_slices_in_range(&text_fragment_info.range);
- let mut flags = flags;
- flags.remove(SplitOptions::RETRY_AT_CHARACTER_BOUNDARIES);
- return self.calculate_split_position_using_breaking_strategy(
- character_breaking_strategy,
- max_inline_size,
- flags,
- );
- }
-
- // We aren't at the start of the line, so don't overflow. Let inline layout wrap to
- // the next line instead.
- if !flags.contains(SplitOptions::STARTS_LINE) {
- return None;
- }
- }
-
- break;
- }
-
- let split_is_empty = inline_start_range.is_empty() &&
- !self.requires_line_break_afterward_if_wrapping_on_newlines();
- let inline_start = if !split_is_empty {
- Some(SplitInfo::new(inline_start_range, &**text_fragment_info))
- } else {
- None
- };
- let inline_end = inline_end_range
- .map(|inline_end_range| SplitInfo::new(inline_end_range, &**text_fragment_info));
-
- Some(SplitResult {
- inline_start: inline_start,
- inline_end: inline_end,
- text_run: text_fragment_info.run.clone(),
- })
- }
-
- /// The opposite of `calculate_split_position_using_breaking_strategy`: merges this fragment
- /// with the next one.
- pub fn merge_with(&mut self, next_fragment: Fragment) {
- match (&mut self.specific, &next_fragment.specific) {
- (
- &mut SpecificFragmentInfo::ScannedText(ref mut this_info),
- &SpecificFragmentInfo::ScannedText(ref other_info),
- ) => {
- debug_assert!(Arc::ptr_eq(&this_info.run, &other_info.run));
- this_info.range_end_including_stripped_whitespace =
- other_info.range_end_including_stripped_whitespace;
- if other_info.requires_line_break_afterward_if_wrapping_on_newlines() {
- this_info.flags.insert(
- ScannedTextFlags::REQUIRES_LINE_BREAK_AFTERWARD_IF_WRAPPING_ON_NEWLINES,
- );
- }
- if other_info.insertion_point.is_some() {
- this_info.insertion_point = other_info.insertion_point;
- }
- self.border_padding.inline_end = next_fragment.border_padding.inline_end;
- self.margin.inline_end = next_fragment.margin.inline_end;
- },
- _ => panic!("Can only merge two scanned-text fragments!"),
- }
- self.reset_text_range_and_inline_size();
- self.meld_with_next_inline_fragment(&next_fragment);
- }
-
- /// Restore any whitespace that was stripped from a text fragment, and recompute inline metrics
- /// if necessary.
- pub fn reset_text_range_and_inline_size(&mut self) {
- if let SpecificFragmentInfo::ScannedText(ref mut info) = self.specific {
- if info.run.extra_word_spacing != Au(0) {
- Arc::make_mut(&mut info.run).extra_word_spacing = Au(0);
- }
-
- // FIXME (mbrubeck): Do we need to restore leading too?
- let range_end = info.range_end_including_stripped_whitespace;
- if info.range.end() == range_end {
- return;
- }
- info.range.extend_to(range_end);
- info.content_size.inline = info.run.metrics_for_range(&info.range).advance_width;
- self.border_box.size.inline =
- info.content_size.inline + self.border_padding.inline_start_end();
- }
- }
-
- /// Assigns replaced inline-size, padding, and margins for this fragment only if it is replaced
- /// content per CSS 2.1 § 10.3.2.
- pub fn assign_replaced_inline_size_if_necessary(
- &mut self,
- container_inline_size: Au,
- container_block_size: Option<Au>,
- ) {
- match self.specific {
- SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_none() => return,
- SpecificFragmentInfo::Generic |
- SpecificFragmentInfo::GeneratedContent(_) |
- SpecificFragmentInfo::Table |
- SpecificFragmentInfo::TableCell |
- SpecificFragmentInfo::TableRow |
- SpecificFragmentInfo::TableWrapper |
- SpecificFragmentInfo::Multicol |
- SpecificFragmentInfo::MulticolColumn => return,
- SpecificFragmentInfo::TableColumn(_) => {
- panic!("Table column fragments do not have inline size")
- },
- SpecificFragmentInfo::UnscannedText(_) => {
- panic!("Unscanned text fragments should have been scanned by now!")
- },
- SpecificFragmentInfo::Canvas(_) |
- SpecificFragmentInfo::Image(_) |
- SpecificFragmentInfo::Media(_) |
- SpecificFragmentInfo::Iframe(_) |
- SpecificFragmentInfo::InlineBlock(_) |
- SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
- SpecificFragmentInfo::InlineAbsolute(_) |
- SpecificFragmentInfo::ScannedText(_) |
- SpecificFragmentInfo::TruncatedFragment(_) |
- SpecificFragmentInfo::Svg(_) => {},
- };
-
- 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 =
- block_flow.base.intrinsic_inline_sizes.preferred_inline_size;
-
- // This is a hypothetical box, so it takes up no space.
- self.border_box.size.inline = Au(0);
- },
- SpecificFragmentInfo::InlineBlock(ref mut info) => {
- let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_mut_block();
- self.border_box.size.inline = max(
- block_flow.base.intrinsic_inline_sizes.minimum_inline_size,
- block_flow.base.intrinsic_inline_sizes.preferred_inline_size,
- );
- block_flow.base.block_container_inline_size = self.border_box.size.inline;
- block_flow.base.block_container_writing_mode = self.style.writing_mode;
- },
- SpecificFragmentInfo::InlineAbsolute(ref mut info) => {
- let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_mut_block();
- self.border_box.size.inline = max(
- block_flow.base.intrinsic_inline_sizes.minimum_inline_size,
- block_flow.base.intrinsic_inline_sizes.preferred_inline_size,
- );
- 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::TruncatedFragment(ref t) if t.text_info.is_some() => {
- let info = t.text_info.as_ref().unwrap();
- // Scanned text fragments will have already had their content inline-sizes assigned
- // by this point.
- self.border_box.size.inline =
- info.content_size.inline + self.border_padding.inline_start_end();
- },
- 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 + self.border_padding.inline_start_end();
- },
-
- // 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();
- },
-
- ref unhandled @ _ => {
- panic!("this case should have been handled above: {:?}", unhandled)
- },
- }
- }
-
- /// Assign block-size for this fragment if it is replaced content. The inline-size must have
- /// been assigned first.
- ///
- /// Ideally, this should follow CSS 2.1 § 10.6.2.
- pub fn assign_replaced_block_size_if_necessary(&mut self) {
- match self.specific {
- SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_none() => return,
- SpecificFragmentInfo::Generic |
- SpecificFragmentInfo::GeneratedContent(_) |
- SpecificFragmentInfo::Table |
- SpecificFragmentInfo::TableCell |
- SpecificFragmentInfo::TableRow |
- SpecificFragmentInfo::TableWrapper |
- SpecificFragmentInfo::Multicol |
- SpecificFragmentInfo::MulticolColumn => return,
- SpecificFragmentInfo::TableColumn(_) => {
- panic!("Table column fragments do not have block size")
- },
- SpecificFragmentInfo::UnscannedText(_) => {
- panic!("Unscanned text fragments should have been scanned by now!")
- },
- SpecificFragmentInfo::Canvas(_) |
- SpecificFragmentInfo::Iframe(_) |
- SpecificFragmentInfo::Image(_) |
- SpecificFragmentInfo::Media(_) |
- SpecificFragmentInfo::InlineBlock(_) |
- SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
- SpecificFragmentInfo::InlineAbsolute(_) |
- SpecificFragmentInfo::ScannedText(_) |
- SpecificFragmentInfo::TruncatedFragment(_) |
- SpecificFragmentInfo::Svg(_) => {},
- }
-
- match self.specific {
- // Text
- SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_some() => {
- let info = t.text_info.as_ref().unwrap();
- // 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 + self.border_padding.block_start_end();
- },
- 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 + 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();
- self.border_box.size.block = block_flow.base.position.size.block +
- block_flow.fragment.margin.block_start_end()
- },
- SpecificFragmentInfo::InlineAbsoluteHypothetical(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();
- self.border_box.size.block = block_flow.base.position.size.block;
- },
- SpecificFragmentInfo::InlineAbsolute(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();
- self.border_box.size.block = block_flow.base.position.size.block +
- block_flow.fragment.margin.block_start_end()
- },
-
- // Replaced elements
- _ if self.is_replaced() => {},
-
- ref unhandled @ _ => panic!("should have been handled above: {:?}", unhandled),
- }
- }
-
- /// Returns true if this fragment is replaced content.
- pub fn is_replaced(&self) -> bool {
- match self.specific {
- SpecificFragmentInfo::Iframe(_) |
- SpecificFragmentInfo::Canvas(_) |
- SpecificFragmentInfo::Image(_) |
- SpecificFragmentInfo::Media(_) |
- SpecificFragmentInfo::Svg(_) => true,
- _ => 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(),
- }
- }
-
- /// Calculates block-size above baseline, depth below baseline, and ascent for this fragment
- /// when used in an inline formatting context. See CSS 2.1 § 10.8.1.
- ///
- /// This does not take `vertical-align` into account. For that, use `aligned_inline_metrics()`.
- fn content_inline_metrics(&self, layout_context: &LayoutContext) -> InlineMetrics {
- // CSS 2.1 § 10.8: "The height of each inline-level box in the line box is
- // calculated. For replaced elements, inline-block elements, and inline-table
- // elements, this is the height of their margin box."
- //
- // FIXME(pcwalton): We have to handle `Generic` and `GeneratedContent` here to avoid
- // crashing in a couple of `css21_dev/html4/content-` WPTs, but I don't see how those two
- // fragment types should end up inside inlines. (In the case of `GeneratedContent`, those
- // fragment types should have been resolved by now…)
- let inline_metrics = match self.specific {
- SpecificFragmentInfo::Canvas(_) |
- SpecificFragmentInfo::Iframe(_) |
- SpecificFragmentInfo::Image(_) |
- SpecificFragmentInfo::Media(_) |
- SpecificFragmentInfo::Svg(_) |
- SpecificFragmentInfo::Generic |
- SpecificFragmentInfo::GeneratedContent(_) => {
- let ascent = self.border_box.size.block + self.margin.block_end;
- InlineMetrics {
- space_above_baseline: ascent + self.margin.block_start,
- space_below_baseline: Au(0),
- ascent: ascent,
- }
- },
- SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_some() => {
- let info = t.text_info.as_ref().unwrap();
- inline_metrics_of_text(info, self, layout_context)
- },
- SpecificFragmentInfo::ScannedText(ref info) => {
- inline_metrics_of_text(info, self, layout_context)
- },
- SpecificFragmentInfo::InlineBlock(ref info) => {
- inline_metrics_of_block(&info.flow_ref, &*self.style)
- },
- SpecificFragmentInfo::InlineAbsoluteHypothetical(ref info) => {
- inline_metrics_of_block(&info.flow_ref, &*self.style)
- },
- SpecificFragmentInfo::TruncatedFragment(..) |
- SpecificFragmentInfo::InlineAbsolute(_) => InlineMetrics::new(Au(0), Au(0), Au(0)),
- SpecificFragmentInfo::Table |
- SpecificFragmentInfo::TableCell |
- SpecificFragmentInfo::TableColumn(_) |
- SpecificFragmentInfo::TableRow |
- SpecificFragmentInfo::TableWrapper |
- SpecificFragmentInfo::Multicol |
- SpecificFragmentInfo::MulticolColumn |
- SpecificFragmentInfo::UnscannedText(_) => {
- unreachable!("Shouldn't see fragments of this type here!")
- },
- };
- return inline_metrics;
-
- fn inline_metrics_of_text(
- info: &ScannedTextFragmentInfo,
- self_: &Fragment,
- layout_context: &LayoutContext,
- ) -> InlineMetrics {
- // Fragments with no glyphs don't contribute any inline metrics.
- // TODO: Filter out these fragments during flow construction?
- if info.insertion_point.is_none() && info.content_size.inline == Au(0) {
- return InlineMetrics::new(Au(0), Au(0), Au(0));
- }
- // See CSS 2.1 § 10.8.1.
- let font_metrics = with_thread_local_font_context(layout_context, |font_context| {
- text::font_metrics_for_style(font_context, self_.style.clone_font())
- });
- let line_height = text::line_height_from_style(&*self_.style, &font_metrics);
- InlineMetrics::from_font_metrics(&info.run.font_metrics, line_height)
- }
-
- fn inline_metrics_of_block(flow: &FlowRef, style: &ComputedValues) -> InlineMetrics {
- // CSS 2.1 § 10.8: "The height of each inline-level box in the line box is calculated.
- // For replaced elements, inline-block elements, and inline-table elements, this is the
- // height of their margin box."
- //
- // CSS 2.1 § 10.8.1: "The baseline of an 'inline-block' is the baseline of its last
- // line box in the normal flow, unless it has either no in-flow line boxes or if its
- // 'overflow' property has a computed value other than 'visible', in which case the
- // baseline is the bottom margin edge."
- //
- // NB: We must use `block_flow.fragment.border_box.size.block` here instead of
- // `block_flow.base.position.size.block` because sometimes the latter is late-computed
- // and isn't up to date at this point.
- let block_flow = flow.as_block();
- let start_margin = block_flow.fragment.margin.block_start;
- let end_margin = block_flow.fragment.margin.block_end;
- let border_box_block_size = block_flow.fragment.border_box.size.block;
-
- // --------
- // margin
- // top -------- + +
- // | |
- // | |
- // A ..pogo.. | + baseline_offset_of_last_line_box_in_flow()
- // |
- // -------- + border_box_block_size
- // margin
- // B --------
- //
- // § 10.8.1 says that the baseline (and thus ascent, which is the
- // distance from the baseline to the top) should be A if it has an
- // in-flow line box and if overflow: visible, and B otherwise.
- let ascent = match (
- flow.baseline_offset_of_last_line_box_in_flow(),
- style.get_box().overflow_y,
- ) {
- // Case A
- (Some(baseline_offset), StyleOverflow::Visible) => baseline_offset,
- // Case B
- _ => border_box_block_size + end_margin,
- };
-
- let space_below_baseline = border_box_block_size + end_margin - ascent;
- let space_above_baseline = ascent + start_margin;
-
- InlineMetrics::new(space_above_baseline, space_below_baseline, ascent)
- }
- }
-
- /// Calculates the offset from the baseline that applies to this fragment due to
- /// `vertical-align`. Positive values represent downward displacement.
- ///
- /// If `actual_line_metrics` is supplied, then these metrics are used to determine the
- /// displacement of the fragment when `top` or `bottom` `vertical-align` values are
- /// encountered. If this is not supplied, then `top` and `bottom` values are ignored.
- fn vertical_alignment_offset(
- &self,
- layout_context: &LayoutContext,
- content_inline_metrics: &InlineMetrics,
- minimum_line_metrics: &LineMetrics,
- actual_line_metrics: Option<&LineMetrics>,
- ) -> Au {
- let mut offset = Au(0);
- for style in self.inline_styles() {
- // If any of the inline styles say `top` or `bottom`, adjust the vertical align
- // appropriately.
- //
- // FIXME(#5624, pcwalton): This passes our current reftests but isn't the right thing
- // to do.
- match style.get_box().vertical_align {
- VerticalAlign::Keyword(kw) => match kw {
- VerticalAlignKeyword::Baseline => {},
- VerticalAlignKeyword::Middle => {
- let font_metrics =
- with_thread_local_font_context(layout_context, |font_context| {
- text::font_metrics_for_style(font_context, self.style.clone_font())
- });
- offset += (content_inline_metrics.ascent -
- content_inline_metrics.space_below_baseline -
- font_metrics.x_height)
- .scale_by(0.5)
- },
- VerticalAlignKeyword::Sub => {
- offset += minimum_line_metrics
- .space_needed()
- .scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
- },
- VerticalAlignKeyword::Super => {
- offset -= minimum_line_metrics
- .space_needed()
- .scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
- },
- VerticalAlignKeyword::TextTop => {
- offset = self.content_inline_metrics(layout_context).ascent -
- minimum_line_metrics.space_above_baseline
- },
- VerticalAlignKeyword::TextBottom => {
- offset = minimum_line_metrics.space_below_baseline -
- self.content_inline_metrics(layout_context)
- .space_below_baseline
- },
- VerticalAlignKeyword::Top => {
- if let Some(actual_line_metrics) = actual_line_metrics {
- offset = content_inline_metrics.ascent -
- actual_line_metrics.space_above_baseline
- }
- },
- VerticalAlignKeyword::Bottom => {
- if let Some(actual_line_metrics) = actual_line_metrics {
- offset = actual_line_metrics.space_below_baseline -
- content_inline_metrics.space_below_baseline
- }
- },
- },
- VerticalAlign::Length(ref lp) => {
- offset -= lp.to_used_value(minimum_line_metrics.space_needed());
- },
- }
- }
- offset
- }
-
- /// Calculates block-size above baseline, depth below baseline, and ascent for this fragment
- /// when used in an inline formatting context, taking `vertical-align` (other than `top` or
- /// `bottom`) into account. See CSS 2.1 § 10.8.1.
- ///
- /// If `actual_line_metrics` is supplied, then these metrics are used to determine the
- /// displacement of the fragment when `top` or `bottom` `vertical-align` values are
- /// encountered. If this is not supplied, then `top` and `bottom` values are ignored.
- pub fn aligned_inline_metrics(
- &self,
- layout_context: &LayoutContext,
- minimum_line_metrics: &LineMetrics,
- actual_line_metrics: Option<&LineMetrics>,
- ) -> InlineMetrics {
- let content_inline_metrics = self.content_inline_metrics(layout_context);
- let vertical_alignment_offset = self.vertical_alignment_offset(
- layout_context,
- &content_inline_metrics,
- minimum_line_metrics,
- actual_line_metrics,
- );
- let mut space_above_baseline = match actual_line_metrics {
- None => content_inline_metrics.space_above_baseline,
- Some(actual_line_metrics) => actual_line_metrics.space_above_baseline,
- };
- space_above_baseline = space_above_baseline - vertical_alignment_offset;
- let space_below_baseline =
- content_inline_metrics.space_below_baseline + vertical_alignment_offset;
- let ascent = content_inline_metrics.ascent - vertical_alignment_offset;
- InlineMetrics::new(space_above_baseline, space_below_baseline, ascent)
- }
-
- /// Returns true if this fragment is a hypothetical box. See CSS 2.1 § 10.3.7.
- pub fn is_hypothetical(&self) -> bool {
- match self.specific {
- SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => true,
- _ => false,
- }
- }
-
- /// Returns true if this fragment can merge with another immediately-following fragment or
- /// false otherwise.
- pub fn can_merge_with_fragment(&self, other: &Fragment) -> bool {
- match (&self.specific, &other.specific) {
- (
- &SpecificFragmentInfo::UnscannedText(ref first_unscanned_text),
- &SpecificFragmentInfo::UnscannedText(_),
- ) => {
- // FIXME: Should probably use a whitelist of styles that can safely differ (#3165)
- if self.style().get_font() != other.style().get_font() ||
- self.text_decoration_line() != other.text_decoration_line() ||
- self.white_space() != other.white_space() ||
- self.color() != other.color()
- {
- return false;
- }
-
- if first_unscanned_text.text.ends_with('\n') {
- return false;
- }
-
- // If this node has any styles that have border/padding/margins on the following
- // side, then we can't merge with the next fragment.
- if let Some(ref inline_context) = self.inline_context {
- for inline_context_node in inline_context.nodes.iter() {
- if !inline_context_node
- .flags
- .contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT)
- {
- continue;
- }
- if inline_context_node.style.logical_margin().inline_end !=
- LengthPercentageOrAuto::zero()
- {
- return false;
- }
- if inline_context_node.style.logical_padding().inline_end !=
- LengthPercentage::zero()
- {
- return false;
- }
- if inline_context_node.style.logical_border_width().inline_end != Au(0) {
- return false;
- }
- }
- }
-
- // If the next fragment has any styles that have border/padding/margins on the
- // preceding side, then it can't merge with us.
- if let Some(ref inline_context) = other.inline_context {
- for inline_context_node in inline_context.nodes.iter() {
- if !inline_context_node
- .flags
- .contains(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT)
- {
- continue;
- }
- if inline_context_node.style.logical_margin().inline_start !=
- LengthPercentageOrAuto::zero()
- {
- return false;
- }
- if inline_context_node.style.logical_padding().inline_start !=
- LengthPercentage::zero()
- {
- return false;
- }
- if inline_context_node
- .style
- .logical_border_width()
- .inline_start !=
- Au(0)
- {
- return false;
- }
- }
- }
-
- true
- },
- _ => false,
- }
- }
-
- /// Returns true if and only if this is the *primary fragment* for the fragment's style object
- /// (conceptually, though style sharing makes this not really true, of course). The primary
- /// fragment is the one that draws backgrounds, borders, etc., and takes borders, padding and
- /// margins into account. Every style object has at most one primary fragment.
- ///
- /// At present, all fragments are primary fragments except for inline-block and table wrapper
- /// fragments. Inline-block fragments are not primary fragments because the corresponding block
- /// flow is the primary fragment, while table wrapper fragments are not primary fragments
- /// because the corresponding table flow is the primary fragment.
pub fn is_primary_fragment(&self) -> bool {
- match self.specific {
- SpecificFragmentInfo::InlineBlock(_) |
- SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
- SpecificFragmentInfo::InlineAbsolute(_) |
- SpecificFragmentInfo::MulticolColumn |
- SpecificFragmentInfo::TableWrapper => false,
- SpecificFragmentInfo::Canvas(_) |
- SpecificFragmentInfo::Generic |
- SpecificFragmentInfo::GeneratedContent(_) |
- SpecificFragmentInfo::Iframe(_) |
- SpecificFragmentInfo::Image(_) |
- SpecificFragmentInfo::Media(_) |
- SpecificFragmentInfo::ScannedText(_) |
- SpecificFragmentInfo::Svg(_) |
- SpecificFragmentInfo::Table |
- SpecificFragmentInfo::TableCell |
- SpecificFragmentInfo::TableColumn(_) |
- SpecificFragmentInfo::TableRow |
- SpecificFragmentInfo::TruncatedFragment(_) |
- SpecificFragmentInfo::Multicol |
- SpecificFragmentInfo::UnscannedText(_) => true,
- }
- }
-
- /// Determines the inline sizes of inline-block fragments. These cannot be fully computed until
- /// inline size assignment has run for the child flow: thus it is computed "late", during
- /// block size assignment.
- pub fn update_late_computed_replaced_inline_size_if_necessary(&mut self) {
- if let SpecificFragmentInfo::InlineBlock(ref mut inline_block_info) = self.specific {
- let block_flow = FlowRef::deref_mut(&mut inline_block_info.flow_ref).as_block();
- self.border_box.size.inline = block_flow.fragment.margin_box_inline_size();
- }
- }
-
- pub fn update_late_computed_inline_position_if_necessary(&mut self) {
- if let SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) = self.specific {
- let position = self.border_box.start.i;
- FlowRef::deref_mut(&mut info.flow_ref)
- .update_late_computed_inline_position_if_necessary(position)
- }
- }
-
- pub fn update_late_computed_block_position_if_necessary(&mut self) {
- if let SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) = self.specific {
- let position = self.border_box.start.b;
- FlowRef::deref_mut(&mut info.flow_ref)
- .update_late_computed_block_position_if_necessary(position)
- }
- }
-
- pub fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) {
- self.style = (*new_style).clone()
- }
-
- /// Given the stacking-context-relative position of the containing flow, returns the border box
- /// of this fragment relative to the parent stacking context. This takes `position: relative`
- /// into account.
- ///
- /// If `coordinate_system` is `Parent`, this returns the border box in the parent stacking
- /// context's coordinate system. Otherwise, if `coordinate_system` is `Own` and this fragment
- /// establishes a stacking context itself, this returns a border box anchored at (0, 0). (If
- /// this fragment does not establish a stacking context, then it always belongs to its parent
- /// stacking context and thus `coordinate_system` is ignored.)
- ///
- /// This is the method you should use for display list construction as well as
- /// `getBoundingClientRect()` and so forth.
- pub fn stacking_relative_border_box(
- &self,
- stacking_relative_flow_origin: &Vector2D<Au>,
- relative_containing_block_size: &LogicalSize<Au>,
- relative_containing_block_mode: WritingMode,
- coordinate_system: CoordinateSystem,
- ) -> Rect<Au> {
- let container_size =
- relative_containing_block_size.to_physical(relative_containing_block_mode);
- let border_box = self
- .border_box
- .to_physical(self.style.writing_mode, container_size);
- if coordinate_system == CoordinateSystem::Own && self.establishes_stacking_context() {
- return Rect::new(Point2D::zero(), border_box.size);
- }
-
- // FIXME(pcwalton): This can double-count relative position sometimes for inlines (e.g.
- // `<div style="position:relative">x</div>`, because the `position:relative` trickles down
- // to the inline flow. Possibly we should extend the notion of "primary fragment" to fix
- // this.
- let relative_position = self.relative_position(relative_containing_block_size);
- border_box
- .translate_by_size(&relative_position.to_physical(self.style.writing_mode))
- .translate(&stacking_relative_flow_origin)
- }
-
- /// Given the stacking-context-relative border box, returns the stacking-context-relative
- /// content box.
- pub fn stacking_relative_content_box(
- &self,
- stacking_relative_border_box: Rect<Au>,
- ) -> Rect<Au> {
- let border_padding = self.border_padding.to_physical(self.style.writing_mode);
- Rect::new(
- Point2D::new(
- stacking_relative_border_box.origin.x + border_padding.left,
- stacking_relative_border_box.origin.y + border_padding.top,
- ),
- Size2D::new(
- stacking_relative_border_box.size.width - border_padding.horizontal(),
- stacking_relative_border_box.size.height - border_padding.vertical(),
- ),
- )
- }
-
- /// Returns true if this fragment may establish a reference frame.
- pub fn can_establish_reference_frame(&self) -> bool {
- !self.style().get_box().transform.0.is_empty() ||
- self.style().get_box().perspective != Perspective::None
- }
-
- /// Returns true if this fragment has a filter, transform, or perspective property set.
- pub fn has_filter_transform_or_perspective(&self) -> bool {
- !self.style().get_box().transform.0.is_empty() ||
- !self.style().get_effects().filter.0.is_empty() ||
- self.style().get_box().perspective != Perspective::None
- }
-
- /// Returns true if this fragment establishes a new stacking context and false otherwise.
- pub fn establishes_stacking_context(&self) -> bool {
- // Text fragments shouldn't create stacking contexts.
- match self.specific {
- SpecificFragmentInfo::TruncatedFragment(_) |
- SpecificFragmentInfo::ScannedText(_) |
- SpecificFragmentInfo::UnscannedText(_) => return false,
- _ => {},
- }
-
- if self.style().get_effects().opacity != 1.0 {
- return true;
- }
-
- if self.style().get_effects().mix_blend_mode != MixBlendMode::Normal {
- return true;
- }
-
- if self.has_filter_transform_or_perspective() {
- return true;
- }
-
- if self.style().get_box().transform_style == TransformStyle::Preserve3d ||
- self.style().overrides_transform_style()
- {
- return true;
- }
-
- // Fixed position and sticky position always create stacking contexts.
- if self.style().get_box().position == Position::Fixed ||
- self.style().get_box().position == Position::Sticky
- {
- return true;
- }
-
- // Statically positioned fragments don't establish stacking contexts if the previous
- // conditions are not fulfilled. Furthermore, z-index doesn't apply to statically
- // positioned fragments.
- if self.style().get_box().position == Position::Static {
- return false;
- }
-
- // For absolutely and relatively positioned fragments we only establish a stacking
- // context if there is a z-index set.
- // See https://www.w3.org/TR/CSS2/visuren.html#z-index
- !self.style().get_position().z_index.is_auto()
- }
-
- // Get the effective z-index of this fragment. Z-indices only apply to positioned element
- // per CSS 2 9.9.1 (http://www.w3.org/TR/CSS2/visuren.html#z-index), so this value may differ
- // from the value specified in the style.
- pub fn effective_z_index(&self) -> i32 {
- match self.style().get_box().position {
- Position::Static => {},
- _ => return self.style().get_position().z_index.integer_or(0),
- }
-
- if !self.style().get_box().transform.0.is_empty() {
- return self.style().get_position().z_index.integer_or(0);
- }
-
- match self.style().get_box().display {
- Display::Flex => self.style().get_position().z_index.integer_or(0),
- _ => 0,
- }
- }
-
- /// Computes the overflow rect of this fragment relative to the start of the flow.
- pub fn compute_overflow(
- &self,
- flow_size: &Size2D<Au>,
- relative_containing_block_size: &LogicalSize<Au>,
- ) -> Overflow {
- let mut border_box = self
- .border_box
- .to_physical(self.style.writing_mode, *flow_size);
-
- // Relative position can cause us to draw outside our border box.
- //
- // FIXME(pcwalton): I'm not a fan of the way this makes us crawl though so many styles all
- // the time. Can't we handle relative positioning by just adjusting `border_box`?
- let relative_position = self.relative_position(relative_containing_block_size);
- border_box =
- border_box.translate_by_size(&relative_position.to_physical(self.style.writing_mode));
- let mut overflow = Overflow::from_rect(&border_box);
-
- // Box shadows cause us to draw outside our border box.
- for box_shadow in &*self.style().get_effects().box_shadow.0 {
- let offset = Vector2D::new(
- Au::from(box_shadow.base.horizontal),
- Au::from(box_shadow.base.vertical),
- );
- let inflation = Au::from(box_shadow.spread) +
- Au::from(box_shadow.base.blur) * BLUR_INFLATION_FACTOR;
- overflow.paint = overflow
- .paint
- .union(&border_box.translate(&offset).inflate(inflation, inflation))
- }
-
- // Outlines cause us to draw outside our border box.
- let outline_width = Au::from(self.style.get_outline().outline_width);
- if outline_width != Au(0) {
- overflow.paint = overflow
- .paint
- .union(&border_box.inflate(outline_width, outline_width))
- }
-
- // Include the overflow of the block flow, if any.
- match self.specific {
- SpecificFragmentInfo::InlineBlock(ref info) => {
- let block_flow = info.flow_ref.as_block();
- overflow.union(&block_flow.base().overflow);
- },
- SpecificFragmentInfo::InlineAbsolute(ref info) => {
- let block_flow = info.flow_ref.as_block();
- overflow.union(&block_flow.base().overflow);
- },
- _ => (),
- }
-
- // FIXME(pcwalton): Sometimes excessively fancy glyphs can make us draw outside our border
- // box too.
- overflow
- }
-
- pub fn requires_line_break_afterward_if_wrapping_on_newlines(&self) -> bool {
- match self.specific {
- SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_some() => {
- let text = t.text_info.as_ref().unwrap();
- text.requires_line_break_afterward_if_wrapping_on_newlines()
- },
- SpecificFragmentInfo::ScannedText(ref text) => {
- text.requires_line_break_afterward_if_wrapping_on_newlines()
- },
- _ => false,
- }
- }
-
- pub fn strip_leading_whitespace_if_necessary(&mut self) -> WhitespaceStrippingResult {
- if self.white_space().preserve_spaces() {
- return WhitespaceStrippingResult::RetainFragment;
- }
-
- return match self.specific {
- SpecificFragmentInfo::TruncatedFragment(ref mut t) if t.text_info.is_some() => {
- let scanned_text_fragment_info = t.text_info.as_mut().unwrap();
- scanned_text(scanned_text_fragment_info, &mut self.border_box)
- },
- SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) => {
- scanned_text(scanned_text_fragment_info, &mut self.border_box)
- },
- SpecificFragmentInfo::UnscannedText(ref mut unscanned_text_fragment_info) => {
- let mut new_text_string = String::new();
- let mut modified = false;
- for (i, character) in unscanned_text_fragment_info.text.char_indices() {
- if gfx::text::util::is_bidi_control(character) {
- new_text_string.push(character);
- continue;
- }
- if char_is_whitespace(character) {
- modified = true;
- continue;
- }
- // Finished processing leading control chars and whitespace.
- if modified {
- new_text_string.push_str(&unscanned_text_fragment_info.text[i..]);
- }
- break;
- }
- if modified {
- unscanned_text_fragment_info.text = new_text_string.into_boxed_str();
- }
-
- WhitespaceStrippingResult::from_unscanned_text_fragment_info(
- &unscanned_text_fragment_info,
- )
- },
- _ => WhitespaceStrippingResult::RetainFragment,
- };
-
- fn scanned_text(
- scanned_text_fragment_info: &mut ScannedTextFragmentInfo,
- border_box: &mut LogicalRect<Au>,
- ) -> WhitespaceStrippingResult {
- let leading_whitespace_byte_count = scanned_text_fragment_info
- .text()
- .find(|c| !char_is_whitespace(c))
- .unwrap_or(scanned_text_fragment_info.text().len());
-
- let whitespace_len = ByteIndex(leading_whitespace_byte_count as isize);
- let whitespace_range =
- Range::new(scanned_text_fragment_info.range.begin(), whitespace_len);
- let text_bounds = scanned_text_fragment_info
- .run
- .metrics_for_range(&whitespace_range)
- .bounding_box;
- border_box.size.inline = border_box.size.inline - text_bounds.size.width;
- scanned_text_fragment_info.content_size.inline =
- scanned_text_fragment_info.content_size.inline - text_bounds.size.width;
-
- scanned_text_fragment_info
- .range
- .adjust_by(whitespace_len, -whitespace_len);
-
- WhitespaceStrippingResult::RetainFragment
- }
- }
-
- /// Returns true if the entire fragment was stripped.
- pub fn strip_trailing_whitespace_if_necessary(&mut self) -> WhitespaceStrippingResult {
- if self.white_space().preserve_spaces() {
- return WhitespaceStrippingResult::RetainFragment;
- }
-
- return match self.specific {
- SpecificFragmentInfo::TruncatedFragment(ref mut t) if t.text_info.is_some() => {
- let scanned_text_fragment_info = t.text_info.as_mut().unwrap();
- scanned_text(scanned_text_fragment_info, &mut self.border_box)
- },
- SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) => {
- scanned_text(scanned_text_fragment_info, &mut self.border_box)
- },
- SpecificFragmentInfo::UnscannedText(ref mut unscanned_text_fragment_info) => {
- let mut trailing_bidi_control_characters_to_retain = Vec::new();
- let (mut modified, mut last_character_index) = (true, 0);
- for (i, character) in unscanned_text_fragment_info.text.char_indices().rev() {
- if gfx::text::util::is_bidi_control(character) {
- trailing_bidi_control_characters_to_retain.push(character);
- continue;
- }
- if char_is_whitespace(character) {
- modified = true;
- continue;
- }
- last_character_index = i + character.len_utf8();
- break;
- }
- if modified {
- let mut text = unscanned_text_fragment_info.text.to_string();
- text.truncate(last_character_index);
- for character in trailing_bidi_control_characters_to_retain.iter().rev() {
- text.push(*character);
- }
- unscanned_text_fragment_info.text = text.into_boxed_str();
- }
-
- WhitespaceStrippingResult::from_unscanned_text_fragment_info(
- &unscanned_text_fragment_info,
- )
- },
- _ => WhitespaceStrippingResult::RetainFragment,
- };
-
- fn scanned_text(
- scanned_text_fragment_info: &mut ScannedTextFragmentInfo,
- border_box: &mut LogicalRect<Au>,
- ) -> WhitespaceStrippingResult {
- let mut trailing_whitespace_start_byte = 0;
- for (i, c) in scanned_text_fragment_info.text().char_indices().rev() {
- if !char_is_whitespace(c) {
- trailing_whitespace_start_byte = i + c.len_utf8();
- break;
- }
- }
- let whitespace_start = ByteIndex(trailing_whitespace_start_byte as isize);
- let whitespace_len = scanned_text_fragment_info.range.length() - whitespace_start;
- let mut whitespace_range = Range::new(whitespace_start, whitespace_len);
- whitespace_range.shift_by(scanned_text_fragment_info.range.begin());
-
- let text_bounds = scanned_text_fragment_info
- .run
- .metrics_for_range(&whitespace_range)
- .bounding_box;
- border_box.size.inline -= text_bounds.size.width;
- scanned_text_fragment_info.content_size.inline -= text_bounds.size.width;
-
- scanned_text_fragment_info.range.extend_by(-whitespace_len);
- WhitespaceStrippingResult::RetainFragment
- }
- }
-
- pub fn inline_styles(&self) -> InlineStyleIterator {
- InlineStyleIterator::new(self)
- }
-
- /// Returns the inline-size of this fragment's margin box.
- pub fn margin_box_inline_size(&self) -> Au {
- self.border_box.size.inline + self.margin.inline_start_end()
- }
-
- /// Returns true if this node *or any of the nodes within its inline fragment context* have
- /// non-`static` `position`.
- pub fn is_positioned(&self) -> bool {
- if self.style.get_box().position != Position::Static {
- return true;
- }
- if let Some(ref inline_context) = self.inline_context {
- for node in inline_context.nodes.iter() {
- if node.style.get_box().position != Position::Static {
- return true;
- }
- }
- }
- false
- }
-
- /// Returns true if this node is absolutely positioned.
- pub fn is_absolutely_positioned(&self) -> bool {
- self.style.get_box().position == Position::Absolute
- }
-
- pub fn is_inline_absolute(&self) -> bool {
- match self.specific {
- SpecificFragmentInfo::InlineAbsolute(..) => true,
- _ => false,
- }
- }
-
- pub fn meld_with_next_inline_fragment(&mut self, next_fragment: &Fragment) {
- if let Some(ref mut inline_context_of_this_fragment) = self.inline_context {
- if let Some(ref inline_context_of_next_fragment) = next_fragment.inline_context {
- for (
- inline_context_node_from_this_fragment,
- inline_context_node_from_next_fragment,
- ) in inline_context_of_this_fragment
- .nodes
- .iter_mut()
- .rev()
- .zip(inline_context_of_next_fragment.nodes.iter().rev())
- {
- if !inline_context_node_from_next_fragment
- .flags
- .contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT)
- {
- continue;
- }
- if inline_context_node_from_next_fragment.address !=
- inline_context_node_from_this_fragment.address
- {
- continue;
- }
- inline_context_node_from_this_fragment
- .flags
- .insert(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT);
- }
- }
- }
- }
-
- pub fn meld_with_prev_inline_fragment(&mut self, prev_fragment: &Fragment) {
- if let Some(ref mut inline_context_of_this_fragment) = self.inline_context {
- if let Some(ref inline_context_of_prev_fragment) = prev_fragment.inline_context {
- for (
- inline_context_node_from_prev_fragment,
- inline_context_node_from_this_fragment,
- ) in inline_context_of_prev_fragment
- .nodes
- .iter()
- .rev()
- .zip(inline_context_of_this_fragment.nodes.iter_mut().rev())
- {
- if !inline_context_node_from_prev_fragment
- .flags
- .contains(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT)
- {
- continue;
- }
- if inline_context_node_from_prev_fragment.address !=
- inline_context_node_from_this_fragment.address
- {
- continue;
- }
- inline_context_node_from_this_fragment
- .flags
- .insert(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT);
- }
- }
- }
- }
-
- /// Returns true if any of the inline styles associated with this fragment have
- /// `vertical-align` set to `top` or `bottom`.
- pub fn is_vertically_aligned_to_top_or_bottom(&self) -> bool {
- fn is_top_or_bottom(v: &VerticalAlign) -> bool {
- match *v {
- VerticalAlign::Keyword(VerticalAlignKeyword::Top) |
- VerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => true,
- _ => false,
- }
- }
-
- if is_top_or_bottom(&self.style.get_box().vertical_align) {
- return true;
- }
-
- if let Some(ref inline_context) = self.inline_context {
- for node in &inline_context.nodes {
- if is_top_or_bottom(&node.style.get_box().vertical_align) {
- return true;
- }
- }
- }
- false
- }
-
- pub fn is_text_or_replaced(&self) -> bool {
- match self.specific {
- SpecificFragmentInfo::Generic |
- SpecificFragmentInfo::InlineAbsolute(_) |
- SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
- SpecificFragmentInfo::InlineBlock(_) |
- SpecificFragmentInfo::Multicol |
- SpecificFragmentInfo::MulticolColumn |
- SpecificFragmentInfo::Table |
- SpecificFragmentInfo::TableCell |
- SpecificFragmentInfo::TableColumn(_) |
- SpecificFragmentInfo::TableRow |
- SpecificFragmentInfo::TableWrapper => false,
- SpecificFragmentInfo::Canvas(_) |
- SpecificFragmentInfo::GeneratedContent(_) |
- SpecificFragmentInfo::Iframe(_) |
- SpecificFragmentInfo::Image(_) |
- SpecificFragmentInfo::Media(_) |
- SpecificFragmentInfo::ScannedText(_) |
- SpecificFragmentInfo::TruncatedFragment(_) |
- SpecificFragmentInfo::Svg(_) |
- SpecificFragmentInfo::UnscannedText(_) => true,
- }
- }
-
- /// Returns the 4D matrix representing this fragment's transform.
- pub fn transform_matrix(
- &self,
- stacking_relative_border_box: &Rect<Au>,
- ) -> Option<LayoutTransform> {
- let list = &self.style.get_box().transform;
- let transform = LayoutTransform::from_untyped(
- &list
- .to_transform_3d_matrix(Some(stacking_relative_border_box))
- .ok()?
- .0,
- );
-
- let transform_origin = &self.style.get_box().transform_origin;
- let transform_origin_x = transform_origin
- .horizontal
- .to_used_value(stacking_relative_border_box.size.width)
- .to_f32_px();
- let transform_origin_y = transform_origin
- .vertical
- .to_used_value(stacking_relative_border_box.size.height)
- .to_f32_px();
- let transform_origin_z = transform_origin.depth.px();
-
- let pre_transform = LayoutTransform::create_translation(
- transform_origin_x,
- transform_origin_y,
- transform_origin_z,
- );
- let post_transform = LayoutTransform::create_translation(
- -transform_origin_x,
- -transform_origin_y,
- -transform_origin_z,
- );
-
- Some(pre_transform.pre_mul(&transform).pre_mul(&post_transform))
- }
-
- /// Returns the 4D matrix representing this fragment's perspective.
- pub fn perspective_matrix(
- &self,
- stacking_relative_border_box: &Rect<Au>,
- ) -> Option<LayoutTransform> {
- match self.style().get_box().perspective {
- Perspective::Length(length) => {
- let perspective_origin = self.style().get_box().perspective_origin;
- let perspective_origin = Point2D::new(
- perspective_origin
- .horizontal
- .to_used_value(stacking_relative_border_box.size.width),
- perspective_origin
- .vertical
- .to_used_value(stacking_relative_border_box.size.height),
- )
- .to_layout();
-
- let pre_transform = LayoutTransform::create_translation(
- perspective_origin.x,
- perspective_origin.y,
- 0.0,
- );
- let post_transform = LayoutTransform::create_translation(
- -perspective_origin.x,
- -perspective_origin.y,
- 0.0,
- );
-
- let perspective_matrix = LayoutTransform::from_untyped(
- &transform::create_perspective_matrix(length.px()),
- );
-
- Some(
- pre_transform
- .pre_mul(&perspective_matrix)
- .pre_mul(&post_transform),
- )
- },
- Perspective::None => None,
- }
- }
-}
-
-impl fmt::Debug for Fragment {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let border_padding_string = if !self.border_padding.is_zero() {
- format!("\nborder_padding={:?}", self.border_padding)
- } else {
- "".to_owned()
- };
-
- let margin_string = if !self.margin.is_zero() {
- format!("\nmargin={:?}", self.margin)
- } else {
- "".to_owned()
- };
-
- let damage_string = if self.restyle_damage != RestyleDamage::empty() {
- format!("\ndamage={:?}", self.restyle_damage)
- } else {
- "".to_owned()
- };
-
- write!(
- f,
- "\n{}({}) [{:?}]\nborder_box={:?}{}{}{}",
- self.specific.get_type(),
- self.debug_id,
- self.specific,
- self.border_box,
- border_padding_string,
- margin_string,
- damage_string
- )
- }
-}
-
-bitflags! {
- struct QuantitiesIncludedInIntrinsicInlineSizes: u8 {
- const INTRINSIC_INLINE_SIZE_INCLUDES_MARGINS = 0x01;
- const INTRINSIC_INLINE_SIZE_INCLUDES_PADDING = 0x02;
- const INTRINSIC_INLINE_SIZE_INCLUDES_BORDER = 0x04;
- const INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED = 0x08;
- }
-}
-
-bitflags! {
- // Various flags we can use when splitting fragments. See
- // `calculate_split_position_using_breaking_strategy()`.
- struct SplitOptions: u8 {
- #[doc = "True if this is the first fragment on the line."]
- const STARTS_LINE = 0x01;
- #[doc = "True if we should attempt to split at character boundaries if this split fails. \
- This is used to implement `overflow-wrap: break-word`."]
- const RETRY_AT_CHARACTER_BOUNDARIES = 0x02;
+ true
}
}
@@ -3283,173 +108,3 @@ pub trait FragmentBorderBoxIterator {
/// we skip the operation for this fragment, but continue processing siblings.
fn should_process(&mut self, fragment: &Fragment) -> bool;
}
-
-/// The coordinate system used in `stacking_relative_border_box()`. See the documentation of that
-/// method for details.
-#[derive(Clone, Debug, PartialEq)]
-pub enum CoordinateSystem {
- /// The border box returned is relative to the fragment's parent stacking context.
- Parent,
- /// The border box returned is relative to the fragment's own stacking context, if applicable.
- Own,
-}
-
-pub struct InlineStyleIterator<'a> {
- fragment: &'a Fragment,
- inline_style_index: usize,
- primary_style_yielded: bool,
-}
-
-impl<'a> Iterator for InlineStyleIterator<'a> {
- type Item = &'a ComputedValues;
-
- fn next(&mut self) -> Option<&'a ComputedValues> {
- if !self.primary_style_yielded {
- self.primary_style_yielded = true;
- return Some(&*self.fragment.style);
- }
- let inline_context = self.fragment.inline_context.as_ref()?;
- let inline_style_index = self.inline_style_index;
- if inline_style_index == inline_context.nodes.len() {
- return None;
- }
- self.inline_style_index += 1;
- Some(&*inline_context.nodes[inline_style_index].style)
- }
-}
-
-impl<'a> InlineStyleIterator<'a> {
- fn new(fragment: &Fragment) -> InlineStyleIterator {
- InlineStyleIterator {
- fragment: fragment,
- inline_style_index: 0,
- primary_style_yielded: false,
- }
- }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum WhitespaceStrippingResult {
- RetainFragment,
- FragmentContainedOnlyBidiControlCharacters,
- FragmentContainedOnlyWhitespace,
-}
-
-impl WhitespaceStrippingResult {
- fn from_unscanned_text_fragment_info(
- info: &UnscannedTextFragmentInfo,
- ) -> WhitespaceStrippingResult {
- if info.text.is_empty() {
- WhitespaceStrippingResult::FragmentContainedOnlyWhitespace
- } else if info.text.chars().all(gfx::text::util::is_bidi_control) {
- WhitespaceStrippingResult::FragmentContainedOnlyBidiControlCharacters
- } else {
- WhitespaceStrippingResult::RetainFragment
- }
- }
-}
-
-/// The overflow area. We need two different notions of overflow: paint overflow and scrollable
-/// overflow.
-#[derive(Clone, Copy, Debug)]
-pub struct Overflow {
- pub scroll: Rect<Au>,
- pub paint: Rect<Au>,
-}
-
-impl Overflow {
- pub fn new() -> Overflow {
- Overflow {
- scroll: Rect::zero(),
- paint: Rect::zero(),
- }
- }
-
- pub fn from_rect(border_box: &Rect<Au>) -> Overflow {
- Overflow {
- scroll: *border_box,
- paint: *border_box,
- }
- }
-
- pub fn union(&mut self, other: &Overflow) {
- self.scroll = self.scroll.union(&other.scroll);
- self.paint = self.paint.union(&other.paint);
- }
-
- pub fn translate(&mut self, by: &Vector2D<Au>) {
- self.scroll = self.scroll.translate(by);
- self.paint = self.paint.translate(by);
- }
-}
-
-bitflags! {
- pub struct FragmentFlags: u8 {
- // TODO(stshine): find a better name since these flags can also be used for grid item.
- /// Whether this fragment represents a child in a row flex container.
- const IS_INLINE_FLEX_ITEM = 0b0000_0001;
- /// Whether this fragment represents a child in a column flex container.
- const IS_BLOCK_FLEX_ITEM = 0b0000_0010;
- /// Whether this fragment represents the generated text from a text-overflow clip.
- const IS_ELLIPSIS = 0b0000_0100;
- }
-}
-
-/// Specified distances from the margin edge of a block to its content in the inline direction.
-/// These are returned by `guess_inline_content_edge_offsets()` and are used in the float placement
-/// speculation logic.
-#[derive(Clone, Copy, Debug)]
-pub struct SpeculatedInlineContentEdgeOffsets {
- pub start: Au,
- pub end: Au,
-}
-
-#[cfg(not(debug_assertions))]
-#[derive(Clone)]
-struct DebugId;
-
-#[cfg(debug_assertions)]
-#[derive(Clone)]
-struct DebugId(u16);
-
-#[cfg(not(debug_assertions))]
-impl DebugId {
- pub fn new() -> DebugId {
- DebugId
- }
-}
-
-#[cfg(debug_assertions)]
-impl DebugId {
- pub fn new() -> DebugId {
- DebugId(layout_debug::generate_unique_debug_id())
- }
-}
-
-#[cfg(not(debug_assertions))]
-impl fmt::Display for DebugId {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{:p}", &self)
- }
-}
-
-#[cfg(debug_assertions)]
-impl fmt::Display for DebugId {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.0)
- }
-}
-
-#[cfg(not(debug_assertions))]
-impl Serialize for DebugId {
- fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
- serializer.serialize_str(&format!("{:p}", &self))
- }
-}
-
-#[cfg(debug_assertions)]
-impl Serialize for DebugId {
- fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
- serializer.serialize_u16(self.0)
- }
-}
diff --git a/components/layout_2020/generated_content.rs b/components/layout_2020/generated_content.rs
deleted file mode 100644
index e1ebc570a62..00000000000
--- a/components/layout_2020/generated_content.rs
+++ /dev/null
@@ -1,651 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The generated content assignment phase.
-//!
-//! This phase handles CSS counters, quotes, and ordered lists per CSS § 12.3-12.5. It cannot be
-//! done in parallel and is therefore a sequential pass that runs on as little of the flow tree
-//! as possible.
-
-use crate::context::{with_thread_local_font_context, LayoutContext};
-use crate::display_list::items::OpaqueNode;
-use crate::flow::{Flow, FlowFlags, GetBaseFlow, ImmutableFlowUtils};
-use crate::fragment::{
- Fragment, GeneratedContentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo,
-};
-use crate::text::TextRunScanner;
-use crate::traversal::InorderFlowTraversal;
-use script_layout_interface::wrapper_traits::PseudoElementType;
-use smallvec::SmallVec;
-use std::collections::{HashMap, LinkedList};
-use style::computed_values::display::T as Display;
-use style::computed_values::list_style_type::T as ListStyleType;
-use style::properties::ComputedValues;
-use style::selector_parser::RestyleDamage;
-use style::servo::restyle_damage::ServoRestyleDamage;
-use style::values::generics::counters::ContentItem;
-
-// Decimal styles per CSS-COUNTER-STYLES § 6.1:
-static DECIMAL: [char; 10] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
-// TODO(pcwalton): `decimal-leading-zero`
-static ARABIC_INDIC: [char; 10] = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
-// TODO(pcwalton): `armenian`, `upper-armenian`, `lower-armenian`
-static BENGALI: [char; 10] = [
- '০', '১', '২', '৩', '৪', '৫', '৬', '৭', '৮', '৯',
-];
-static CAMBODIAN: [char; 10] = [
- '០', '១', '២', '៣', '៤', '៥', '៦', '៧', '៨', '៩',
-];
-// TODO(pcwalton): Suffix for CJK decimal.
-static CJK_DECIMAL: [char; 10] = [
- '〇', '一', '二', '三', '四', '五', '六', '七', '八', '九',
-];
-static DEVANAGARI: [char; 10] = [
- '०', '१', '२', '३', '४', '५', '६', '७', '८', '९',
-];
-// TODO(pcwalton): `georgian`
-static GUJARATI: [char; 10] = [
- '૦', '૧', '૨', '૩', '૪', '૫', '૬', '૭', '૮', '૯',
-];
-static GURMUKHI: [char; 10] = [
- '੦', '੧', '੨', '੩', '੪', '੫', '੬', '੭', '੮', '੯',
-];
-// TODO(pcwalton): `hebrew`
-static KANNADA: [char; 10] = [
- '೦', '೧', '೨', '೩', '೪', '೫', '೬', '೭', '೮', '೯',
-];
-static LAO: [char; 10] = [
- '໐', '໑', '໒', '໓', '໔', '໕', '໖', '໗', '໘', '໙',
-];
-static MALAYALAM: [char; 10] = [
- '൦', '൧', '൨', '൩', '൪', '൫', '൬', '൭', '൮', '൯',
-];
-static MONGOLIAN: [char; 10] = [
- '᠐', '᠑', '᠒', '᠓', '᠔', '᠕', '᠖', '᠗', '᠘', '᠙',
-];
-static MYANMAR: [char; 10] = [
- '၀', '၁', '၂', '၃', '၄', '၅', '၆', '၇', '၈', '၉',
-];
-static ORIYA: [char; 10] = [
- '୦', '୧', '୨', '୩', '୪', '୫', '୬', '୭', '୮', '୯',
-];
-static PERSIAN: [char; 10] = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
-// TODO(pcwalton): `lower-roman`, `upper-roman`
-static TELUGU: [char; 10] = [
- '౦', '౧', '౨', '౩', '౪', '౫', '౬', '౭', '౮', '౯',
-];
-static THAI: [char; 10] = [
- '๐', '๑', '๒', '๓', '๔', '๕', '๖', '๗', '๘', '๙',
-];
-static TIBETAN: [char; 10] = [
- '༠', '༡', '༢', '༣', '༤', '༥', '༦', '༧', '༨', '༩',
-];
-
-// Alphabetic styles per CSS-COUNTER-STYLES § 6.2:
-static LOWER_ALPHA: [char; 26] = [
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
- 't', 'u', 'v', 'w', 'x', 'y', 'z',
-];
-static UPPER_ALPHA: [char; 26] = [
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
- 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
-];
-static CJK_EARTHLY_BRANCH: [char; 12] = [
- '子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥',
-];
-static CJK_HEAVENLY_STEM: [char; 10] = [
- '甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸',
-];
-static LOWER_GREEK: [char; 24] = [
- 'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ', 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', 'π',
- 'ρ', 'σ', 'τ', 'υ', 'φ', 'χ', 'ψ', 'ω',
-];
-static HIRAGANA: [char; 48] = [
- 'あ', 'い', 'う', 'え', 'お', 'か', 'き', 'く', 'け', 'こ', 'さ', 'し', 'す',
- 'せ', 'そ', 'た', 'ち', 'つ', 'て', 'と', 'な', 'に', 'ぬ', 'ね', 'の', 'は',
- 'ひ', 'ふ', 'へ', 'ほ', 'ま', 'み', 'む', 'め', 'も', 'や', 'ゆ', 'よ', 'ら',
- 'り', 'る', 'れ', 'ろ', 'わ', 'ゐ', 'ゑ', 'を', 'ん',
-];
-static HIRAGANA_IROHA: [char; 47] = [
- 'い', 'ろ', 'は', 'に', 'ほ', 'へ', 'と', 'ち', 'り', 'ぬ', 'る', 'を', 'わ',
- 'か', 'よ', 'た', 'れ', 'そ', 'つ', 'ね', 'な', 'ら', 'む', 'う', 'ゐ', 'の',
- 'お', 'く', 'や', 'ま', 'け', 'ふ', 'こ', 'え', 'て', 'あ', 'さ', 'き', 'ゆ',
- 'め', 'み', 'し', 'ゑ', 'ひ', 'も', 'せ', 'す',
-];
-static KATAKANA: [char; 48] = [
- 'ア', 'イ', 'ウ', 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ', 'サ', 'シ', 'ス',
- 'セ', 'ソ', 'タ', 'チ', 'ツ', 'テ', 'ト', 'ナ', 'ニ', 'ヌ', 'ネ', 'ノ', 'ハ',
- 'ヒ', 'フ', 'ヘ', 'ホ', 'マ', 'ミ', 'ム', 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ',
- 'リ', 'ル', 'レ', 'ロ', 'ワ', 'ヰ', 'ヱ', 'ヲ', 'ン',
-];
-static KATAKANA_IROHA: [char; 47] = [
- 'イ', 'ロ', 'ハ', 'ニ', 'ホ', 'ヘ', 'ト', 'チ', 'リ', 'ヌ', 'ル', 'ヲ', 'ワ',
- 'カ', 'ヨ', 'タ', 'レ', 'ソ', 'ツ', 'ネ', 'ナ', 'ラ', 'ム', 'ウ', 'ヰ', 'ノ',
- 'オ', 'ク', 'ヤ', 'マ', 'ケ', 'フ', 'コ', 'エ', 'テ', 'ア', 'サ', 'キ', 'ユ',
- 'メ', 'ミ', 'シ', 'ヱ', 'ヒ', 'モ', 'セ', 'ス',
-];
-
-/// The generated content resolution traversal.
-pub struct ResolveGeneratedContent<'a> {
- /// The layout context.
- layout_context: &'a LayoutContext<'a>,
- /// The counter representing an ordered list item.
- list_item: Counter,
- /// Named CSS counters.
- counters: HashMap<String, Counter>,
- /// The level of quote nesting.
- quote: u32,
-}
-
-impl<'a> ResolveGeneratedContent<'a> {
- /// Creates a new generated content resolution traversal.
- pub fn new(layout_context: &'a LayoutContext) -> ResolveGeneratedContent<'a> {
- ResolveGeneratedContent {
- layout_context: layout_context,
- list_item: Counter::new(),
- counters: HashMap::new(),
- quote: 0,
- }
- }
-}
-
-impl<'a> InorderFlowTraversal for ResolveGeneratedContent<'a> {
- #[inline]
- fn process(&mut self, flow: &mut dyn Flow, level: u32) {
- let mut mutator = ResolveGeneratedContentFragmentMutator {
- traversal: self,
- level: level,
- is_block: flow.is_block_like(),
- incremented: false,
- };
- flow.mutate_fragments(&mut |fragment| mutator.mutate_fragment(fragment))
- }
-
- #[inline]
- fn should_process_subtree(&mut self, flow: &mut dyn Flow) -> bool {
- flow.base()
- .restyle_damage
- .intersects(ServoRestyleDamage::RESOLVE_GENERATED_CONTENT) ||
- flow.base().flags.intersects(
- FlowFlags::AFFECTS_COUNTERS | FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN,
- )
- }
-}
-
-/// The object that mutates the generated content fragments.
-struct ResolveGeneratedContentFragmentMutator<'a, 'b: 'a> {
- /// The traversal.
- traversal: &'a mut ResolveGeneratedContent<'b>,
- /// The level we're at in the flow tree.
- level: u32,
- /// Whether this flow is a block flow.
- is_block: bool,
- /// Whether we've incremented the counter yet.
- incremented: bool,
-}
-
-impl<'a, 'b> ResolveGeneratedContentFragmentMutator<'a, 'b> {
- fn mutate_fragment(&mut self, fragment: &mut Fragment) {
- // We only reset and/or increment counters once per flow. This avoids double-incrementing
- // counters on list items (once for the main fragment and once for the marker).
- if !self.incremented {
- self.reset_and_increment_counters_as_necessary(fragment);
- }
-
- let mut list_style_type = fragment.style().get_list().list_style_type;
- if fragment.style().get_box().display != Display::ListItem {
- list_style_type = ListStyleType::None
- }
-
- let mut new_info = None;
- {
- let info =
- if let SpecificFragmentInfo::GeneratedContent(ref mut info) = fragment.specific {
- info
- } else {
- return;
- };
-
- match **info {
- GeneratedContentInfo::ListItem => {
- new_info = self.traversal.list_item.render(
- self.traversal.layout_context,
- fragment.node,
- fragment.pseudo.clone(),
- fragment.style.clone(),
- list_style_type,
- RenderingMode::Suffix(".\u{00a0}"),
- )
- },
- GeneratedContentInfo::Empty |
- GeneratedContentInfo::ContentItem(ContentItem::String(_)) => {
- // Nothing to do here.
- },
- GeneratedContentInfo::ContentItem(ContentItem::Counter(
- ref counter_name,
- counter_style,
- )) => {
- let temporary_counter = Counter::new();
- let counter = self
- .traversal
- .counters
- .get(&*counter_name.0)
- .unwrap_or(&temporary_counter);
- new_info = counter.render(
- self.traversal.layout_context,
- fragment.node,
- fragment.pseudo.clone(),
- fragment.style.clone(),
- counter_style,
- RenderingMode::Plain,
- )
- },
- GeneratedContentInfo::ContentItem(ContentItem::Counters(
- ref counter_name,
- ref separator,
- counter_style,
- )) => {
- let temporary_counter = Counter::new();
- let counter = self
- .traversal
- .counters
- .get(&*counter_name.0)
- .unwrap_or(&temporary_counter);
- new_info = counter.render(
- self.traversal.layout_context,
- fragment.node,
- fragment.pseudo,
- fragment.style.clone(),
- counter_style,
- RenderingMode::All(&separator),
- );
- },
- GeneratedContentInfo::ContentItem(ContentItem::OpenQuote) => {
- new_info = render_text(
- self.traversal.layout_context,
- fragment.node,
- fragment.pseudo,
- fragment.style.clone(),
- self.quote(&*fragment.style, false),
- );
- self.traversal.quote += 1
- },
- GeneratedContentInfo::ContentItem(ContentItem::CloseQuote) => {
- if self.traversal.quote >= 1 {
- self.traversal.quote -= 1
- }
-
- new_info = render_text(
- self.traversal.layout_context,
- fragment.node,
- fragment.pseudo,
- fragment.style.clone(),
- self.quote(&*fragment.style, true),
- );
- },
- GeneratedContentInfo::ContentItem(ContentItem::NoOpenQuote) => {
- self.traversal.quote += 1
- },
- GeneratedContentInfo::ContentItem(ContentItem::NoCloseQuote) => {
- if self.traversal.quote >= 1 {
- self.traversal.quote -= 1
- }
- },
- GeneratedContentInfo::ContentItem(ContentItem::Url(..)) => {
- unreachable!("Servo doesn't parse content: url(..) yet")
- },
- }
- };
-
- fragment.specific = match new_info {
- Some(new_info) => new_info,
- // If the fragment did not generate any content, replace it with a no-op placeholder
- // so that it isn't processed again on the next layout. FIXME (mbrubeck): When
- // processing an inline flow, this traversal should be allowed to insert or remove
- // fragments. Then we can just remove these fragments rather than adding placeholders.
- None => SpecificFragmentInfo::GeneratedContent(Box::new(GeneratedContentInfo::Empty)),
- };
- }
-
- fn reset_and_increment_counters_as_necessary(&mut self, fragment: &mut Fragment) {
- let mut list_style_type = fragment.style().get_list().list_style_type;
- if !self.is_block || fragment.style().get_box().display != Display::ListItem {
- list_style_type = ListStyleType::None
- }
-
- match list_style_type {
- ListStyleType::Disc |
- ListStyleType::None |
- ListStyleType::Circle |
- ListStyleType::Square |
- ListStyleType::DisclosureOpen |
- ListStyleType::DisclosureClosed => {},
- _ => self.traversal.list_item.increment(self.level, 1),
- }
-
- // Truncate down counters.
- for (_, counter) in &mut self.traversal.counters {
- counter.truncate_to_level(self.level);
- }
- self.traversal.list_item.truncate_to_level(self.level);
-
- for pair in &*fragment.style().get_counters().counter_reset {
- let counter_name = &*pair.name.0;
- if let Some(ref mut counter) = self.traversal.counters.get_mut(counter_name) {
- counter.reset(self.level, pair.value);
- continue;
- }
-
- let mut counter = Counter::new();
- counter.reset(self.level, pair.value);
- self.traversal
- .counters
- .insert(counter_name.to_owned(), counter);
- }
-
- for pair in &*fragment.style().get_counters().counter_increment {
- let counter_name = &*pair.name.0;
- if let Some(ref mut counter) = self.traversal.counters.get_mut(counter_name) {
- counter.increment(self.level, pair.value);
- continue;
- }
-
- let mut counter = Counter::new();
- counter.increment(self.level, pair.value);
- self.traversal
- .counters
- .insert(counter_name.to_owned(), counter);
- }
-
- self.incremented = true
- }
-
- fn quote(&self, style: &ComputedValues, close: bool) -> String {
- let quotes = &style.get_list().quotes;
- if quotes.0.is_empty() {
- return String::new();
- }
- let pair = if self.traversal.quote as usize >= quotes.0.len() {
- quotes.0.last().unwrap()
- } else {
- &quotes.0[self.traversal.quote as usize]
- };
- if close {
- pair.closing.to_string()
- } else {
- pair.opening.to_string()
- }
- }
-}
-
-/// A counter per CSS 2.1 § 12.4.
-struct Counter {
- /// The values at each level.
- values: Vec<CounterValue>,
-}
-
-impl Counter {
- fn new() -> Counter {
- Counter { values: Vec::new() }
- }
-
- fn reset(&mut self, level: u32, value: i32) {
- // Do we have an instance of the counter at this level? If so, just mutate it.
- if let Some(ref mut existing_value) = self.values.last_mut() {
- if level == existing_value.level {
- existing_value.value = value;
- return;
- }
- }
-
- // Otherwise, push a new instance of the counter.
- self.values.push(CounterValue {
- level: level,
- value: value,
- })
- }
-
- fn truncate_to_level(&mut self, level: u32) {
- if let Some(position) = self.values.iter().position(|value| value.level > level) {
- self.values.truncate(position)
- }
- }
-
- fn increment(&mut self, level: u32, amount: i32) {
- if let Some(ref mut value) = self.values.last_mut() {
- value.value += amount;
- return;
- }
-
- self.values.push(CounterValue {
- level: level,
- value: amount,
- })
- }
-
- fn render(
- &self,
- layout_context: &LayoutContext,
- node: OpaqueNode,
- pseudo: PseudoElementType,
- style: crate::ServoArc<ComputedValues>,
- list_style_type: ListStyleType,
- mode: RenderingMode,
- ) -> Option<SpecificFragmentInfo> {
- let mut string = String::new();
- match mode {
- RenderingMode::Plain => {
- let value = match self.values.last() {
- Some(ref value) => value.value,
- None => 0,
- };
- push_representation(value, list_style_type, &mut string)
- },
- RenderingMode::Suffix(suffix) => {
- let value = match self.values.last() {
- Some(ref value) => value.value,
- None => 0,
- };
- push_representation(value, list_style_type, &mut string);
- string.push_str(suffix)
- },
- RenderingMode::All(separator) => {
- let mut first = true;
- for value in &self.values {
- if !first {
- string.push_str(separator)
- }
- first = false;
- push_representation(value.value, list_style_type, &mut string)
- }
- },
- }
-
- if string.is_empty() {
- None
- } else {
- render_text(layout_context, node, pseudo, style, string)
- }
- }
-}
-
-/// How a counter value is to be rendered.
-enum RenderingMode<'a> {
- /// The innermost counter value is rendered with no extra decoration.
- Plain,
- /// The innermost counter value is rendered with the given string suffix.
- Suffix(&'a str),
- /// All values of the counter are rendered with the given separator string between them.
- All(&'a str),
-}
-
-/// The value of a counter at a given level.
-struct CounterValue {
- /// The level of the flow tree that this corresponds to.
- level: u32,
- /// The value of the counter at this level.
- value: i32,
-}
-
-/// Creates fragment info for a literal string.
-fn render_text(
- layout_context: &LayoutContext,
- node: OpaqueNode,
- pseudo: PseudoElementType,
- style: crate::ServoArc<ComputedValues>,
- string: String,
-) -> Option<SpecificFragmentInfo> {
- let mut fragments = LinkedList::new();
- let info = SpecificFragmentInfo::UnscannedText(Box::new(UnscannedTextFragmentInfo::new(
- string.into_boxed_str(),
- None,
- )));
- fragments.push_back(Fragment::from_opaque_node_and_style(
- node,
- pseudo,
- style.clone(),
- style,
- RestyleDamage::rebuild_and_reflow(),
- info,
- ));
- // FIXME(pcwalton): This should properly handle multiple marker fragments. This could happen
- // due to text run splitting.
- let fragments = with_thread_local_font_context(layout_context, |font_context| {
- TextRunScanner::new().scan_for_runs(font_context, fragments)
- });
- if fragments.is_empty() {
- None
- } else {
- Some(fragments.fragments.into_iter().next().unwrap().specific)
- }
-}
-
-/// Appends string that represents the value rendered using the system appropriate for the given
-/// `list-style-type` onto the given string.
-fn push_representation(value: i32, list_style_type: ListStyleType, accumulator: &mut String) {
- match list_style_type {
- ListStyleType::None => {},
- ListStyleType::Disc |
- ListStyleType::Circle |
- ListStyleType::Square |
- ListStyleType::DisclosureOpen |
- ListStyleType::DisclosureClosed => accumulator.push(static_representation(list_style_type)),
- ListStyleType::Decimal => push_numeric_representation(value, &DECIMAL, accumulator),
- ListStyleType::ArabicIndic => {
- push_numeric_representation(value, &ARABIC_INDIC, accumulator)
- },
- ListStyleType::Bengali => push_numeric_representation(value, &BENGALI, accumulator),
- ListStyleType::Cambodian | ListStyleType::Khmer => {
- push_numeric_representation(value, &CAMBODIAN, accumulator)
- },
- ListStyleType::CjkDecimal => push_numeric_representation(value, &CJK_DECIMAL, accumulator),
- ListStyleType::Devanagari => push_numeric_representation(value, &DEVANAGARI, accumulator),
- ListStyleType::Gujarati => push_numeric_representation(value, &GUJARATI, accumulator),
- ListStyleType::Gurmukhi => push_numeric_representation(value, &GURMUKHI, accumulator),
- ListStyleType::Kannada => push_numeric_representation(value, &KANNADA, accumulator),
- ListStyleType::Lao => push_numeric_representation(value, &LAO, accumulator),
- ListStyleType::Malayalam => push_numeric_representation(value, &MALAYALAM, accumulator),
- ListStyleType::Mongolian => push_numeric_representation(value, &MONGOLIAN, accumulator),
- ListStyleType::Myanmar => push_numeric_representation(value, &MYANMAR, accumulator),
- ListStyleType::Oriya => push_numeric_representation(value, &ORIYA, accumulator),
- ListStyleType::Persian => push_numeric_representation(value, &PERSIAN, accumulator),
- ListStyleType::Telugu => push_numeric_representation(value, &TELUGU, accumulator),
- ListStyleType::Thai => push_numeric_representation(value, &THAI, accumulator),
- ListStyleType::Tibetan => push_numeric_representation(value, &TIBETAN, accumulator),
- ListStyleType::LowerAlpha => {
- push_alphabetic_representation(value, &LOWER_ALPHA, accumulator)
- },
- ListStyleType::UpperAlpha => {
- push_alphabetic_representation(value, &UPPER_ALPHA, accumulator)
- },
- ListStyleType::CjkEarthlyBranch => {
- push_alphabetic_representation(value, &CJK_EARTHLY_BRANCH, accumulator)
- },
- ListStyleType::CjkHeavenlyStem => {
- push_alphabetic_representation(value, &CJK_HEAVENLY_STEM, accumulator)
- },
- ListStyleType::LowerGreek => {
- push_alphabetic_representation(value, &LOWER_GREEK, accumulator)
- },
- ListStyleType::Hiragana => push_alphabetic_representation(value, &HIRAGANA, accumulator),
- ListStyleType::HiraganaIroha => {
- push_alphabetic_representation(value, &HIRAGANA_IROHA, accumulator)
- },
- ListStyleType::Katakana => push_alphabetic_representation(value, &KATAKANA, accumulator),
- ListStyleType::KatakanaIroha => {
- push_alphabetic_representation(value, &KATAKANA_IROHA, accumulator)
- },
- }
-}
-
-/// Returns the static character that represents the value rendered using the given list-style, if
-/// possible.
-pub fn static_representation(list_style_type: ListStyleType) -> char {
- match list_style_type {
- ListStyleType::Disc => '•',
- ListStyleType::Circle => '◦',
- ListStyleType::Square => '▪',
- ListStyleType::DisclosureOpen => '▾',
- ListStyleType::DisclosureClosed => '‣',
- _ => panic!("No static representation for this list-style-type!"),
- }
-}
-
-/// Pushes the string that represents the value rendered using the given *alphabetic system* onto
-/// the accumulator per CSS-COUNTER-STYLES § 3.1.4.
-fn push_alphabetic_representation(value: i32, system: &[char], accumulator: &mut String) {
- let mut abs_value = handle_negative_value(value, accumulator);
-
- let mut string: SmallVec<[char; 8]> = SmallVec::new();
- while abs_value != 0 {
- // Step 1.
- abs_value = abs_value - 1;
- // Step 2.
- string.push(system[abs_value % system.len()]);
- // Step 3.
- abs_value = abs_value / system.len();
- }
-
- accumulator.extend(string.iter().cloned().rev())
-}
-
-/// Pushes the string that represents the value rendered using the given *numeric system* onto the
-/// accumulator per CSS-COUNTER-STYLES § 3.1.5.
-fn push_numeric_representation(value: i32, system: &[char], accumulator: &mut String) {
- let mut abs_value = handle_negative_value(value, accumulator);
-
- // Step 1.
- if abs_value == 0 {
- accumulator.push(system[0]);
- return;
- }
-
- // Step 2.
- let mut string: SmallVec<[char; 8]> = SmallVec::new();
- while abs_value != 0 {
- // Step 2.1.
- string.push(system[abs_value % system.len()]);
- // Step 2.2.
- abs_value = abs_value / system.len();
- }
-
- // Step 3.
- accumulator.extend(string.iter().cloned().rev())
-}
-
-/// If the system uses a negative sign, handle negative values per CSS-COUNTER-STYLES § 2.
-///
-/// Returns the absolute value of the counter.
-fn handle_negative_value(value: i32, accumulator: &mut String) -> usize {
- // 3. If the counter value is negative and the counter style uses a negative sign, instead
- // generate an initial representation using the absolute value of the counter value.
- if value < 0 {
- // TODO: Support different negative signs using the 'negative' descriptor.
- // https://drafts.csswg.org/date/2015-07-16/css-counter-styles/#counter-style-negative
- accumulator.push('-');
- value.abs() as usize
- } else {
- value as usize
- }
-}
diff --git a/components/layout_2020/incremental.rs b/components/layout_2020/incremental.rs
deleted file mode 100644
index 4e313f088bb..00000000000
--- a/components/layout_2020/incremental.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::flow::{Flow, FlowFlags, GetBaseFlow};
-use style::computed_values::float::T as Float;
-use style::selector_parser::RestyleDamage;
-use style::servo::restyle_damage::ServoRestyleDamage;
-
-/// Used in a flow traversal to indicate whether this re-layout should be incremental or not.
-#[derive(Clone, Copy, PartialEq)]
-pub enum RelayoutMode {
- Incremental,
- Force,
-}
-
-bitflags! {
- pub struct SpecialRestyleDamage: u8 {
- #[doc = "If this flag is set, we need to reflow the entire document. This is more or less a \
- temporary hack to deal with cases that we don't handle incrementally yet."]
- const REFLOW_ENTIRE_DOCUMENT = 0x01;
- }
-}
-
-impl dyn Flow {
- pub fn compute_layout_damage(&mut self) -> SpecialRestyleDamage {
- let mut special_damage = SpecialRestyleDamage::empty();
- let is_absolutely_positioned = self
- .base()
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
-
- // In addition to damage, we use this phase to compute whether nodes affect CSS counters.
- let mut has_counter_affecting_children = false;
-
- {
- let self_base = self.mut_base();
- // Take a snapshot of the parent damage before updating it with damage from children.
- let parent_damage = self_base.restyle_damage;
-
- for kid in self_base.children.iter_mut() {
- let child_is_absolutely_positioned = kid
- .base()
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
- kid.mut_base().restyle_damage.insert(
- parent_damage
- .damage_for_child(is_absolutely_positioned, child_is_absolutely_positioned),
- );
- {
- let kid: &mut dyn Flow = kid;
- special_damage.insert(kid.compute_layout_damage());
- }
- self_base.restyle_damage.insert(
- kid.base()
- .restyle_damage
- .damage_for_parent(child_is_absolutely_positioned),
- );
-
- has_counter_affecting_children = has_counter_affecting_children ||
- kid.base().flags.intersects(
- FlowFlags::AFFECTS_COUNTERS | FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN,
- );
- }
- }
-
- let self_base = self.mut_base();
- if self_base.flags.float_kind() != Float::None &&
- self_base
- .restyle_damage
- .intersects(ServoRestyleDamage::REFLOW)
- {
- special_damage.insert(SpecialRestyleDamage::REFLOW_ENTIRE_DOCUMENT);
- }
-
- if has_counter_affecting_children {
- self_base
- .flags
- .insert(FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN)
- } else {
- self_base
- .flags
- .remove(FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN)
- }
-
- special_damage
- }
-
- pub fn reflow_entire_document(&mut self) {
- let self_base = self.mut_base();
- self_base
- .restyle_damage
- .insert(RestyleDamage::rebuild_and_reflow());
- self_base
- .restyle_damage
- .remove(ServoRestyleDamage::RECONSTRUCT_FLOW);
- for kid in self_base.children.iter_mut() {
- kid.reflow_entire_document();
- }
- }
-}
diff --git a/components/layout_2020/inline.rs b/components/layout_2020/inline.rs
deleted file mode 100644
index f4dacefc5ef..00000000000
--- a/components/layout_2020/inline.rs
+++ /dev/null
@@ -1,2220 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::block::AbsoluteAssignBSizesTraversal;
-use crate::context::{LayoutContext, LayoutFontContext};
-use crate::display_list::items::{DisplayListSection, OpaqueNode};
-use crate::display_list::{
- BorderPaintingMode, DisplayListBuildState, StackingContextCollectionState,
-};
-use crate::floats::{FloatKind, Floats, PlacementInfo};
-use crate::flow::{BaseFlow, Flow, FlowClass, ForceNonfloatedFlag};
-use crate::flow::{EarlyAbsolutePositionInfo, FlowFlags, GetBaseFlow, OpaqueFlow};
-use crate::flow_ref::FlowRef;
-use crate::fragment::FragmentFlags;
-use crate::fragment::SpecificFragmentInfo;
-use crate::fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow};
-use crate::layout_debug;
-use crate::model::IntrinsicISizesContribution;
-use crate::text;
-use crate::traversal::PreorderFlowTraversal;
-use crate::ServoArc;
-use app_units::{Au, MIN_AU};
-use euclid::{Point2D, Rect, Size2D};
-use gfx::font::FontMetrics;
-use gfx_traits::print_tree::PrintTree;
-use range::{Range, RangeIndex};
-use script_layout_interface::wrapper_traits::PseudoElementType;
-use servo_geometry::MaxRect;
-use std::cmp::max;
-use std::collections::VecDeque;
-use std::sync::Arc;
-use std::{fmt, i32, isize, mem};
-use style::computed_values::display::T as Display;
-use style::computed_values::overflow_x::T as StyleOverflow;
-use style::computed_values::position::T as Position;
-use style::computed_values::text_align::T as TextAlign;
-use style::computed_values::text_justify::T as TextJustify;
-use style::computed_values::white_space::T as WhiteSpace;
-use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
-use style::properties::ComputedValues;
-use style::servo::restyle_damage::ServoRestyleDamage;
-use style::values::computed::box_::VerticalAlign;
-use style::values::generics::box_::VerticalAlignKeyword;
-use style::values::specified::text::TextOverflowSide;
-use unicode_bidi as bidi;
-
-/// `Line`s are represented as offsets into the child list, rather than
-/// as an object that "owns" fragments. Choosing a different set of line
-/// breaks requires a new list of offsets, and possibly some splitting and
-/// merging of TextFragments.
-///
-/// A similar list will keep track of the mapping between CSS fragments and
-/// the corresponding fragments in the inline flow.
-///
-/// After line breaks are determined, render fragments in the inline flow may
-/// overlap visually. For example, in the case of nested inline CSS fragments,
-/// outer inlines must be at least as large as the inner inlines, for
-/// purposes of drawing noninherited things like backgrounds, borders,
-/// outlines.
-///
-/// N.B. roc has an alternative design where the list instead consists of
-/// things like "start outer fragment, text, start inner fragment, text, end inner
-/// fragment, text, end outer fragment, text". This seems a little complicated to
-/// serve as the starting point, but the current design doesn't make it
-/// hard to try out that alternative.
-///
-/// Line fragments also contain some metadata used during line breaking. The
-/// green zone is the area that the line can expand to before it collides
-/// with a float or a horizontal wall of the containing block. The block-start
-/// inline-start corner of the green zone is the same as that of the line, but
-/// the green zone can be taller and wider than the line itself.
-#[derive(Clone, Debug, Serialize)]
-pub struct Line {
- /// A range of line indices that describe line breaks.
- ///
- /// For example, consider the following HTML and rendered element with
- /// linebreaks:
- ///
- /// ~~~html
- /// <span>I <span>like truffles, <img></span> yes I do.</span>
- /// ~~~
- ///
- /// ~~~text
- /// +------------+
- /// | I like |
- /// | truffles, |
- /// | +----+ |
- /// | | | |
- /// | +----+ yes |
- /// | I do. |
- /// +------------+
- /// ~~~
- ///
- /// The ranges that describe these lines would be:
- ///
- /// | [0, 2) | [2, 3) | [3, 5) | [5, 6) |
- /// |----------|-------------|-------------|----------|
- /// | 'I like' | 'truffles,' | '<img> yes' | 'I do.' |
- pub range: Range<FragmentIndex>,
-
- /// The bidirectional embedding level runs for this line, in visual order.
- ///
- /// Can be set to `None` if the line is 100% left-to-right.
- pub visual_runs: Option<Vec<(Range<FragmentIndex>, bidi::Level)>>,
-
- /// The bounds are the exact position and extents of the line with respect
- /// to the parent box.
- ///
- /// For example, for the HTML below...
- ///
- /// ~~~html
- /// <div><span>I like truffles, <img></span></div>
- /// ~~~
- ///
- /// ...the bounds would be:
- ///
- /// ~~~text
- /// +-----------------------------------------------------------+
- /// | ^ |
- /// | | |
- /// | origin.y |
- /// | | |
- /// | v |
- /// |< - origin.x ->+ - - - - - - - - +---------+---- |
- /// | | | | ^ |
- /// | | | <img> | size.block |
- /// | I like truffles, | | v |
- /// | + - - - - - - - - +---------+---- |
- /// | | | |
- /// | |<------ size.inline ------>| |
- /// | |
- /// | |
- /// +-----------------------------------------------------------+
- /// ~~~
- pub bounds: LogicalRect<Au>,
-
- /// The green zone is the greatest extent from which a line can extend to
- /// before it collides with a float.
- ///
- /// ~~~text
- /// +-----------------------+
- /// |::::::::::::::::: |
- /// |:::::::::::::::::FFFFFF|
- /// |============:::::FFFFFF|
- /// |:::::::::::::::::FFFFFF|
- /// |:::::::::::::::::FFFFFF|
- /// |::::::::::::::::: |
- /// | FFFFFFFFF |
- /// | FFFFFFFFF |
- /// | FFFFFFFFF |
- /// | |
- /// +-----------------------+
- ///
- /// === line
- /// ::: green zone
- /// FFF float
- /// ~~~
- pub green_zone: LogicalSize<Au>,
-
- /// The minimum metrics for this line, as specified by the style.
- pub minimum_metrics: LineMetrics,
-
- /// The actual metrics for this line.
- pub metrics: LineMetrics,
-}
-
-impl Line {
- fn new(writing_mode: WritingMode, minimum_metrics: &LineMetrics) -> Line {
- Line {
- range: Range::empty(),
- visual_runs: None,
- bounds: LogicalRect::zero(writing_mode),
- green_zone: LogicalSize::zero(writing_mode),
- minimum_metrics: *minimum_metrics,
- metrics: *minimum_metrics,
- }
- }
-
- /// Returns the new metrics that this line would have if `new_fragment` were added to it.
- ///
- /// FIXME(pcwalton): this assumes that the tallest fragment in the line determines the line
- /// block-size. This might not be the case with some weird text fonts.
- fn new_metrics_for_fragment(
- &self,
- new_fragment: &Fragment,
- layout_context: &LayoutContext,
- ) -> LineMetrics {
- if !new_fragment.is_vertically_aligned_to_top_or_bottom() {
- let fragment_inline_metrics =
- new_fragment.aligned_inline_metrics(layout_context, &self.minimum_metrics, None);
- self.metrics
- .new_metrics_for_fragment(&fragment_inline_metrics)
- } else {
- self.metrics
- }
- }
-
- /// Returns the new block size that this line would have if `new_fragment` were added to it.
- /// `new_inline_metrics` represents the new inline metrics that this line would have; it can
- /// be computed with `new_inline_metrics()`.
- fn new_block_size_for_fragment(
- &self,
- new_fragment: &Fragment,
- new_line_metrics: &LineMetrics,
- layout_context: &LayoutContext,
- ) -> Au {
- let new_block_size = if new_fragment.is_vertically_aligned_to_top_or_bottom() {
- max(
- new_fragment
- .aligned_inline_metrics(layout_context, &self.minimum_metrics, None)
- .space_needed(),
- self.minimum_metrics.space_needed(),
- )
- } else {
- new_line_metrics.space_needed()
- };
- max(self.bounds.size.block, new_block_size)
- }
-}
-
-int_range_index! {
- #[derive(Serialize)]
- #[doc = "The index of a fragment in a flattened vector of DOM elements."]
- struct FragmentIndex(isize)
-}
-
-/// Arranges fragments into lines, splitting them up as necessary.
-struct LineBreaker {
- /// The floats we need to flow around.
- floats: Floats,
- /// The resulting fragment list for the flow, consisting of possibly-broken fragments.
- new_fragments: Vec<Fragment>,
- /// The next fragment or fragments that we need to work on.
- work_list: VecDeque<Fragment>,
- /// The line we're currently working on.
- pending_line: Line,
- /// The lines we've already committed.
- lines: Vec<Line>,
- /// The index of the last known good line breaking opportunity. The opportunity will either
- /// be inside this fragment (if it is splittable) or immediately prior to it.
- last_known_line_breaking_opportunity: Option<FragmentIndex>,
- /// The current position in the block direction.
- cur_b: Au,
- /// The computed value of the indentation for the first line (`text-indent`, CSS 2.1 § 16.1).
- first_line_indentation: Au,
- /// The minimum metrics for each line, as specified by the line height and font style.
- minimum_metrics: LineMetrics,
-}
-
-impl LineBreaker {
- /// Creates a new `LineBreaker` with a set of floats and the indentation of the first line.
- fn new(
- float_context: Floats,
- first_line_indentation: Au,
- minimum_line_metrics: &LineMetrics,
- ) -> LineBreaker {
- LineBreaker {
- new_fragments: Vec::new(),
- work_list: VecDeque::new(),
- pending_line: Line::new(float_context.writing_mode, minimum_line_metrics),
- floats: float_context,
- lines: Vec::new(),
- cur_b: Au(0),
- last_known_line_breaking_opportunity: None,
- first_line_indentation: first_line_indentation,
- minimum_metrics: *minimum_line_metrics,
- }
- }
-
- /// Resets the `LineBreaker` to the initial state it had after a call to `new`.
- fn reset_scanner(&mut self) {
- self.lines = Vec::new();
- self.new_fragments = Vec::new();
- self.cur_b = Au(0);
- self.reset_line();
- }
-
- /// Reinitializes the pending line to blank data.
- fn reset_line(&mut self) {
- self.last_known_line_breaking_opportunity = None;
- // https://github.com/rust-lang/rust/issues/49282
- self.pending_line = Line::new(self.floats.writing_mode, &self.minimum_metrics);
- }
-
- /// Reflows fragments for the given inline flow.
- fn scan_for_lines(&mut self, flow: &mut InlineFlow, layout_context: &LayoutContext) {
- self.reset_scanner();
-
- // Create our fragment iterator.
- debug!(
- "LineBreaker: scanning for lines, {} fragments",
- flow.fragments.len()
- );
- let mut old_fragments = mem::replace(&mut flow.fragments, InlineFragments::new());
- let old_fragment_iter = old_fragments.fragments.into_iter();
-
- // TODO(pcwalton): This would likely be better as a list of dirty line
- // indices. That way we could resynchronize if we discover during reflow
- // that all subsequent fragments must have the same position as they had
- // in the previous reflow. I don't know how common this case really is
- // in practice, but it's probably worth handling.
- self.lines = Vec::new();
-
- // Do the reflow.
- self.reflow_fragments(old_fragment_iter, flow, layout_context);
-
- // Perform unicode bidirectional layout.
- let para_level = flow.base.writing_mode.to_bidi_level();
-
- // The text within a fragment is at a single bidi embedding level
- // (because we split fragments on level run boundaries during flow
- // construction), so we can build a level array with just one entry per
- // fragment.
- let levels: Vec<bidi::Level> = self
- .new_fragments
- .iter()
- .map(|fragment| match fragment.specific {
- SpecificFragmentInfo::ScannedText(ref info) => info.run.bidi_level,
- _ => para_level,
- })
- .collect();
-
- let mut lines = mem::replace(&mut self.lines, Vec::new());
-
- // If everything is LTR, don't bother with reordering.
- if bidi::level::has_rtl(&levels) {
- // Compute and store the visual ordering of the fragments within the
- // line.
- for line in &mut lines {
- let range = line.range.begin().to_usize()..line.range.end().to_usize();
- // FIXME: Update to use BidiInfo::visual_runs, as this algorithm needs access to
- // the original text and original BidiClass of its characters.
- #[allow(deprecated)]
- let runs = bidi::deprecated::visual_runs(range, &levels);
- line.visual_runs = Some(
- runs.iter()
- .map(|run| {
- let start = FragmentIndex(run.start as isize);
- let len = FragmentIndex(run.len() as isize);
- (Range::new(start, len), levels[run.start])
- })
- .collect(),
- );
- }
- }
-
- // Place the fragments back into the flow.
- old_fragments.fragments = mem::replace(&mut self.new_fragments, vec![]);
- flow.fragments = old_fragments;
- flow.lines = lines;
- }
-
- /// Reflows the given fragments, which have been plucked out of the inline flow.
- fn reflow_fragments<'a, I>(
- &mut self,
- mut old_fragment_iter: I,
- flow: &'a InlineFlow,
- layout_context: &LayoutContext,
- ) where
- I: Iterator<Item = Fragment>,
- {
- loop {
- // Acquire the next fragment to lay out from the work list or fragment list, as
- // appropriate.
- let fragment = match self.next_unbroken_fragment(&mut old_fragment_iter) {
- None => break,
- Some(fragment) => fragment,
- };
-
- // Do not reflow truncated fragments. Reflow the original fragment only.
- let fragment = if fragment.flags.contains(FragmentFlags::IS_ELLIPSIS) {
- continue;
- } else if let SpecificFragmentInfo::TruncatedFragment(info) = fragment.specific {
- info.full
- } else {
- fragment
- };
-
- // Try to append the fragment.
- self.reflow_fragment(fragment, flow, layout_context);
- }
-
- if !self.pending_line_is_empty() {
- debug!(
- "LineBreaker: partially full line {} at end of scanning; committing it",
- self.lines.len()
- );
- self.flush_current_line()
- }
- }
-
- /// Acquires a new fragment to lay out from the work list or fragment list as appropriate.
- /// Note that you probably don't want to call this method directly in order to be incremental-
- /// reflow-safe; try `next_unbroken_fragment` instead.
- fn next_fragment<I>(&mut self, old_fragment_iter: &mut I) -> Option<Fragment>
- where
- I: Iterator<Item = Fragment>,
- {
- self.work_list
- .pop_front()
- .or_else(|| old_fragment_iter.next())
- }
-
- /// Acquires a new fragment to lay out from the work list or fragment list,
- /// merging it with any subsequent fragments as appropriate. In effect, what
- /// this method does is to return the next fragment to lay out, undoing line
- /// break operations that any previous reflows may have performed. You
- /// probably want to be using this method instead of `next_fragment`.
- fn next_unbroken_fragment<I>(&mut self, old_fragment_iter: &mut I) -> Option<Fragment>
- where
- I: Iterator<Item = Fragment>,
- {
- let mut result = self.next_fragment(old_fragment_iter)?;
-
- loop {
- let candidate = match self.next_fragment(old_fragment_iter) {
- None => return Some(result),
- Some(fragment) => fragment,
- };
-
- let need_to_merge = match (&mut result.specific, &candidate.specific) {
- (
- &mut SpecificFragmentInfo::ScannedText(ref mut result_info),
- &SpecificFragmentInfo::ScannedText(ref candidate_info),
- ) => {
- result.margin.inline_end == Au(0) &&
- candidate.margin.inline_start == Au(0) &&
- result.border_padding.inline_end == Au(0) &&
- candidate.border_padding.inline_start == Au(0) &&
- result_info.selected() == candidate_info.selected() &&
- Arc::ptr_eq(&result_info.run, &candidate_info.run) &&
- inline_contexts_are_equal(
- &result.inline_context,
- &candidate.inline_context,
- )
- },
- _ => false,
- };
-
- if need_to_merge {
- result.merge_with(candidate);
- continue;
- }
-
- self.work_list.push_front(candidate);
- return Some(result);
- }
- }
-
- /// Commits a line to the list.
- fn flush_current_line(&mut self) {
- debug!(
- "LineBreaker: flushing line {}: {:?}",
- self.lines.len(),
- self.pending_line
- );
- self.strip_trailing_whitespace_from_pending_line_if_necessary();
- self.lines.push(self.pending_line.clone());
- self.cur_b = self.pending_line.bounds.start.b + self.pending_line.bounds.size.block;
- self.reset_line();
- }
-
- /// Removes trailing whitespace from the pending line if necessary. This is done right before
- /// flushing it.
- fn strip_trailing_whitespace_from_pending_line_if_necessary(&mut self) {
- if self.pending_line.range.is_empty() {
- return;
- }
- let last_fragment_index = self.pending_line.range.end() - FragmentIndex(1);
- let fragment = &mut self.new_fragments[last_fragment_index.get() as usize];
-
- let old_fragment_inline_size = fragment.border_box.size.inline;
-
- fragment.strip_trailing_whitespace_if_necessary();
-
- self.pending_line.bounds.size.inline +=
- fragment.border_box.size.inline - old_fragment_inline_size;
- }
-
- /// Computes the position of a line that has only the provided fragment. Returns the bounding
- /// rect of the line's green zone (whose origin coincides with the line's origin) and the
- /// actual inline-size of the first fragment after splitting.
- fn initial_line_placement(
- &self,
- flow: &InlineFlow,
- first_fragment: &Fragment,
- ceiling: Au,
- ) -> (LogicalRect<Au>, Au) {
- debug!(
- "LineBreaker: trying to place first fragment of line {}; fragment size: {:?}, \
- splittable: {}",
- self.lines.len(),
- first_fragment.border_box.size,
- first_fragment.can_split()
- );
-
- // Initially, pretend a splittable fragment has zero inline-size. We will move it later if
- // it has nonzero inline-size and that causes problems.
- let placement_inline_size = if first_fragment.can_split() {
- first_fragment.minimum_splittable_inline_size()
- } else {
- first_fragment.margin_box_inline_size() + self.indentation_for_pending_fragment()
- };
-
- // Try to place the fragment between floats.
- let line_bounds = self.floats.place_between_floats(&PlacementInfo {
- size: LogicalSize::new(
- self.floats.writing_mode,
- placement_inline_size,
- first_fragment.border_box.size.block,
- ),
- ceiling: ceiling,
- max_inline_size: flow.base.position.size.inline,
- kind: FloatKind::Left,
- });
-
- let fragment_margin_box_inline_size = first_fragment.margin_box_inline_size();
-
- // Simple case: if the fragment fits, then we can stop here.
- if line_bounds.size.inline > fragment_margin_box_inline_size {
- debug!("LineBreaker: fragment fits on line {}", self.lines.len());
- return (line_bounds, fragment_margin_box_inline_size);
- }
-
- // If not, but we can't split the fragment, then we'll place the line here and it will
- // overflow.
- if !first_fragment.can_split() {
- debug!("LineBreaker: line doesn't fit, but is unsplittable");
- }
-
- (line_bounds, fragment_margin_box_inline_size)
- }
-
- /// Performs float collision avoidance. This is called when adding a fragment is going to
- /// increase the block-size, and because of that we will collide with some floats.
- ///
- /// We have two options here:
- /// 1) Move the entire line so that it doesn't collide any more.
- /// 2) Break the line and put the new fragment on the next line.
- ///
- /// The problem with option 1 is that we might move the line and then wind up breaking anyway,
- /// which violates the standard. But option 2 is going to look weird sometimes.
- ///
- /// So we'll try to move the line whenever we can, but break if we have to.
- ///
- /// Returns false if and only if we should break the line.
- fn avoid_floats(
- &mut self,
- flow: &InlineFlow,
- in_fragment: Fragment,
- new_block_size: Au,
- ) -> bool {
- debug!("LineBreaker: entering float collision avoider!");
-
- // First predict where the next line is going to be.
- let (next_line, first_fragment_inline_size) =
- self.initial_line_placement(flow, &in_fragment, self.pending_line.bounds.start.b);
- let next_green_zone = next_line.size;
-
- let new_inline_size = self.pending_line.bounds.size.inline + first_fragment_inline_size;
-
- // Now, see if everything can fit at the new location.
- if next_green_zone.inline >= new_inline_size && next_green_zone.block >= new_block_size {
- debug!(
- "LineBreaker: case=adding fragment collides vertically with floats: moving \
- line"
- );
-
- self.pending_line.bounds.start = next_line.start;
- self.pending_line.green_zone = next_green_zone;
-
- debug_assert!(
- !self.pending_line_is_empty(),
- "Non-terminating line breaking"
- );
- self.work_list.push_front(in_fragment);
- return true;
- }
-
- debug!("LineBreaker: case=adding fragment collides vertically with floats: breaking line");
- self.work_list.push_front(in_fragment);
- false
- }
-
- /// Tries to append the given fragment to the line, splitting it if necessary. Commits the
- /// current line if needed.
- fn reflow_fragment(
- &mut self,
- mut fragment: Fragment,
- flow: &InlineFlow,
- layout_context: &LayoutContext,
- ) {
- // Undo any whitespace stripping from previous reflows.
- fragment.reset_text_range_and_inline_size();
- // Determine initial placement for the fragment if we need to.
- //
- // Also, determine whether we can legally break the line before, or
- // inside, this fragment.
- let fragment_is_line_break_opportunity = if self.pending_line_is_empty() {
- fragment.strip_leading_whitespace_if_necessary();
- let (line_bounds, _) = self.initial_line_placement(flow, &fragment, self.cur_b);
- self.pending_line.bounds.start = line_bounds.start;
- self.pending_line.green_zone = line_bounds.size;
- false
- } else {
- // In case of Foo<span style="...">bar</span>, the line breaker will
- // set the "suppress line break before" flag for the second fragment.
- //
- // In case of Foo<span>bar</span> the second fragment ("bar") will
- // start _within_ a glyph run, so we also avoid breaking there
- //
- // is_on_glyph_run_boundary does a binary search, but this is ok
- // because the result will be cached and reused in
- // `calculate_split_position` later
- if fragment.suppress_line_break_before() || !fragment.is_on_glyph_run_boundary() {
- false
- } else {
- fragment.white_space().allow_wrap()
- }
- };
-
- debug!(
- "LineBreaker: trying to append to line {} \
- (fragment size: {:?}, green zone: {:?}): {:?}",
- self.lines.len(),
- fragment.border_box.size,
- self.pending_line.green_zone,
- fragment
- );
-
- // NB: At this point, if `green_zone.inline <
- // self.pending_line.bounds.size.inline` or `green_zone.block <
- // self.pending_line.bounds.size.block`, then we committed a line that
- // overlaps with floats.
- let green_zone = self.pending_line.green_zone;
- let new_line_metrics = self
- .pending_line
- .new_metrics_for_fragment(&fragment, layout_context);
- let new_block_size = self.pending_line.new_block_size_for_fragment(
- &fragment,
- &new_line_metrics,
- layout_context,
- );
- if new_block_size > green_zone.block {
- // Uh-oh. Float collision imminent. Enter the float collision avoider!
- if !self.avoid_floats(flow, fragment, new_block_size) {
- self.flush_current_line();
- }
- return;
- }
-
- // Record the last known good line break opportunity if this is one.
- if fragment_is_line_break_opportunity {
- self.last_known_line_breaking_opportunity = Some(self.pending_line.range.end())
- }
-
- // If we must flush the line after finishing this fragment due to `white-space: pre`,
- // detect that.
- let line_flush_mode = if fragment.white_space().preserve_newlines() {
- if fragment.requires_line_break_afterward_if_wrapping_on_newlines() {
- LineFlushMode::Flush
- } else {
- LineFlushMode::No
- }
- } else {
- LineFlushMode::No
- };
-
- // If we're not going to overflow the green zone vertically, we might still do so
- // horizontally. We'll try to place the whole fragment on this line and break somewhere if
- // it doesn't fit.
- let indentation = self.indentation_for_pending_fragment();
- let new_inline_size =
- self.pending_line.bounds.size.inline + fragment.margin_box_inline_size() + indentation;
- if new_inline_size <= green_zone.inline {
- debug!("LineBreaker: fragment fits without splitting");
- self.push_fragment_to_line(layout_context, fragment, line_flush_mode);
- return;
- }
-
- // If the wrapping mode prevents us from splitting, then back up and split at the last
- // known good split point.
- if !fragment.white_space().allow_wrap() {
- debug!(
- "LineBreaker: fragment can't split; falling back to last known good split point"
- );
- self.split_line_at_last_known_good_position(layout_context, fragment, line_flush_mode);
- return;
- }
-
- // Split it up!
- let available_inline_size =
- green_zone.inline - self.pending_line.bounds.size.inline - indentation;
- let inline_start_fragment;
- let inline_end_fragment;
- let split_result = match fragment
- .calculate_split_position(available_inline_size, self.pending_line_is_empty())
- {
- None => {
- // We failed to split. Defer to the next line if we're allowed to; otherwise,
- // rewind to the last line breaking opportunity.
- if fragment_is_line_break_opportunity {
- debug!("LineBreaker: fragment was unsplittable; deferring to next line");
- self.work_list.push_front(fragment);
- self.flush_current_line();
- } else {
- self.split_line_at_last_known_good_position(
- layout_context,
- fragment,
- LineFlushMode::No,
- );
- }
- return;
- },
- Some(split_result) => split_result,
- };
-
- inline_start_fragment = split_result
- .inline_start
- .as_ref()
- .map(|x| fragment.transform_with_split_info(x, split_result.text_run.clone(), true));
- inline_end_fragment = split_result
- .inline_end
- .as_ref()
- .map(|x| fragment.transform_with_split_info(x, split_result.text_run.clone(), false));
-
- // Push the first fragment onto the line we're working on and start off the next line with
- // the second fragment. If there's no second fragment, the next line will start off empty.
- match (inline_start_fragment, inline_end_fragment) {
- (Some(mut inline_start_fragment), Some(mut inline_end_fragment)) => {
- inline_start_fragment.border_padding.inline_end = Au(0);
- if let Some(ref mut inline_context) = inline_start_fragment.inline_context {
- for node in &mut inline_context.nodes {
- node.flags
- .remove(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT);
- }
- }
- inline_start_fragment.border_box.size.inline +=
- inline_start_fragment.border_padding.inline_start;
-
- inline_end_fragment.border_padding.inline_start = Au(0);
- if let Some(ref mut inline_context) = inline_end_fragment.inline_context {
- for node in &mut inline_context.nodes {
- node.flags
- .remove(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT);
- }
- }
- inline_end_fragment.border_box.size.inline +=
- inline_end_fragment.border_padding.inline_end;
-
- self.push_fragment_to_line(
- layout_context,
- inline_start_fragment,
- LineFlushMode::Flush,
- );
- self.work_list.push_front(inline_end_fragment)
- },
- (Some(fragment), None) => {
- self.push_fragment_to_line(layout_context, fragment, line_flush_mode);
- },
- (None, Some(fragment)) => {
- // Yes, this can happen!
- self.flush_current_line();
- self.work_list.push_front(fragment)
- },
- (None, None) => {},
- }
- }
-
- /// Pushes a fragment to the current line unconditionally, possibly truncating it and placing
- /// an ellipsis based on the value of `text-overflow`. If `flush_line` is `Flush`, then flushes
- /// the line afterward;
- fn push_fragment_to_line(
- &mut self,
- layout_context: &LayoutContext,
- fragment: Fragment,
- line_flush_mode: LineFlushMode,
- ) {
- let indentation = self.indentation_for_pending_fragment();
- if self.pending_line_is_empty() {
- debug_assert!(self.new_fragments.len() <= (isize::MAX as usize));
- self.pending_line.range.reset(
- FragmentIndex(self.new_fragments.len() as isize),
- FragmentIndex(0),
- );
- }
-
- // Determine if an ellipsis will be necessary to account for `text-overflow`.
- let available_inline_size = self.pending_line.green_zone.inline -
- self.pending_line.bounds.size.inline -
- indentation;
-
- let ellipsis = match (
- &fragment.style().get_text().text_overflow.second,
- fragment.style().get_box().overflow_x,
- ) {
- (&TextOverflowSide::Clip, _) | (_, StyleOverflow::Visible) => None,
- (&TextOverflowSide::Ellipsis, _) => {
- if fragment.margin_box_inline_size() > available_inline_size {
- Some("…".to_string())
- } else {
- None
- }
- },
- (&TextOverflowSide::String(ref string), _) => {
- if fragment.margin_box_inline_size() > available_inline_size {
- Some(string.to_string())
- } else {
- None
- }
- },
- };
-
- if let Some(string) = ellipsis {
- let ellipsis = fragment.transform_into_ellipsis(layout_context, string);
- let truncated = fragment
- .truncate_to_inline_size(available_inline_size - ellipsis.margin_box_inline_size());
- self.push_fragment_to_line_ignoring_text_overflow(truncated, layout_context);
- self.push_fragment_to_line_ignoring_text_overflow(ellipsis, layout_context);
- } else {
- self.push_fragment_to_line_ignoring_text_overflow(fragment, layout_context);
- }
-
- if line_flush_mode == LineFlushMode::Flush {
- self.flush_current_line()
- }
- }
-
- /// Pushes a fragment to the current line unconditionally, without placing an ellipsis in the
- /// case of `text-overflow: ellipsis`.
- fn push_fragment_to_line_ignoring_text_overflow(
- &mut self,
- fragment: Fragment,
- layout_context: &LayoutContext,
- ) {
- let indentation = self.indentation_for_pending_fragment();
- self.pending_line.range.extend_by(FragmentIndex(1));
-
- if !fragment.is_inline_absolute() && !fragment.is_hypothetical() {
- self.pending_line.bounds.size.inline = self.pending_line.bounds.size.inline +
- fragment.margin_box_inline_size() +
- indentation;
- self.pending_line.metrics = self
- .pending_line
- .new_metrics_for_fragment(&fragment, layout_context);
- self.pending_line.bounds.size.block = self.pending_line.new_block_size_for_fragment(
- &fragment,
- &self.pending_line.metrics,
- layout_context,
- );
- }
-
- self.new_fragments.push(fragment);
- }
-
- fn split_line_at_last_known_good_position(
- &mut self,
- layout_context: &LayoutContext,
- cur_fragment: Fragment,
- line_flush_mode: LineFlushMode,
- ) {
- let last_known_line_breaking_opportunity = match self.last_known_line_breaking_opportunity {
- None => {
- // No line breaking opportunity exists at all for this line. Overflow.
- self.push_fragment_to_line(layout_context, cur_fragment, line_flush_mode);
- return;
- },
- Some(last_known_line_breaking_opportunity) => last_known_line_breaking_opportunity,
- };
-
- self.work_list.push_front(cur_fragment);
- for fragment_index in
- (last_known_line_breaking_opportunity.get()..self.pending_line.range.end().get()).rev()
- {
- debug_assert_eq!(fragment_index, (self.new_fragments.len() as isize) - 1);
- self.work_list.push_front(self.new_fragments.pop().unwrap());
- }
-
- // FIXME(pcwalton): This should actually attempt to split the last fragment if
- // possible to do so, to handle cases like:
- //
- // (available width)
- // +-------------+
- // The alphabet
- // (<em>abcdefghijklmnopqrstuvwxyz</em>)
- //
- // Here, the last known-good split point is inside the fragment containing
- // "The alphabet (", which has already been committed by the time we get to this
- // point. Unfortunately, the existing splitting API (`calculate_split_position`)
- // has no concept of "split right before the last non-whitespace position". We'll
- // need to add that feature to the API to handle this case correctly.
- self.pending_line
- .range
- .extend_to(last_known_line_breaking_opportunity);
- self.flush_current_line();
- }
-
- /// Returns the indentation that needs to be applied before the fragment we're reflowing.
- fn indentation_for_pending_fragment(&self) -> Au {
- if self.pending_line_is_empty() && self.lines.is_empty() {
- self.first_line_indentation
- } else {
- Au(0)
- }
- }
-
- /// Returns true if the pending line is empty and false otherwise.
- fn pending_line_is_empty(&self) -> bool {
- self.pending_line.range.length() == FragmentIndex(0)
- }
-}
-
-/// Represents a list of inline fragments, including element ranges.
-#[derive(Clone, Serialize)]
-pub struct InlineFragments {
- /// The fragments themselves.
- pub fragments: Vec<Fragment>,
-}
-
-impl InlineFragments {
- /// Creates an empty set of inline fragments.
- pub fn new() -> InlineFragments {
- InlineFragments { fragments: vec![] }
- }
-
- /// Returns the number of inline fragments.
- pub fn len(&self) -> usize {
- self.fragments.len()
- }
-
- /// Returns true if this list contains no fragments and false if it contains at least one
- /// fragment.
- pub fn is_empty(&self) -> bool {
- self.fragments.is_empty()
- }
-
- /// A convenience function to return the fragment at a given index.
- pub fn get(&self, index: usize) -> &Fragment {
- &self.fragments[index]
- }
-
- /// A convenience function to return a mutable reference to the fragment at a given index.
- pub fn get_mut(&mut self, index: usize) -> &mut Fragment {
- &mut self.fragments[index]
- }
-}
-
-#[allow(unsafe_code)]
-unsafe impl crate::flow::HasBaseFlow for InlineFlow {}
-
-/// Flows for inline layout.
-#[derive(Serialize)]
-#[repr(C)]
-pub struct InlineFlow {
- /// Data common to all flows.
- pub base: BaseFlow,
-
- /// A vector of all inline fragments. Several fragments may correspond to one node/element.
- pub fragments: InlineFragments,
-
- /// A vector of ranges into fragments that represents line positions. These ranges are disjoint
- /// and are the result of inline layout. This also includes some metadata used for positioning
- /// lines.
- pub lines: Vec<Line>,
-
- /// The minimum metrics for each line, as specified by the line height and font style.
- pub minimum_line_metrics: LineMetrics,
-
- /// The amount of indentation to use on the first line. This is determined by our block parent
- /// (because percentages are relative to the containing block, and we aren't in a position to
- /// compute things relative to our parent's containing block).
- pub first_line_indentation: Au,
-}
-
-impl InlineFlow {
- pub fn from_fragments(fragments: InlineFragments, writing_mode: WritingMode) -> InlineFlow {
- let mut flow = InlineFlow {
- base: BaseFlow::new(None, writing_mode, ForceNonfloatedFlag::ForceNonfloated),
- fragments: fragments,
- lines: Vec::new(),
- minimum_line_metrics: LineMetrics::new(Au(0), Au(0)),
- first_line_indentation: Au(0),
- };
-
- if flow
- .fragments
- .fragments
- .iter()
- .any(Fragment::is_unscanned_generated_content)
- {
- flow.base
- .restyle_damage
- .insert(ServoRestyleDamage::RESOLVE_GENERATED_CONTENT);
- }
-
- flow
- }
-
- /// Sets fragment positions in the inline direction based on alignment for one line. This
- /// performs text justification if mandated by the style.
- fn set_inline_fragment_positions(
- fragments: &mut InlineFragments,
- line: &Line,
- line_align: TextAlign,
- indentation: Au,
- is_last_line: bool,
- ) {
- // Figure out how much inline-size we have.
- let slack_inline_size = max(Au(0), line.green_zone.inline - line.bounds.size.inline);
-
- // Compute the value we're going to use for `text-justify`.
- if fragments.fragments.is_empty() {
- return;
- }
- let text_justify = fragments.fragments[0]
- .style()
- .get_inherited_text()
- .text_justify;
-
- // Translate `left` and `right` to logical directions.
- let is_ltr = fragments.fragments[0].style().writing_mode.is_bidi_ltr();
- let line_align = match (line_align, is_ltr) {
- (TextAlign::Left, true) |
- (TextAlign::ServoLeft, true) |
- (TextAlign::Right, false) |
- (TextAlign::ServoRight, false) => TextAlign::Start,
- (TextAlign::Left, false) |
- (TextAlign::ServoLeft, false) |
- (TextAlign::Right, true) |
- (TextAlign::ServoRight, true) => TextAlign::End,
- _ => line_align,
- };
-
- // Set the fragment inline positions based on that alignment, and justify the text if
- // necessary.
- let mut inline_start_position_for_fragment = line.bounds.start.i + indentation;
- match line_align {
- TextAlign::Justify if !is_last_line && text_justify != TextJustify::None => {
- InlineFlow::justify_inline_fragments(fragments, line, slack_inline_size)
- },
- TextAlign::Justify | TextAlign::Start => {},
- TextAlign::Center | TextAlign::ServoCenter => {
- inline_start_position_for_fragment =
- inline_start_position_for_fragment + slack_inline_size.scale_by(0.5)
- },
- TextAlign::End => {
- inline_start_position_for_fragment =
- inline_start_position_for_fragment + slack_inline_size
- },
- TextAlign::Left | TextAlign::ServoLeft | TextAlign::Right | TextAlign::ServoRight => {
- unreachable!()
- },
- }
-
- // Lay out the fragments in visual order.
- let run_count = match line.visual_runs {
- Some(ref runs) => runs.len(),
- None => 1,
- };
- for run_idx in 0..run_count {
- let (range, level) = match line.visual_runs {
- Some(ref runs) if is_ltr => runs[run_idx],
- Some(ref runs) => runs[run_count - run_idx - 1], // reverse order for RTL runs
- None => (line.range, bidi::Level::ltr()),
- };
-
- struct MaybeReverse<I> {
- iter: I,
- reverse: bool,
- }
-
- impl<I: DoubleEndedIterator> Iterator for MaybeReverse<I> {
- type Item = I::Item;
-
- fn next(&mut self) -> Option<I::Item> {
- if self.reverse {
- self.iter.next_back()
- } else {
- self.iter.next()
- }
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
- }
- }
-
- // If the bidi embedding direction is opposite the layout direction, lay out this
- // run in reverse order.
- let fragment_indices = MaybeReverse {
- iter: range.begin().get()..range.end().get(),
- reverse: level.is_ltr() != is_ltr,
- };
-
- for fragment_index in fragment_indices {
- let fragment = fragments.get_mut(fragment_index as usize);
- inline_start_position_for_fragment =
- inline_start_position_for_fragment + fragment.margin.inline_start;
-
- let border_start = if fragment.style.writing_mode.is_bidi_ltr() == is_ltr {
- inline_start_position_for_fragment
- } else {
- line.green_zone.inline -
- inline_start_position_for_fragment -
- fragment.margin.inline_end -
- fragment.border_box.size.inline
- };
- fragment.border_box = LogicalRect::new(
- fragment.style.writing_mode,
- border_start,
- fragment.border_box.start.b,
- fragment.border_box.size.inline,
- fragment.border_box.size.block,
- );
- fragment.update_late_computed_inline_position_if_necessary();
-
- if !fragment.is_inline_absolute() {
- inline_start_position_for_fragment = inline_start_position_for_fragment +
- fragment.border_box.size.inline +
- fragment.margin.inline_end;
- }
- }
- }
- }
-
- /// Justifies the given set of inline fragments, distributing the `slack_inline_size` among all
- /// of them according to the value of `text-justify`.
- fn justify_inline_fragments(
- fragments: &mut InlineFragments,
- line: &Line,
- slack_inline_size: Au,
- ) {
- // Fast path.
- if slack_inline_size == Au(0) {
- return;
- }
-
- // First, calculate the number of expansion opportunities (spaces, normally).
- let mut expansion_opportunities = 0;
- for fragment_index in line.range.each_index() {
- let fragment = fragments.get(fragment_index.to_usize());
- let scanned_text_fragment_info = match fragment.specific {
- SpecificFragmentInfo::ScannedText(ref info) if !info.range.is_empty() => info,
- _ => continue,
- };
- let fragment_range = scanned_text_fragment_info.range;
-
- for slice in scanned_text_fragment_info
- .run
- .character_slices_in_range(&fragment_range)
- {
- expansion_opportunities += slice.glyphs.space_count_in_range(&slice.range)
- }
- }
-
- if expansion_opportunities == 0 {
- return;
- }
-
- // Then distribute all the space across the expansion opportunities.
- let space_per_expansion_opportunity = slack_inline_size / expansion_opportunities as i32;
- for fragment_index in line.range.each_index() {
- let fragment = fragments.get_mut(fragment_index.to_usize());
- let scanned_text_fragment_info = match fragment.specific {
- SpecificFragmentInfo::ScannedText(ref mut info) if !info.range.is_empty() => info,
- _ => continue,
- };
- let fragment_range = scanned_text_fragment_info.range;
- let run = Arc::make_mut(&mut scanned_text_fragment_info.run);
- run.extra_word_spacing = space_per_expansion_opportunity;
-
- // Recompute the fragment's border box size.
- let new_inline_size = run.advance_for_range(&fragment_range);
- let new_size = LogicalSize::new(
- fragment.style.writing_mode,
- new_inline_size,
- fragment.border_box.size.block,
- );
- fragment.border_box = LogicalRect::from_point_size(
- fragment.style.writing_mode,
- fragment.border_box.start,
- new_size,
- );
- }
- }
-
- /// Sets final fragment positions in the block direction for one line.
- fn set_block_fragment_positions(
- fragments: &mut InlineFragments,
- line: &Line,
- minimum_line_metrics: &LineMetrics,
- layout_context: &LayoutContext,
- ) {
- for fragment_index in line.range.each_index() {
- let fragment = fragments.get_mut(fragment_index.to_usize());
- let line_metrics = LineMetrics::for_line_and_fragment(line, fragment, layout_context);
- let inline_metrics = fragment.aligned_inline_metrics(
- layout_context,
- minimum_line_metrics,
- Some(&line_metrics),
- );
-
- // Align the top of the fragment's border box with its ascent above the baseline.
- fragment.border_box.start.b =
- line.bounds.start.b + line_metrics.space_above_baseline - inline_metrics.ascent;
-
- // CSS 2.1 § 10.8: "The height of each inline-level box in the line box is
- // calculated. For replaced elements, inline-block elements, and inline-table
- // elements, this is the height of their margin box; for inline boxes, this is their
- // 'line-height'."
- //
- // CSS 2.1 § 10.8.1: "Although margins, borders, and padding of non-replaced elements
- // do not enter into the line box calculation, they are still rendered around inline
- // boxes."
- //
- // Effectively, if the fragment is a non-replaced element (excluding inline-block), we
- // need to align its ascent above the baseline with the top of the *content box*, not
- // the border box. Since the code above has already aligned it to the border box, we
- // simply need to adjust it in this case.
- if !fragment.is_replaced_or_inline_block() {
- fragment.border_box.start.b -= fragment.border_padding.block_start
- }
-
- fragment.update_late_computed_block_position_if_necessary();
- }
- }
-
- /// Computes the minimum metrics for each line. This is done during flow construction.
- ///
- /// `style` is the style of the block.
- pub fn minimum_line_metrics(
- &self,
- font_context: &mut LayoutFontContext,
- style: &ComputedValues,
- ) -> LineMetrics {
- InlineFlow::minimum_line_metrics_for_fragments(
- &self.fragments.fragments,
- font_context,
- style,
- )
- }
-
- /// Computes the minimum line metrics for the given fragments. This is typically done during
- /// flow construction.
- ///
- /// `style` is the style of the block that these fragments belong to.
- pub fn minimum_line_metrics_for_fragments(
- fragments: &[Fragment],
- font_context: &mut LayoutFontContext,
- style: &ComputedValues,
- ) -> LineMetrics {
- // As a special case, if this flow contains only hypothetical fragments, then the entire
- // flow is hypothetical and takes up no space. See CSS 2.1 § 10.3.7.
- if fragments.iter().all(Fragment::is_hypothetical) {
- return LineMetrics::new(Au(0), Au(0));
- }
-
- let font_style = style.clone_font();
- let font_metrics = text::font_metrics_for_style(font_context, font_style);
- let line_height = text::line_height_from_style(style, &font_metrics);
- let inline_metrics = if fragments.iter().any(Fragment::is_text_or_replaced) {
- InlineMetrics::from_font_metrics(&font_metrics, line_height)
- } else {
- InlineMetrics::new(Au(0), Au(0), Au(0))
- };
-
- let mut line_metrics = LineMetrics::new(Au(0), MIN_AU);
- let mut largest_block_size_for_top_fragments = Au(0);
- let mut largest_block_size_for_bottom_fragments = Au(0);
-
- // We use `VerticalAlign::baseline()` here because `vertical-align` must
- // not apply to the inside of inline blocks.
- update_line_metrics_for_fragment(
- &mut line_metrics,
- &inline_metrics,
- style.get_box().display,
- VerticalAlign::baseline(),
- &mut largest_block_size_for_top_fragments,
- &mut largest_block_size_for_bottom_fragments,
- );
-
- // According to CSS 2.1 § 10.8, `line-height` of any inline element specifies the minimal
- // height of line boxes within the element.
- for inline_context in fragments
- .iter()
- .filter_map(|fragment| fragment.inline_context.as_ref())
- {
- for node in &inline_context.nodes {
- let font_style = node.style.clone_font();
- let font_metrics = text::font_metrics_for_style(font_context, font_style);
- let line_height = text::line_height_from_style(&*node.style, &font_metrics);
- let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height);
-
- update_line_metrics_for_fragment(
- &mut line_metrics,
- &inline_metrics,
- node.style.get_box().display,
- node.style.get_box().vertical_align,
- &mut largest_block_size_for_top_fragments,
- &mut largest_block_size_for_bottom_fragments,
- );
- }
- }
-
- line_metrics.space_above_baseline = max(
- line_metrics.space_above_baseline,
- largest_block_size_for_bottom_fragments - max(line_metrics.space_below_baseline, Au(0)),
- );
- line_metrics.space_below_baseline = max(
- line_metrics.space_below_baseline,
- largest_block_size_for_top_fragments - line_metrics.space_above_baseline,
- );
-
- return line_metrics;
-
- fn update_line_metrics_for_fragment(
- line_metrics: &mut LineMetrics,
- inline_metrics: &InlineMetrics,
- display_value: Display,
- vertical_align_value: VerticalAlign,
- largest_block_size_for_top_fragments: &mut Au,
- largest_block_size_for_bottom_fragments: &mut Au,
- ) {
- // FIXME(emilio): This should probably be handled.
- let vertical_align_value = match vertical_align_value {
- VerticalAlign::Keyword(kw) => kw,
- VerticalAlign::Length(..) => {
- *line_metrics = line_metrics.new_metrics_for_fragment(inline_metrics);
- return;
- },
- };
-
- match (display_value, vertical_align_value) {
- (Display::Inline, VerticalAlignKeyword::Top) |
- (Display::Block, VerticalAlignKeyword::Top) |
- (Display::InlineFlex, VerticalAlignKeyword::Top) |
- (Display::InlineBlock, VerticalAlignKeyword::Top)
- if inline_metrics.space_above_baseline >= Au(0) =>
- {
- *largest_block_size_for_top_fragments = max(
- *largest_block_size_for_top_fragments,
- inline_metrics.space_above_baseline + inline_metrics.space_below_baseline,
- )
- },
- (Display::Inline, VerticalAlignKeyword::Bottom) |
- (Display::Block, VerticalAlignKeyword::Bottom) |
- (Display::InlineFlex, VerticalAlignKeyword::Bottom) |
- (Display::InlineBlock, VerticalAlignKeyword::Bottom)
- if inline_metrics.space_below_baseline >= Au(0) =>
- {
- *largest_block_size_for_bottom_fragments = max(
- *largest_block_size_for_bottom_fragments,
- inline_metrics.space_above_baseline + inline_metrics.space_below_baseline,
- )
- },
- _ => *line_metrics = line_metrics.new_metrics_for_fragment(inline_metrics),
- }
- }
- }
-
- fn update_restyle_damage(&mut self) {
- let mut damage = self.base.restyle_damage;
-
- for frag in &self.fragments.fragments {
- damage.insert(frag.restyle_damage());
- }
-
- self.base.restyle_damage = damage;
- }
-
- fn containing_block_range_for_flow_surrounding_fragment_at_index(
- &self,
- fragment_index: FragmentIndex,
- ) -> Range<FragmentIndex> {
- let mut start_index = fragment_index;
- while start_index > FragmentIndex(0) &&
- self.fragments.fragments[(start_index - FragmentIndex(1)).get() as usize]
- .is_positioned()
- {
- start_index = start_index - FragmentIndex(1)
- }
-
- let mut end_index = fragment_index + FragmentIndex(1);
- while end_index < FragmentIndex(self.fragments.fragments.len() as isize) &&
- self.fragments.fragments[end_index.get() as usize].is_positioned()
- {
- end_index = end_index + FragmentIndex(1)
- }
-
- Range::new(start_index, end_index - start_index)
- }
-
- fn containing_block_range_for_flow(&self, opaque_flow: OpaqueFlow) -> Range<FragmentIndex> {
- match self
- .fragments
- .fragments
- .iter()
- .position(|fragment| match fragment.specific {
- SpecificFragmentInfo::InlineAbsolute(ref inline_absolute) => {
- OpaqueFlow::from_flow(&*inline_absolute.flow_ref) == opaque_flow
- },
- SpecificFragmentInfo::InlineAbsoluteHypothetical(
- ref inline_absolute_hypothetical,
- ) => OpaqueFlow::from_flow(&*inline_absolute_hypothetical.flow_ref) == opaque_flow,
- _ => false,
- }) {
- Some(index) => {
- let index = FragmentIndex(index as isize);
- self.containing_block_range_for_flow_surrounding_fragment_at_index(index)
- },
- None => {
- // FIXME(pcwalton): This is quite wrong. We should only return the range
- // surrounding the inline fragments that constitute the containing block. But this
- // suffices to get Google looking right.
- Range::new(
- FragmentIndex(0),
- FragmentIndex(self.fragments.fragments.len() as isize),
- )
- },
- }
- }
-
- pub fn baseline_offset_of_last_line(&self) -> Option<Au> {
- self.last_line_containing_real_fragments().map(|line| {
- line.bounds.start.b + line.bounds.size.block - line.metrics.space_below_baseline
- })
- }
-
- // Returns the last line that doesn't consist entirely of hypothetical boxes.
- fn last_line_containing_real_fragments(&self) -> Option<&Line> {
- for line in self.lines.iter().rev() {
- if (line.range.begin().get()..line.range.end().get())
- .any(|index| !self.fragments.fragments[index as usize].is_hypothetical())
- {
- return Some(line);
- }
- }
- None
- }
-
- fn build_display_list_for_inline_fragment_at_index(
- &mut self,
- state: &mut DisplayListBuildState,
- index: usize,
- ) {
- let fragment = self.fragments.fragments.get_mut(index).unwrap();
- let stacking_relative_border_box = self
- .base
- .stacking_relative_border_box_for_display_list(fragment);
- fragment.build_display_list(
- state,
- stacking_relative_border_box,
- BorderPaintingMode::Separate,
- DisplayListSection::Content,
- self.base.clip,
- None,
- );
- }
-}
-
-impl Flow for InlineFlow {
- fn class(&self) -> FlowClass {
- FlowClass::Inline
- }
-
- fn as_inline(&self) -> &InlineFlow {
- self
- }
-
- fn as_mut_inline(&mut self) -> &mut InlineFlow {
- self
- }
-
- fn bubble_inline_sizes(&mut self) {
- self.update_restyle_damage();
-
- let _scope = layout_debug_scope!("inline::bubble_inline_sizes {:x}", self.base.debug_id());
-
- let writing_mode = self.base.writing_mode;
- for kid in self.base.child_iter_mut() {
- kid.mut_base().floats = Floats::new(writing_mode);
- }
-
- self.base
- .flags
- .remove(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
-
- let mut intrinsic_sizes_for_flow = IntrinsicISizesContribution::new();
- let mut intrinsic_sizes_for_inline_run = IntrinsicISizesContribution::new();
- let mut intrinsic_sizes_for_nonbroken_run = IntrinsicISizesContribution::new();
- for fragment in &mut self.fragments.fragments {
- let intrinsic_sizes_for_fragment = fragment.compute_intrinsic_inline_sizes().finish();
- match fragment.style.get_inherited_text().white_space {
- WhiteSpace::Nowrap => intrinsic_sizes_for_nonbroken_run
- .union_nonbreaking_inline(&intrinsic_sizes_for_fragment),
- WhiteSpace::Pre => {
- intrinsic_sizes_for_nonbroken_run
- .union_nonbreaking_inline(&intrinsic_sizes_for_fragment);
-
- // Flush the intrinsic sizes we've been gathering up in order to handle the
- // line break, if necessary.
- if fragment.requires_line_break_afterward_if_wrapping_on_newlines() {
- intrinsic_sizes_for_inline_run
- .union_inline(&intrinsic_sizes_for_nonbroken_run.finish());
- intrinsic_sizes_for_nonbroken_run = IntrinsicISizesContribution::new();
- intrinsic_sizes_for_flow
- .union_block(&intrinsic_sizes_for_inline_run.finish());
- intrinsic_sizes_for_inline_run = IntrinsicISizesContribution::new();
- }
- },
- WhiteSpace::PreWrap | WhiteSpace::PreLine => {
- // Flush the intrinsic sizes we were gathering up for the nonbroken run, if
- // necessary.
- intrinsic_sizes_for_inline_run
- .union_inline(&intrinsic_sizes_for_nonbroken_run.finish());
- intrinsic_sizes_for_nonbroken_run = IntrinsicISizesContribution::new();
-
- intrinsic_sizes_for_nonbroken_run.union_inline(&intrinsic_sizes_for_fragment);
-
- // Flush the intrinsic sizes we've been gathering up in order to handle the
- // line break, if necessary.
- if fragment.requires_line_break_afterward_if_wrapping_on_newlines() {
- intrinsic_sizes_for_inline_run
- .union_inline(&intrinsic_sizes_for_nonbroken_run.finish());
- intrinsic_sizes_for_nonbroken_run = IntrinsicISizesContribution::new();
- intrinsic_sizes_for_flow
- .union_block(&intrinsic_sizes_for_inline_run.finish());
- intrinsic_sizes_for_inline_run = IntrinsicISizesContribution::new();
- }
- },
- WhiteSpace::Normal => {
- // Flush the intrinsic sizes we were gathering up for the nonbroken run, if
- // necessary.
- intrinsic_sizes_for_inline_run
- .union_inline(&intrinsic_sizes_for_nonbroken_run.finish());
- intrinsic_sizes_for_nonbroken_run = IntrinsicISizesContribution::new();
-
- intrinsic_sizes_for_nonbroken_run.union_inline(&intrinsic_sizes_for_fragment);
- },
- }
-
- fragment
- .restyle_damage
- .remove(ServoRestyleDamage::BUBBLE_ISIZES);
-
- if fragment.is_text_or_replaced() {
- self.base
- .flags
- .insert(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
- }
- }
-
- // Flush any remaining nonbroken-run and inline-run intrinsic sizes.
- intrinsic_sizes_for_inline_run.union_inline(&intrinsic_sizes_for_nonbroken_run.finish());
- intrinsic_sizes_for_flow.union_block(&intrinsic_sizes_for_inline_run.finish());
-
- // Finish up the computation.
- self.base.intrinsic_inline_sizes = intrinsic_sizes_for_flow.finish()
- }
-
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
- /// When called on this context, the context has had its inline-size set by the parent context.
- fn assign_inline_sizes(&mut self, _: &LayoutContext) {
- let _scope = layout_debug_scope!("inline::assign_inline_sizes {:x}", self.base.debug_id());
-
- // Initialize content fragment inline-sizes if they haven't been initialized already.
- //
- // TODO: Combine this with `LineBreaker`'s walk in the fragment list, or put this into
- // `Fragment`.
-
- debug!(
- "InlineFlow::assign_inline_sizes: floats in: {:?}",
- self.base.floats
- );
-
- let inline_size = self.base.block_container_inline_size;
- let container_mode = self.base.block_container_writing_mode;
- let container_block_size = self.base.block_container_explicit_block_size;
- self.base.position.size.inline = inline_size;
-
- {
- let this = &mut *self;
- for fragment in this.fragments.fragments.iter_mut() {
- fragment.compute_border_and_padding(inline_size);
- fragment.compute_block_direction_margins(inline_size);
- fragment.compute_inline_direction_margins(inline_size);
- fragment
- .assign_replaced_inline_size_if_necessary(inline_size, container_block_size);
- }
- }
-
- // If there are any inline-block kids, propagate explicit block and inline
- // sizes down to them.
- let block_container_explicit_block_size = self.base.block_container_explicit_block_size;
- for kid in self.base.child_iter_mut() {
- let kid_base = kid.mut_base();
-
- kid_base.block_container_inline_size = inline_size;
- kid_base.block_container_writing_mode = container_mode;
- kid_base.block_container_explicit_block_size = block_container_explicit_block_size;
- }
- }
-
- /// Calculate and set the block-size of this flow. See CSS 2.1 § 10.6.1.
- /// Note that we do not need to do in-order traversal because the children
- /// are always block formatting context.
- fn assign_block_size(&mut self, layout_context: &LayoutContext) {
- let _scope = layout_debug_scope!("inline::assign_block_size {:x}", self.base.debug_id());
-
- // Divide the fragments into lines.
- //
- // TODO(pcwalton, #226): Get the CSS `line-height` property from the
- // style of the containing block to determine the minimum line block
- // size.
- //
- // TODO(pcwalton, #226): Get the CSS `line-height` property from each
- // non-replaced inline element to determine its block-size for computing
- // the line's own block-size.
- //
- // TODO(pcwalton): Cache the line scanner?
- debug!(
- "assign_block_size_inline: floats in: {:?}",
- self.base.floats
- );
-
- // Assign the block-size and late-computed inline-sizes for the inline fragments.
- for fragment in &mut self.fragments.fragments {
- fragment.update_late_computed_replaced_inline_size_if_necessary();
- fragment.assign_replaced_block_size_if_necessary();
- }
-
- // Reset our state, so that we handle incremental reflow correctly.
- //
- // TODO(pcwalton): Do something smarter, like Gecko and WebKit?
- self.lines.clear();
-
- // Determine how much indentation the first line wants.
- let mut indentation = if self.fragments.is_empty() {
- Au(0)
- } else {
- self.first_line_indentation
- };
-
- // Perform line breaking.
- let mut scanner = LineBreaker::new(
- self.base.floats.clone(),
- indentation,
- &self.minimum_line_metrics,
- );
- scanner.scan_for_lines(self, layout_context);
-
- // Now, go through each line and lay out the fragments inside.
- let line_count = self.lines.len();
- for (line_index, line) in self.lines.iter_mut().enumerate() {
- // Lay out fragments in the inline direction, and justify them if
- // necessary.
- InlineFlow::set_inline_fragment_positions(
- &mut self.fragments,
- line,
- self.base.flags.text_align(),
- indentation,
- line_index + 1 == line_count,
- );
-
- // Compute the final positions in the block direction of each fragment.
- InlineFlow::set_block_fragment_positions(
- &mut self.fragments,
- line,
- &self.minimum_line_metrics,
- layout_context,
- );
-
- // This is used to set the block-start position of the next line in
- // the next iteration of the loop. We're no longer on the first
- // line, so set indentation to zero.
- indentation = Au(0)
- }
-
- if self.is_absolute_containing_block() {
- // Assign block-sizes for all flows in this absolute flow tree.
- // This is preorder because the block-size of an absolute flow may depend on
- // the block-size of its containing block, which may also be an absolute flow.
- let assign_abs_b_sizes = AbsoluteAssignBSizesTraversal(layout_context.shared_context());
- assign_abs_b_sizes.traverse_absolute_flows(&mut *self);
- }
-
- self.base.position.size.block = match self.last_line_containing_real_fragments() {
- Some(last_line) => last_line.bounds.start.b + last_line.bounds.size.block,
- None => Au(0),
- };
-
- self.base.floats = scanner.floats;
- let writing_mode = self.base.floats.writing_mode;
- self.base.floats.translate(LogicalSize::new(
- writing_mode,
- Au(0),
- -self.base.position.size.block,
- ));
-
- let containing_block_size =
- LogicalSize::new(writing_mode, Au(0), self.base.position.size.block);
- self.mutate_fragments(&mut |f: &mut Fragment| match f.specific {
- SpecificFragmentInfo::InlineBlock(ref mut info) => {
- let block = FlowRef::deref_mut(&mut info.flow_ref);
- block.mut_base().early_absolute_position_info = EarlyAbsolutePositionInfo {
- relative_containing_block_size: containing_block_size,
- relative_containing_block_mode: writing_mode,
- };
- },
- SpecificFragmentInfo::InlineAbsolute(ref mut info) => {
- let block = FlowRef::deref_mut(&mut info.flow_ref);
- block.mut_base().early_absolute_position_info = EarlyAbsolutePositionInfo {
- relative_containing_block_size: containing_block_size,
- relative_containing_block_mode: writing_mode,
- };
- },
- _ => (),
- });
-
- self.base
- .restyle_damage
- .remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
- for fragment in &mut self.fragments.fragments {
- fragment
- .restyle_damage
- .remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
- }
- }
-
- fn compute_stacking_relative_position(&mut self, _: &LayoutContext) {
- // First, gather up the positions of all the containing blocks (if any).
- //
- // FIXME(pcwalton): This will get the absolute containing blocks inside `...` wrong in the
- // case of something like:
- //
- // <span style="position: relative">
- // Foo
- // <span style="display: inline-block">...</span>
- // </span>
- let mut containing_block_positions = Vec::new();
- let container_size = Size2D::new(self.base.block_container_inline_size, Au(0));
- for (fragment_index, fragment) in self.fragments.fragments.iter().enumerate() {
- match fragment.specific {
- SpecificFragmentInfo::InlineAbsolute(_) => {
- let containing_block_range = self
- .containing_block_range_for_flow_surrounding_fragment_at_index(
- FragmentIndex(fragment_index as isize),
- );
- let first_fragment_index = containing_block_range.begin().get() as usize;
- debug_assert!(first_fragment_index < self.fragments.fragments.len());
- let first_fragment = &self.fragments.fragments[first_fragment_index];
- let padding_box_origin = (first_fragment.border_box -
- first_fragment.style.logical_border_width())
- .start;
- containing_block_positions.push(
- padding_box_origin.to_physical(self.base.writing_mode, container_size),
- );
- },
- SpecificFragmentInfo::InlineBlock(_) if fragment.is_positioned() => {
- let containing_block_range = self
- .containing_block_range_for_flow_surrounding_fragment_at_index(
- FragmentIndex(fragment_index as isize),
- );
- let first_fragment_index = containing_block_range.begin().get() as usize;
- debug_assert!(first_fragment_index < self.fragments.fragments.len());
- let first_fragment = &self.fragments.fragments[first_fragment_index];
- let padding_box_origin = (first_fragment.border_box -
- first_fragment.style.logical_border_width())
- .start;
- containing_block_positions.push(
- padding_box_origin.to_physical(self.base.writing_mode, container_size),
- );
- },
- _ => {},
- }
- }
-
- // Then compute the positions of all of our fragments.
- let mut containing_block_positions = containing_block_positions.iter();
- for fragment in &mut self.fragments.fragments {
- let stacking_relative_border_box = fragment.stacking_relative_border_box(
- &self.base.stacking_relative_position,
- &self
- .base
- .early_absolute_position_info
- .relative_containing_block_size,
- self.base
- .early_absolute_position_info
- .relative_containing_block_mode,
- CoordinateSystem::Parent,
- );
- let stacking_relative_content_box =
- fragment.stacking_relative_content_box(stacking_relative_border_box);
-
- let is_positioned = fragment.is_positioned();
- match fragment.specific {
- SpecificFragmentInfo::InlineBlock(ref mut info) => {
- let flow = FlowRef::deref_mut(&mut info.flow_ref);
- let block_flow = flow.as_mut_block();
- block_flow.base.late_absolute_position_info =
- self.base.late_absolute_position_info;
-
- let stacking_relative_position = self.base.stacking_relative_position;
- if is_positioned {
- let padding_box_origin = containing_block_positions.next().unwrap();
- block_flow
- .base
- .late_absolute_position_info
- .stacking_relative_position_of_absolute_containing_block =
- *padding_box_origin + stacking_relative_position;
- }
-
- block_flow.base.stacking_relative_position =
- stacking_relative_content_box.origin.to_vector();
-
- // Write the clip in our coordinate system into the child flow. (The kid will
- // fix it up to be in its own coordinate system if necessary.)
- block_flow.base.clip = self.base.clip.clone()
- },
- SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) => {
- let flow = FlowRef::deref_mut(&mut info.flow_ref);
- let block_flow = flow.as_mut_block();
- block_flow.base.late_absolute_position_info =
- self.base.late_absolute_position_info;
-
- block_flow.base.stacking_relative_position =
- stacking_relative_border_box.origin.to_vector();
-
- // As above, this is in our coordinate system for now.
- block_flow.base.clip = self.base.clip.clone()
- },
- SpecificFragmentInfo::InlineAbsolute(ref mut info) => {
- let flow = FlowRef::deref_mut(&mut info.flow_ref);
- let block_flow = flow.as_mut_block();
- block_flow.base.late_absolute_position_info =
- self.base.late_absolute_position_info;
-
- let stacking_relative_position = self.base.stacking_relative_position;
- let padding_box_origin = containing_block_positions.next().unwrap();
- block_flow
- .base
- .late_absolute_position_info
- .stacking_relative_position_of_absolute_containing_block =
- *padding_box_origin + stacking_relative_position;
-
- block_flow.base.stacking_relative_position =
- stacking_relative_border_box.origin.to_vector();
-
- // As above, this is in our coordinate system for now.
- block_flow.base.clip = self.base.clip.clone()
- },
- _ => {},
- }
- }
- }
-
- fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {}
-
- fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {}
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
- self.base.stacking_context_id = state.current_stacking_context_id;
- self.base.clipping_and_scrolling = Some(state.current_clipping_and_scrolling);
- self.base.clip = state
- .clip_stack
- .last()
- .cloned()
- .unwrap_or_else(Rect::max_rect);
-
- let previous_cb_clipping_and_scrolling = state.containing_block_clipping_and_scrolling;
-
- for fragment in self.fragments.fragments.iter_mut() {
- state.containing_block_clipping_and_scrolling = previous_cb_clipping_and_scrolling;
-
- let abspos_containing_block = fragment.style.get_box().position != Position::Static;
- if abspos_containing_block {
- state.containing_block_clipping_and_scrolling =
- state.current_clipping_and_scrolling;
- }
-
- // We clear this here, but it might be set again if we create a stacking context for
- // this fragment.
- fragment.established_reference_frame = None;
-
- if !fragment.collect_stacking_contexts_for_blocklike_fragment(state) {
- if !fragment.establishes_stacking_context() {
- fragment.stacking_context_id = state.current_stacking_context_id;
- } else {
- fragment.create_stacking_context_for_inline_block(&self.base, state);
- }
- }
-
- // Reset the containing block clipping and scrolling before each loop iteration,
- // so we don't pollute subsequent fragments.
- state.containing_block_clipping_and_scrolling = previous_cb_clipping_and_scrolling;
- }
- }
-
- fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
- debug!(
- "Flow: building display list for {} inline fragments",
- self.fragments.len()
- );
-
- // We iterate using an index here, because we want to avoid doing a doing
- // a double-borrow of self (one mutable for the method call and one immutable
- // for the self.fragments.fragment iterator itself).
- for index in 0..self.fragments.fragments.len() {
- let (establishes_stacking_context, stacking_context_id) = {
- let fragment = self.fragments.fragments.get(index).unwrap();
- (
- self.base.stacking_context_id != fragment.stacking_context_id,
- fragment.stacking_context_id,
- )
- };
-
- let parent_stacking_context_id = state.current_stacking_context_id;
- if establishes_stacking_context {
- state.current_stacking_context_id = stacking_context_id;
- }
-
- self.build_display_list_for_inline_fragment_at_index(state, index);
-
- if establishes_stacking_context {
- state.current_stacking_context_id = parent_stacking_context_id
- }
- }
-
- if !self.fragments.fragments.is_empty() {
- self.base
- .build_display_items_for_debugging_tint(state, self.fragments.fragments[0].node);
- }
- }
-
- fn repair_style(&mut self, _: &ServoArc<ComputedValues>) {}
-
- fn compute_overflow(&self) -> Overflow {
- let mut overflow = Overflow::new();
- let flow_size = self.base.position.size.to_physical(self.base.writing_mode);
- let relative_containing_block_size = &self
- .base
- .early_absolute_position_info
- .relative_containing_block_size;
- for fragment in &self.fragments.fragments {
- overflow.union(&fragment.compute_overflow(&flow_size, &relative_containing_block_size))
- }
- overflow
- }
-
- fn iterate_through_fragment_border_boxes(
- &self,
- iterator: &mut dyn FragmentBorderBoxIterator,
- level: i32,
- stacking_context_position: &Point2D<Au>,
- ) {
- // FIXME(#2795): Get the real container size.
- for fragment in &self.fragments.fragments {
- if !iterator.should_process(fragment) {
- continue;
- }
-
- let stacking_relative_position = &self.base.stacking_relative_position;
- let relative_containing_block_size = &self
- .base
- .early_absolute_position_info
- .relative_containing_block_size;
- let relative_containing_block_mode = self
- .base
- .early_absolute_position_info
- .relative_containing_block_mode;
- iterator.process(
- fragment,
- level,
- &fragment
- .stacking_relative_border_box(
- stacking_relative_position,
- relative_containing_block_size,
- relative_containing_block_mode,
- CoordinateSystem::Own,
- )
- .translate(&stacking_context_position.to_vector()),
- )
- }
- }
-
- fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
- for fragment in &mut self.fragments.fragments {
- (*mutator)(fragment)
- }
- }
-
- fn contains_positioned_fragments(&self) -> bool {
- self.fragments
- .fragments
- .iter()
- .any(|fragment| fragment.is_positioned())
- }
-
- fn contains_relatively_positioned_fragments(&self) -> bool {
- self.fragments
- .fragments
- .iter()
- .any(|fragment| fragment.style.get_box().position == Position::Relative)
- }
-
- fn generated_containing_block_size(&self, for_flow: OpaqueFlow) -> LogicalSize<Au> {
- let mut containing_block_size = LogicalSize::new(self.base.writing_mode, Au(0), Au(0));
- for index in self.containing_block_range_for_flow(for_flow).each_index() {
- let fragment = &self.fragments.fragments[index.get() as usize];
- if fragment.is_absolutely_positioned() {
- continue;
- }
- containing_block_size.inline =
- containing_block_size.inline + fragment.border_box.size.inline;
- containing_block_size.block =
- max(containing_block_size.block, fragment.border_box.size.block);
- }
- containing_block_size
- }
-
- fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
- for fragment in &self.fragments.fragments {
- print_tree.add_item(format!("{:?}", fragment));
- }
- }
-}
-
-impl fmt::Debug for InlineFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "{:?}({:x}) {:?}",
- self.class(),
- self.base.debug_id(),
- self.base()
- )
- }
-}
-
-#[derive(Clone)]
-pub struct InlineFragmentNodeInfo {
- pub address: OpaqueNode,
- pub style: ServoArc<ComputedValues>,
- pub selected_style: ServoArc<ComputedValues>,
- pub pseudo: PseudoElementType,
- pub flags: InlineFragmentNodeFlags,
-}
-
-bitflags! {
- pub struct InlineFragmentNodeFlags: u8 {
- const FIRST_FRAGMENT_OF_ELEMENT = 0x01;
- const LAST_FRAGMENT_OF_ELEMENT = 0x02;
- }
-}
-
-impl fmt::Debug for InlineFragmentNodeInfo {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{:?}", self.flags.bits())
- }
-}
-
-#[derive(Clone)]
-pub struct InlineFragmentContext {
- /// The list of nodes that this fragment will be inheriting styles from,
- /// from the most deeply-nested node out.
- pub nodes: Vec<InlineFragmentNodeInfo>,
-}
-
-impl InlineFragmentContext {
- pub fn new() -> InlineFragmentContext {
- InlineFragmentContext { nodes: vec![] }
- }
-
- #[inline]
- pub fn contains_node(&self, node_address: OpaqueNode) -> bool {
- self.nodes
- .iter()
- .position(|node| node.address == node_address)
- .is_some()
- }
-
- fn ptr_eq(&self, other: &InlineFragmentContext) -> bool {
- if self.nodes.len() != other.nodes.len() {
- return false;
- }
- for (this_node, other_node) in self.nodes.iter().zip(&other.nodes) {
- if this_node.address != other_node.address {
- return false;
- }
- }
- true
- }
-}
-
-fn inline_contexts_are_equal(
- inline_context_a: &Option<InlineFragmentContext>,
- inline_context_b: &Option<InlineFragmentContext>,
-) -> bool {
- match (inline_context_a, inline_context_b) {
- (&Some(ref inline_context_a), &Some(ref inline_context_b)) => {
- inline_context_a.ptr_eq(inline_context_b)
- },
- (&None, &None) => true,
- (&Some(_), &None) | (&None, &Some(_)) => false,
- }
-}
-
-/// Ascent and space needed above and below the baseline for a fragment. See CSS 2.1 § 10.8.1.
-///
-/// Descent is not included in this structure because it can be computed from the fragment's
-/// border/content box and the ascent.
-#[derive(Clone, Copy, Debug, Serialize)]
-pub struct InlineMetrics {
- /// The amount of space above the baseline needed for this fragment.
- pub space_above_baseline: Au,
- /// The amount of space below the baseline needed for this fragment.
- pub space_below_baseline: Au,
- /// The distance from the baseline to the top of this fragment. This can differ from
- /// `block_size_above_baseline` if the fragment needs some empty space above it due to
- /// line-height, etc.
- pub ascent: Au,
-}
-
-impl InlineMetrics {
- /// Creates a new set of inline metrics.
- pub fn new(space_above_baseline: Au, space_below_baseline: Au, ascent: Au) -> InlineMetrics {
- InlineMetrics {
- space_above_baseline: space_above_baseline,
- space_below_baseline: space_below_baseline,
- ascent: ascent,
- }
- }
-
- /// Calculates inline metrics from font metrics and line block-size per CSS 2.1 § 10.8.1.
- #[inline]
- pub fn from_font_metrics(font_metrics: &FontMetrics, line_height: Au) -> InlineMetrics {
- let leading = line_height - (font_metrics.ascent + font_metrics.descent);
-
- // Calculating the half leading here and then using leading - half_leading
- // below ensure that we don't introduce any rounding accuracy issues here.
- // The invariant is that the resulting total line height must exactly
- // equal the requested line_height.
- let half_leading = leading.scale_by(0.5);
- InlineMetrics {
- space_above_baseline: font_metrics.ascent + half_leading,
- space_below_baseline: font_metrics.descent + leading - half_leading,
- ascent: font_metrics.ascent,
- }
- }
-
- /// Returns the sum of the space needed above and below the baseline.
- fn space_needed(&self) -> Au {
- self.space_above_baseline + self.space_below_baseline
- }
-}
-
-#[derive(Clone, Copy, PartialEq)]
-enum LineFlushMode {
- No,
- Flush,
-}
-
-#[derive(Clone, Copy, Debug, Serialize)]
-pub struct LineMetrics {
- pub space_above_baseline: Au,
- pub space_below_baseline: Au,
-}
-
-impl LineMetrics {
- pub fn new(space_above_baseline: Au, space_below_baseline: Au) -> LineMetrics {
- LineMetrics {
- space_above_baseline: space_above_baseline,
- space_below_baseline: space_below_baseline,
- }
- }
-
- /// Returns the line metrics that result from combining the line that these metrics represent
- /// with a fragment with the given metrics.
- fn new_metrics_for_fragment(&self, fragment_inline_metrics: &InlineMetrics) -> LineMetrics {
- LineMetrics {
- space_above_baseline: max(
- self.space_above_baseline,
- fragment_inline_metrics.space_above_baseline,
- ),
- space_below_baseline: max(
- self.space_below_baseline,
- fragment_inline_metrics.space_below_baseline,
- ),
- }
- }
-
- fn for_line_and_fragment(
- line: &Line,
- fragment: &Fragment,
- layout_context: &LayoutContext,
- ) -> LineMetrics {
- if !fragment.is_hypothetical() {
- let space_above_baseline = line.metrics.space_above_baseline;
- return LineMetrics {
- space_above_baseline: space_above_baseline,
- space_below_baseline: line.bounds.size.block - space_above_baseline,
- };
- }
-
- let hypothetical_line_metrics = line.new_metrics_for_fragment(fragment, layout_context);
- let hypothetical_block_size =
- line.new_block_size_for_fragment(fragment, &hypothetical_line_metrics, layout_context);
- let hypothetical_space_above_baseline = hypothetical_line_metrics.space_above_baseline;
- LineMetrics {
- space_above_baseline: hypothetical_space_above_baseline,
- space_below_baseline: hypothetical_block_size - hypothetical_space_above_baseline,
- }
- }
-
- /// Returns the sum of the space needed above and below the baseline.
- pub fn space_needed(&self) -> Au {
- self.space_above_baseline + self.space_below_baseline
- }
-}
diff --git a/components/layout_2020/layout_debug.rs b/components/layout_2020/layout_debug.rs
deleted file mode 100644
index d3af6b4d8d5..00000000000
--- a/components/layout_2020/layout_debug.rs
+++ /dev/null
@@ -1,124 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Supports writing a trace file created during each layout scope
-//! that can be viewed by an external tool to make layout debugging easier.
-
-use crate::flow::GetBaseFlow;
-use crate::flow_ref::FlowRef;
-use serde_json::{to_string, to_value, Value};
-use std::borrow::ToOwned;
-use std::cell::RefCell;
-use std::fs::File;
-use std::io::Write;
-#[cfg(debug_assertions)]
-use std::sync::atomic::{AtomicUsize, Ordering};
-
-thread_local!(static STATE_KEY: RefCell<Option<State>> = RefCell::new(None));
-
-#[cfg(debug_assertions)]
-static DEBUG_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
-
-pub struct Scope;
-
-#[macro_export]
-macro_rules! layout_debug_scope(
- ($($arg:tt)*) => (
- if cfg!(debug_assertions) {
- layout_debug::Scope::new(format!($($arg)*))
- } else {
- layout_debug::Scope
- }
- )
-);
-
-#[derive(Serialize)]
-struct ScopeData {
- name: String,
- pre: Value,
- post: Value,
- children: Vec<Box<ScopeData>>,
-}
-
-impl ScopeData {
- fn new(name: String, pre: Value) -> ScopeData {
- ScopeData {
- name: name,
- pre: pre,
- post: Value::Null,
- children: vec![],
- }
- }
-}
-
-struct State {
- flow_root: FlowRef,
- scope_stack: Vec<Box<ScopeData>>,
-}
-
-/// A layout debugging scope. The entire state of the flow tree
-/// will be output at the beginning and end of this scope.
-impl Scope {
- pub fn new(name: String) -> Scope {
- STATE_KEY.with(|ref r| {
- if let Some(ref mut state) = *r.borrow_mut() {
- let flow_trace = to_value(&state.flow_root.base()).unwrap();
- let data = Box::new(ScopeData::new(name.clone(), flow_trace));
- state.scope_stack.push(data);
- }
- });
- Scope
- }
-}
-
-#[cfg(debug_assertions)]
-impl Drop for Scope {
- fn drop(&mut self) {
- STATE_KEY.with(|ref r| {
- if let Some(ref mut state) = *r.borrow_mut() {
- let mut current_scope = state.scope_stack.pop().unwrap();
- current_scope.post = to_value(&state.flow_root.base()).unwrap();
- let previous_scope = state.scope_stack.last_mut().unwrap();
- previous_scope.children.push(current_scope);
- }
- });
- }
-}
-
-/// Generate a unique ID. This is used for items such as Fragment
-/// which are often reallocated but represent essentially the
-/// same data.
-#[cfg(debug_assertions)]
-pub fn generate_unique_debug_id() -> u16 {
- DEBUG_ID_COUNTER.fetch_add(1, Ordering::SeqCst) as u16
-}
-
-/// Begin a layout debug trace. If this has not been called,
-/// creating debug scopes has no effect.
-pub fn begin_trace(flow_root: FlowRef) {
- assert!(STATE_KEY.with(|ref r| r.borrow().is_none()));
-
- STATE_KEY.with(|ref r| {
- let flow_trace = to_value(&flow_root.base()).unwrap();
- let state = State {
- scope_stack: vec![Box::new(ScopeData::new("root".to_owned(), flow_trace))],
- flow_root: flow_root.clone(),
- };
- *r.borrow_mut() = Some(state);
- });
-}
-
-/// End the debug layout trace. This will write the layout
-/// trace to disk in the current directory. The output
-/// file can then be viewed with an external tool.
-pub fn end_trace(generation: u32) {
- let mut thread_state = STATE_KEY.with(|ref r| r.borrow_mut().take().unwrap());
- assert_eq!(thread_state.scope_stack.len(), 1);
- let mut root_scope = thread_state.scope_stack.pop().unwrap();
- root_scope.post = to_value(&thread_state.flow_root.base()).unwrap();
-
- let result = to_string(&root_scope).unwrap();
- let mut file = File::create(format!("layout_trace-{}.json", generation)).unwrap();
- file.write_all(result.as_bytes()).unwrap();
-}
diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs
index f8afdc79190..98a2d5c618d 100644
--- a/components/layout_2020/lib.rs
+++ b/components/layout_2020/lib.rs
@@ -5,58 +5,21 @@
#![deny(unsafe_code)]
#[macro_use]
-extern crate bitflags;
-#[macro_use]
-extern crate html5ever;
-#[macro_use]
-extern crate log;
-#[macro_use]
-extern crate range;
-#[macro_use]
extern crate serde;
-#[macro_use]
-pub mod layout_debug;
-
-pub mod animation;
-mod block;
-pub mod construct;
pub mod context;
pub mod data;
pub mod display_list;
-mod flex;
-mod floats;
pub mod flow;
-mod flow_list;
pub mod flow_ref;
mod fragment;
-mod generated_content;
-pub mod incremental;
-mod inline;
-mod linked_list;
-mod list_item;
-mod model;
-mod multicol;
pub mod opaque_node;
-pub mod parallel;
-mod persistent_list;
pub mod query;
-pub mod sequential;
-mod table;
-mod table_caption;
-mod table_cell;
-mod table_colgroup;
-mod table_row;
-mod table_rowgroup;
-mod table_wrapper;
-mod text;
pub mod traversal;
pub mod wrapper;
// For unit tests:
-pub use self::data::LayoutData;
pub use crate::fragment::Fragment;
-pub use crate::fragment::SpecificFragmentInfo;
// We can't use servo_arc for everything in layout, because the Flow stuff uses
// weak references.
diff --git a/components/layout_2020/linked_list.rs b/components/layout_2020/linked_list.rs
deleted file mode 100644
index 09e605a3d31..00000000000
--- a/components/layout_2020/linked_list.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Utility functions for doubly-linked lists.
-
-use std::collections::LinkedList;
-use std::mem;
-
-/// Splits the head off a list in O(1) time, and returns the head.
-pub fn split_off_head<T>(list: &mut LinkedList<T>) -> LinkedList<T> {
- let tail = list.split_off(1);
- mem::replace(list, tail)
-}
-
-/// Prepends the items in the other list to this one, leaving the other list empty.
-#[inline]
-pub fn prepend_from<T>(this: &mut LinkedList<T>, other: &mut LinkedList<T>) {
- other.append(this);
- mem::swap(this, other);
-}
diff --git a/components/layout_2020/list_item.rs b/components/layout_2020/list_item.rs
deleted file mode 100644
index 3416ff444b3..00000000000
--- a/components/layout_2020/list_item.rs
+++ /dev/null
@@ -1,323 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Layout for elements with a CSS `display` property of `list-item`. These elements consist of a
-//! block and an extra inline fragment for the marker.
-
-use crate::block::BlockFlow;
-use crate::context::{with_thread_local_font_context, LayoutContext};
-use crate::display_list::items::DisplayListSection;
-use crate::display_list::{
- BorderPaintingMode, DisplayListBuildState, StackingContextCollectionState,
-};
-use crate::floats::FloatKind;
-use crate::flow::{Flow, FlowClass, OpaqueFlow};
-use crate::fragment::Overflow;
-use crate::fragment::{
- CoordinateSystem, Fragment, FragmentBorderBoxIterator, GeneratedContentInfo,
-};
-use crate::generated_content;
-use crate::inline::InlineFlow;
-use app_units::Au;
-use euclid::Point2D;
-use style::computed_values::list_style_type::T as ListStyleType;
-use style::computed_values::position::T as Position;
-use style::logical_geometry::LogicalSize;
-use style::properties::ComputedValues;
-use style::servo::restyle_damage::ServoRestyleDamage;
-
-#[allow(unsafe_code)]
-unsafe impl crate::flow::HasBaseFlow for ListItemFlow {}
-
-/// A block with the CSS `display` property equal to `list-item`.
-#[derive(Debug)]
-#[repr(C)]
-pub struct ListItemFlow {
- /// Data common to all block flows.
- pub block_flow: BlockFlow,
- /// The marker, if outside. (Markers that are inside are instead just fragments on the interior
- /// `InlineFlow`.)
- pub marker_fragments: Vec<Fragment>,
-}
-
-impl ListItemFlow {
- pub fn from_fragments_and_flotation(
- main_fragment: Fragment,
- marker_fragments: Vec<Fragment>,
- flotation: Option<FloatKind>,
- ) -> ListItemFlow {
- let mut this = ListItemFlow {
- block_flow: BlockFlow::from_fragment_and_float_kind(main_fragment, flotation),
- marker_fragments: marker_fragments,
- };
-
- if let Some(ref marker) = this.marker_fragments.first() {
- match marker.style().get_list().list_style_type {
- ListStyleType::Disc |
- ListStyleType::None |
- ListStyleType::Circle |
- ListStyleType::Square |
- ListStyleType::DisclosureOpen |
- ListStyleType::DisclosureClosed => {},
- _ => this
- .block_flow
- .base
- .restyle_damage
- .insert(ServoRestyleDamage::RESOLVE_GENERATED_CONTENT),
- }
- }
-
- this
- }
-
- /// Assign inline size and position for the marker. This is done during the `assign_block_size`
- /// traversal because floats will impact the marker position. Therefore we need to have already
- /// called `assign_block_size` on the list item's block flow, in order to know which floats
- /// impact the position.
- ///
- /// Per CSS 2.1 § 12.5.1, the marker position is not precisely specified, but it must be on the
- /// left side of the content (for ltr direction). However, flowing the marker around floats
- /// matches the rendering of Gecko and Blink.
- fn assign_marker_inline_sizes(&mut self, layout_context: &LayoutContext) {
- let base = &self.block_flow.base;
- let available_rect = base.floats.available_rect(
- -base.position.size.block,
- base.position.size.block,
- base.block_container_inline_size,
- );
- let mut marker_inline_start = available_rect
- .unwrap_or(self.block_flow.fragment.border_box)
- .start
- .i;
-
- for marker in self.marker_fragments.iter_mut().rev() {
- let container_block_size = self
- .block_flow
- .explicit_block_containing_size(layout_context.shared_context());
- marker.assign_replaced_inline_size_if_necessary(
- base.block_container_inline_size,
- container_block_size,
- );
-
- // Do this now. There's no need to do this in bubble-widths, since markers do not
- // contribute to the inline size of this flow.
- let intrinsic_inline_sizes = marker.compute_intrinsic_inline_sizes();
-
- marker.border_box.size.inline = intrinsic_inline_sizes
- .content_intrinsic_sizes
- .preferred_inline_size;
- marker_inline_start = marker_inline_start - marker.border_box.size.inline;
- marker.border_box.start.i = marker_inline_start;
- }
- }
-
- fn assign_marker_block_sizes(&mut self, layout_context: &LayoutContext) {
- // FIXME(pcwalton): Do this during flow construction, like `InlineFlow` does?
- let marker_line_metrics = with_thread_local_font_context(layout_context, |font_context| {
- InlineFlow::minimum_line_metrics_for_fragments(
- &self.marker_fragments,
- font_context,
- &*self.block_flow.fragment.style,
- )
- });
-
- for marker in &mut self.marker_fragments {
- marker.assign_replaced_block_size_if_necessary();
- let marker_inline_metrics = marker.aligned_inline_metrics(
- layout_context,
- &marker_line_metrics,
- Some(&marker_line_metrics),
- );
- marker.border_box.start.b =
- marker_line_metrics.space_above_baseline - marker_inline_metrics.ascent;
- }
- }
-}
-
-impl Flow for ListItemFlow {
- fn class(&self) -> FlowClass {
- FlowClass::ListItem
- }
-
- fn as_mut_block(&mut self) -> &mut BlockFlow {
- &mut self.block_flow
- }
-
- fn as_block(&self) -> &BlockFlow {
- &self.block_flow
- }
-
- fn bubble_inline_sizes(&mut self) {
- // The marker contributes no intrinsic inline-size, so…
- self.block_flow.bubble_inline_sizes()
- }
-
- fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
- self.block_flow.assign_inline_sizes(layout_context);
- }
-
- fn assign_block_size(&mut self, layout_context: &LayoutContext) {
- self.block_flow.assign_block_size(layout_context);
- self.assign_marker_inline_sizes(layout_context);
- self.assign_marker_block_sizes(layout_context);
- }
-
- fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
- self.block_flow
- .compute_stacking_relative_position(layout_context)
- }
-
- fn place_float_if_applicable<'a>(&mut self) {
- self.block_flow.place_float_if_applicable()
- }
-
- fn contains_roots_of_absolute_flow_tree(&self) -> bool {
- self.block_flow.contains_roots_of_absolute_flow_tree()
- }
-
- fn is_absolute_containing_block(&self) -> bool {
- self.block_flow.is_absolute_containing_block()
- }
-
- fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
- self.block_flow
- .update_late_computed_inline_position_if_necessary(inline_position)
- }
-
- fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
- self.block_flow
- .update_late_computed_block_position_if_necessary(block_position)
- }
-
- fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
- // Draw the marker, if applicable.
- for marker in &mut self.marker_fragments {
- let stacking_relative_border_box = self
- .block_flow
- .base
- .stacking_relative_border_box_for_display_list(marker);
- marker.build_display_list(
- state,
- stacking_relative_border_box,
- BorderPaintingMode::Separate,
- DisplayListSection::Content,
- self.block_flow.base.clip,
- None,
- );
- }
-
- // Draw the rest of the block.
- self.block_flow
- .build_display_list_for_block(state, BorderPaintingMode::Separate)
- }
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
- self.block_flow.collect_stacking_contexts(state);
- }
-
- fn repair_style(&mut self, new_style: &crate::ServoArc<ComputedValues>) {
- self.block_flow.repair_style(new_style)
- }
-
- fn compute_overflow(&self) -> Overflow {
- let mut overflow = self.block_flow.compute_overflow();
- let flow_size = self
- .block_flow
- .base
- .position
- .size
- .to_physical(self.block_flow.base.writing_mode);
- let relative_containing_block_size = &self
- .block_flow
- .base
- .early_absolute_position_info
- .relative_containing_block_size;
-
- for fragment in &self.marker_fragments {
- overflow.union(&fragment.compute_overflow(&flow_size, &relative_containing_block_size))
- }
- overflow
- }
-
- fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
- self.block_flow.generated_containing_block_size(flow)
- }
-
- /// The 'position' property of this flow.
- fn positioning(&self) -> Position {
- self.block_flow.positioning()
- }
-
- fn iterate_through_fragment_border_boxes(
- &self,
- iterator: &mut dyn FragmentBorderBoxIterator,
- level: i32,
- stacking_context_position: &Point2D<Au>,
- ) {
- self.block_flow.iterate_through_fragment_border_boxes(
- iterator,
- level,
- stacking_context_position,
- );
-
- for marker in &self.marker_fragments {
- if iterator.should_process(marker) {
- iterator.process(
- marker,
- level,
- &marker
- .stacking_relative_border_box(
- &self.block_flow.base.stacking_relative_position,
- &self
- .block_flow
- .base
- .early_absolute_position_info
- .relative_containing_block_size,
- self.block_flow
- .base
- .early_absolute_position_info
- .relative_containing_block_mode,
- CoordinateSystem::Own,
- )
- .translate(&stacking_context_position.to_vector()),
- );
- }
- }
- }
-
- fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
- self.block_flow.mutate_fragments(mutator);
-
- for marker in &mut self.marker_fragments {
- (*mutator)(marker)
- }
- }
-}
-
-/// The kind of content that `list-style-type` results in.
-pub enum ListStyleTypeContent {
- None,
- StaticText(char),
- GeneratedContent(Box<GeneratedContentInfo>),
-}
-
-impl ListStyleTypeContent {
- /// Returns the content to be used for the given value of the `list-style-type` property.
- pub fn from_list_style_type(list_style_type: ListStyleType) -> ListStyleTypeContent {
- // Just to keep things simple, use a nonbreaking space (Unicode 0xa0) to provide the marker
- // separation.
- match list_style_type {
- ListStyleType::None => ListStyleTypeContent::None,
- ListStyleType::Disc |
- ListStyleType::Circle |
- ListStyleType::Square |
- ListStyleType::DisclosureOpen |
- ListStyleType::DisclosureClosed => {
- let text = generated_content::static_representation(list_style_type);
- ListStyleTypeContent::StaticText(text)
- },
- _ => ListStyleTypeContent::GeneratedContent(Box::new(GeneratedContentInfo::ListItem)),
- }
- }
-}
diff --git a/components/layout_2020/model.rs b/components/layout_2020/model.rs
deleted file mode 100644
index 17c3b625af3..00000000000
--- a/components/layout_2020/model.rs
+++ /dev/null
@@ -1,609 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Borders, padding, and margins.
-
-use crate::fragment::Fragment;
-use app_units::Au;
-use euclid::SideOffsets2D;
-use std::cmp::{max, min};
-use std::fmt;
-use style::logical_geometry::{LogicalMargin, WritingMode};
-use style::properties::ComputedValues;
-use style::values::computed::MaxSize;
-use style::values::computed::{LengthPercentageOrAuto, Size};
-
-/// A collapsible margin. See CSS 2.1 § 8.3.1.
-#[derive(Clone, Copy, Debug)]
-pub struct AdjoiningMargins {
- /// The value of the greatest positive margin.
- pub most_positive: Au,
-
- /// The actual value (not the absolute value) of the negative margin with the largest absolute
- /// value. Since this is not the absolute value, this is always zero or negative.
- pub most_negative: Au,
-}
-
-impl AdjoiningMargins {
- pub fn new() -> AdjoiningMargins {
- AdjoiningMargins {
- most_positive: Au(0),
- most_negative: Au(0),
- }
- }
-
- pub fn from_margin(margin_value: Au) -> AdjoiningMargins {
- if margin_value >= Au(0) {
- AdjoiningMargins {
- most_positive: margin_value,
- most_negative: Au(0),
- }
- } else {
- AdjoiningMargins {
- most_positive: Au(0),
- most_negative: margin_value,
- }
- }
- }
-
- pub fn union(&mut self, other: AdjoiningMargins) {
- self.most_positive = max(self.most_positive, other.most_positive);
- self.most_negative = min(self.most_negative, other.most_negative)
- }
-
- pub fn collapse(&self) -> Au {
- self.most_positive + self.most_negative
- }
-}
-
-/// Represents the block-start and block-end margins of a flow with collapsible margins. See CSS 2.1 § 8.3.1.
-#[derive(Clone, Copy, Debug)]
-pub enum CollapsibleMargins {
- /// Margins may not collapse with this flow.
- None(Au, Au),
-
- /// Both the block-start and block-end margins (specified here in that order) may collapse, but the
- /// margins do not collapse through this flow.
- Collapse(AdjoiningMargins, AdjoiningMargins),
-
- /// Margins collapse *through* this flow. This means, essentially, that the flow doesn’t
- /// have any border, padding, or out-of-flow (floating or positioned) content
- CollapseThrough(AdjoiningMargins),
-}
-
-impl CollapsibleMargins {
- pub fn new() -> CollapsibleMargins {
- CollapsibleMargins::None(Au(0), Au(0))
- }
-
- /// Returns the amount of margin that should be applied in a noncollapsible context. This is
- /// currently used to apply block-start margin for hypothetical boxes, since we do not collapse
- /// margins of hypothetical boxes.
- pub fn block_start_margin_for_noncollapsible_context(&self) -> Au {
- match *self {
- CollapsibleMargins::None(block_start, _) => block_start,
- CollapsibleMargins::Collapse(ref block_start, _) |
- CollapsibleMargins::CollapseThrough(ref block_start) => block_start.collapse(),
- }
- }
-
- pub fn block_end_margin_for_noncollapsible_context(&self) -> Au {
- match *self {
- CollapsibleMargins::None(_, block_end) => block_end,
- CollapsibleMargins::Collapse(_, ref block_end) |
- CollapsibleMargins::CollapseThrough(ref block_end) => block_end.collapse(),
- }
- }
-}
-
-enum FinalMarginState {
- MarginsCollapseThrough,
- BottomMarginCollapses,
-}
-
-pub struct MarginCollapseInfo {
- pub state: MarginCollapseState,
- pub block_start_margin: AdjoiningMargins,
- pub margin_in: AdjoiningMargins,
-}
-
-impl MarginCollapseInfo {
- pub fn initialize_block_start_margin(
- fragment: &Fragment,
- can_collapse_block_start_margin_with_kids: bool,
- ) -> MarginCollapseInfo {
- MarginCollapseInfo {
- state: if can_collapse_block_start_margin_with_kids {
- MarginCollapseState::AccumulatingCollapsibleTopMargin
- } else {
- MarginCollapseState::AccumulatingMarginIn
- },
- block_start_margin: AdjoiningMargins::from_margin(fragment.margin.block_start),
- margin_in: AdjoiningMargins::new(),
- }
- }
-
- pub fn finish_and_compute_collapsible_margins(
- mut self,
- fragment: &Fragment,
- containing_block_size: Option<Au>,
- can_collapse_block_end_margin_with_kids: bool,
- mut may_collapse_through: bool,
- ) -> (CollapsibleMargins, Au) {
- let state = match self.state {
- MarginCollapseState::AccumulatingCollapsibleTopMargin => {
- may_collapse_through = may_collapse_through &&
- match fragment.style().content_block_size() {
- Size::Auto => true,
- Size::LengthPercentage(ref lp) => {
- lp.is_definitely_zero() ||
- lp.maybe_to_used_value(containing_block_size).is_none()
- },
- };
-
- if may_collapse_through {
- if fragment.style.min_block_size().is_auto() ||
- fragment.style().min_block_size().is_definitely_zero()
- {
- FinalMarginState::MarginsCollapseThrough
- } else {
- // If the fragment has non-zero min-block-size, margins may not
- // collapse through it.
- FinalMarginState::BottomMarginCollapses
- }
- } else {
- // If the fragment has an explicitly specified block-size, margins may not
- // collapse through it.
- FinalMarginState::BottomMarginCollapses
- }
- },
- MarginCollapseState::AccumulatingMarginIn => FinalMarginState::BottomMarginCollapses,
- };
-
- // Different logic is needed here depending on whether this flow can collapse its block-end
- // margin with its children.
- let block_end_margin = fragment.margin.block_end;
- if !can_collapse_block_end_margin_with_kids {
- match state {
- FinalMarginState::MarginsCollapseThrough => {
- let advance = self.block_start_margin.collapse();
- self.margin_in
- .union(AdjoiningMargins::from_margin(block_end_margin));
- (
- CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in),
- advance,
- )
- },
- FinalMarginState::BottomMarginCollapses => {
- let advance = self.margin_in.collapse();
- self.margin_in
- .union(AdjoiningMargins::from_margin(block_end_margin));
- (
- CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in),
- advance,
- )
- },
- }
- } else {
- match state {
- FinalMarginState::MarginsCollapseThrough => {
- self.block_start_margin
- .union(AdjoiningMargins::from_margin(block_end_margin));
- (
- CollapsibleMargins::CollapseThrough(self.block_start_margin),
- Au(0),
- )
- },
- FinalMarginState::BottomMarginCollapses => {
- self.margin_in
- .union(AdjoiningMargins::from_margin(block_end_margin));
- (
- CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in),
- Au(0),
- )
- },
- }
- }
- }
-
- pub fn current_float_ceiling(&mut self) -> Au {
- match self.state {
- MarginCollapseState::AccumulatingCollapsibleTopMargin => {
- // We do not include the top margin in the float ceiling, because the float flow
- // needs to be positioned relative to our *border box*, not our margin box. See
- // `tests/ref/float_under_top_margin_a.html`.
- Au(0)
- },
- MarginCollapseState::AccumulatingMarginIn => self.margin_in.collapse(),
- }
- }
-
- /// Adds the child's potentially collapsible block-start margin to the current margin state and
- /// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
- /// that should be added to the Y offset during block layout.
- pub fn advance_block_start_margin(
- &mut self,
- child_collapsible_margins: &CollapsibleMargins,
- can_collapse_block_start_margin: bool,
- ) -> Au {
- if !can_collapse_block_start_margin {
- self.state = MarginCollapseState::AccumulatingMarginIn
- }
-
- match (self.state, *child_collapsible_margins) {
- (
- MarginCollapseState::AccumulatingCollapsibleTopMargin,
- CollapsibleMargins::None(block_start, _),
- ) => {
- self.state = MarginCollapseState::AccumulatingMarginIn;
- block_start
- },
- (
- MarginCollapseState::AccumulatingCollapsibleTopMargin,
- CollapsibleMargins::Collapse(block_start, _),
- ) => {
- self.block_start_margin.union(block_start);
- self.state = MarginCollapseState::AccumulatingMarginIn;
- Au(0)
- },
- (
- MarginCollapseState::AccumulatingMarginIn,
- CollapsibleMargins::None(block_start, _),
- ) => {
- let previous_margin_value = self.margin_in.collapse();
- self.margin_in = AdjoiningMargins::new();
- previous_margin_value + block_start
- },
- (
- MarginCollapseState::AccumulatingMarginIn,
- CollapsibleMargins::Collapse(block_start, _),
- ) => {
- self.margin_in.union(block_start);
- let margin_value = self.margin_in.collapse();
- self.margin_in = AdjoiningMargins::new();
- margin_value
- },
- (_, CollapsibleMargins::CollapseThrough(_)) => {
- // For now, we ignore this; this will be handled by `advance_block_end_margin`
- // below.
- Au(0)
- },
- }
- }
-
- /// Adds the child's potentially collapsible block-end margin to the current margin state and
- /// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
- /// that should be added to the Y offset during block layout.
- pub fn advance_block_end_margin(
- &mut self,
- child_collapsible_margins: &CollapsibleMargins,
- ) -> Au {
- match (self.state, *child_collapsible_margins) {
- (
- MarginCollapseState::AccumulatingCollapsibleTopMargin,
- CollapsibleMargins::None(..),
- ) |
- (
- MarginCollapseState::AccumulatingCollapsibleTopMargin,
- CollapsibleMargins::Collapse(..),
- ) => {
- // Can't happen because the state will have been replaced with
- // `MarginCollapseState::AccumulatingMarginIn` above.
- panic!("should not be accumulating collapsible block_start margins anymore!")
- },
- (
- MarginCollapseState::AccumulatingCollapsibleTopMargin,
- CollapsibleMargins::CollapseThrough(margin),
- ) => {
- self.block_start_margin.union(margin);
- Au(0)
- },
- (MarginCollapseState::AccumulatingMarginIn, CollapsibleMargins::None(_, block_end)) => {
- assert_eq!(self.margin_in.most_positive, Au(0));
- assert_eq!(self.margin_in.most_negative, Au(0));
- block_end
- },
- (
- MarginCollapseState::AccumulatingMarginIn,
- CollapsibleMargins::Collapse(_, block_end),
- ) |
- (
- MarginCollapseState::AccumulatingMarginIn,
- CollapsibleMargins::CollapseThrough(block_end),
- ) => {
- self.margin_in.union(block_end);
- Au(0)
- },
- }
- }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub enum MarginCollapseState {
- /// We are accumulating margin on the logical top of this flow.
- AccumulatingCollapsibleTopMargin,
- /// We are accumulating margin between two blocks.
- AccumulatingMarginIn,
-}
-
-/// Intrinsic inline-sizes, which consist of minimum and preferred.
-#[derive(Clone, Copy, Serialize)]
-pub struct IntrinsicISizes {
- /// The *minimum inline-size* of the content.
- pub minimum_inline_size: Au,
- /// The *preferred inline-size* of the content.
- pub preferred_inline_size: Au,
-}
-
-impl fmt::Debug for IntrinsicISizes {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "min={:?}, pref={:?}",
- self.minimum_inline_size, self.preferred_inline_size
- )
- }
-}
-
-impl IntrinsicISizes {
- pub fn new() -> IntrinsicISizes {
- IntrinsicISizes {
- minimum_inline_size: Au(0),
- preferred_inline_size: Au(0),
- }
- }
-}
-
-/// The temporary result of the computation of intrinsic inline-sizes.
-#[derive(Debug)]
-pub struct IntrinsicISizesContribution {
- /// Intrinsic sizes for the content only (not counting borders, padding, or margins).
- pub content_intrinsic_sizes: IntrinsicISizes,
- /// The inline size of borders and padding, as well as margins if appropriate.
- pub surrounding_size: Au,
-}
-
-impl IntrinsicISizesContribution {
- /// Creates and initializes an inline size computation with all sizes set to zero.
- pub fn new() -> IntrinsicISizesContribution {
- IntrinsicISizesContribution {
- content_intrinsic_sizes: IntrinsicISizes::new(),
- surrounding_size: Au(0),
- }
- }
-
- /// Adds the content intrinsic sizes and the surrounding size together to yield the final
- /// intrinsic size computation.
- pub fn finish(self) -> IntrinsicISizes {
- IntrinsicISizes {
- minimum_inline_size: self.content_intrinsic_sizes.minimum_inline_size +
- self.surrounding_size,
- preferred_inline_size: self.content_intrinsic_sizes.preferred_inline_size +
- self.surrounding_size,
- }
- }
-
- /// Updates the computation so that the minimum is the maximum of the current minimum and the
- /// given minimum and the preferred is the sum of the current preferred and the given
- /// preferred. This is used when laying out fragments in the inline direction.
- ///
- /// FIXME(pcwalton): This is incorrect when the inline fragment contains forced line breaks
- /// (e.g. `<br>` or `white-space: pre`).
- pub fn union_inline(&mut self, sizes: &IntrinsicISizes) {
- self.content_intrinsic_sizes.minimum_inline_size = max(
- self.content_intrinsic_sizes.minimum_inline_size,
- sizes.minimum_inline_size,
- );
- self.content_intrinsic_sizes.preferred_inline_size =
- self.content_intrinsic_sizes.preferred_inline_size + sizes.preferred_inline_size
- }
-
- /// Updates the computation so that the minimum is the sum of the current minimum and the
- /// given minimum and the preferred is the sum of the current preferred and the given
- /// preferred. This is used when laying out fragments in the inline direction when
- /// `white-space` is `pre` or `nowrap`.
- pub fn union_nonbreaking_inline(&mut self, sizes: &IntrinsicISizes) {
- self.content_intrinsic_sizes.minimum_inline_size =
- self.content_intrinsic_sizes.minimum_inline_size + sizes.minimum_inline_size;
- self.content_intrinsic_sizes.preferred_inline_size =
- self.content_intrinsic_sizes.preferred_inline_size + sizes.preferred_inline_size
- }
-
- /// Updates the computation so that the minimum is the maximum of the current minimum and the
- /// given minimum and the preferred is the maximum of the current preferred and the given
- /// preferred. This can be useful when laying out fragments in the block direction (but note
- /// that it does not take floats into account, so `BlockFlow` does not use it).
- ///
- /// This is used when contributing the intrinsic sizes for individual fragments.
- pub fn union_block(&mut self, sizes: &IntrinsicISizes) {
- self.content_intrinsic_sizes.minimum_inline_size = max(
- self.content_intrinsic_sizes.minimum_inline_size,
- sizes.minimum_inline_size,
- );
- self.content_intrinsic_sizes.preferred_inline_size = max(
- self.content_intrinsic_sizes.preferred_inline_size,
- sizes.preferred_inline_size,
- )
- }
-}
-
-/// Useful helper data type when computing values for blocks and positioned elements.
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum MaybeAuto {
- Auto,
- Specified(Au),
-}
-
-impl MaybeAuto {
- #[inline]
- pub fn from_style(length: LengthPercentageOrAuto, containing_length: Au) -> MaybeAuto {
- match length {
- LengthPercentageOrAuto::Auto => MaybeAuto::Auto,
- LengthPercentageOrAuto::LengthPercentage(ref lp) => {
- MaybeAuto::Specified(lp.to_used_value(containing_length))
- },
- }
- }
-
- #[inline]
- pub fn from_option(au: Option<Au>) -> MaybeAuto {
- match au {
- Some(l) => MaybeAuto::Specified(l),
- _ => MaybeAuto::Auto,
- }
- }
-
- #[inline]
- pub fn to_option(&self) -> Option<Au> {
- match *self {
- MaybeAuto::Specified(value) => Some(value),
- MaybeAuto::Auto => None,
- }
- }
-
- #[inline]
- pub fn specified_or_default(&self, default: Au) -> Au {
- match *self {
- MaybeAuto::Auto => default,
- MaybeAuto::Specified(value) => value,
- }
- }
-
- #[inline]
- pub fn specified_or_zero(&self) -> Au {
- self.specified_or_default(Au::new(0))
- }
-
- #[inline]
- pub fn is_auto(&self) -> bool {
- match *self {
- MaybeAuto::Auto => true,
- MaybeAuto::Specified(..) => false,
- }
- }
-
- #[inline]
- pub fn map<F>(&self, mapper: F) -> MaybeAuto
- where
- F: FnOnce(Au) -> Au,
- {
- match *self {
- MaybeAuto::Auto => MaybeAuto::Auto,
- MaybeAuto::Specified(value) => MaybeAuto::Specified(mapper(value)),
- }
- }
-}
-
-/// 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: Size, container_size: Option<Au>) -> MaybeAuto {
- match style_length {
- Size::Auto => MaybeAuto::Auto,
- Size::LengthPercentage(ref lp) => {
- MaybeAuto::from_option(lp.0.maybe_to_used_value(container_size))
- },
- }
-}
-
-#[inline]
-pub fn padding_from_style(
- style: &ComputedValues,
- containing_block_inline_size: Au,
- writing_mode: WritingMode,
-) -> LogicalMargin<Au> {
- let padding_style = style.get_padding();
- LogicalMargin::from_physical(
- writing_mode,
- SideOffsets2D::new(
- padding_style
- .padding_top
- .to_used_value(containing_block_inline_size),
- padding_style
- .padding_right
- .to_used_value(containing_block_inline_size),
- padding_style
- .padding_bottom
- .to_used_value(containing_block_inline_size),
- padding_style
- .padding_left
- .to_used_value(containing_block_inline_size),
- ),
- )
-}
-
-/// Returns the explicitly-specified margin lengths from the given style. Percentage and auto
-/// margins are returned as zero.
-///
-/// This is used when calculating intrinsic inline sizes.
-#[inline]
-pub fn specified_margin_from_style(
- style: &ComputedValues,
- writing_mode: WritingMode,
-) -> LogicalMargin<Au> {
- let margin_style = style.get_margin();
- LogicalMargin::from_physical(
- writing_mode,
- SideOffsets2D::new(
- MaybeAuto::from_style(margin_style.margin_top, Au(0)).specified_or_zero(),
- MaybeAuto::from_style(margin_style.margin_right, Au(0)).specified_or_zero(),
- MaybeAuto::from_style(margin_style.margin_bottom, Au(0)).specified_or_zero(),
- MaybeAuto::from_style(margin_style.margin_left, Au(0)).specified_or_zero(),
- ),
- )
-}
-
-/// 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, Serialize)]
-pub struct SizeConstraint {
- min_size: Au,
- max_size: Option<Au>,
-}
-
-impl SizeConstraint {
- /// Create a `SizeConstraint` for an axis.
- pub fn new(
- container_size: Option<Au>,
- min_size: Size,
- max_size: MaxSize,
- border: Option<Au>,
- ) -> SizeConstraint {
- let mut min_size = match min_size {
- Size::Auto => Au(0),
- Size::LengthPercentage(ref lp) => {
- lp.maybe_to_used_value(container_size).unwrap_or(Au(0))
- },
- };
-
- let mut max_size = match max_size {
- MaxSize::None => None,
- MaxSize::LengthPercentage(ref lp) => lp.maybe_to_used_value(container_size),
- };
-
- // 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)));
- }
-
- SizeConstraint { min_size, max_size }
- }
-
- /// Clamp the given size by the given min size and max size constraint.
- pub fn clamp(&self, other: Au) -> Au {
- if other < self.min_size {
- self.min_size
- } else {
- match self.max_size {
- Some(max_size) if max_size < other => max_size,
- _ => other,
- }
- }
- }
-}
diff --git a/components/layout_2020/multicol.rs b/components/layout_2020/multicol.rs
deleted file mode 100644
index d49e5f1d79f..00000000000
--- a/components/layout_2020/multicol.rs
+++ /dev/null
@@ -1,384 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS Multi-column layout http://dev.w3.org/csswg/css-multicol/
-
-use crate::block::BlockFlow;
-use crate::context::LayoutContext;
-use crate::display_list::{DisplayListBuildState, StackingContextCollectionState};
-use crate::floats::FloatKind;
-use crate::flow::{Flow, FlowClass, FragmentationContext, GetBaseFlow, OpaqueFlow};
-use crate::fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
-use crate::ServoArc;
-use app_units::Au;
-use euclid::{Point2D, Vector2D};
-use gfx_traits::print_tree::PrintTree;
-use std::cmp::{max, min};
-use std::fmt;
-use std::sync::Arc;
-use style::logical_geometry::LogicalSize;
-use style::properties::ComputedValues;
-use style::values::computed::length::{
- MaxSize, NonNegativeLengthOrAuto, NonNegativeLengthPercentageOrNormal, Size,
-};
-use style::values::generics::column::ColumnCount;
-
-#[allow(unsafe_code)]
-unsafe impl crate::flow::HasBaseFlow for MulticolFlow {}
-
-#[repr(C)]
-pub struct MulticolFlow {
- pub block_flow: BlockFlow,
-
- /// Length between the inline-start edge of a column and that of the next.
- /// That is, the used column-width + used column-gap.
- pub column_pitch: Au,
-}
-
-#[allow(unsafe_code)]
-unsafe impl crate::flow::HasBaseFlow for MulticolColumnFlow {}
-
-#[repr(C)]
-pub struct MulticolColumnFlow {
- pub block_flow: BlockFlow,
-}
-
-impl MulticolFlow {
- pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> MulticolFlow {
- MulticolFlow {
- block_flow: BlockFlow::from_fragment_and_float_kind(fragment, float_kind),
- column_pitch: Au(0),
- }
- }
-}
-
-impl MulticolColumnFlow {
- pub fn from_fragment(fragment: Fragment) -> MulticolColumnFlow {
- MulticolColumnFlow {
- block_flow: BlockFlow::from_fragment(fragment),
- }
- }
-}
-
-impl Flow for MulticolFlow {
- fn class(&self) -> FlowClass {
- FlowClass::Multicol
- }
-
- fn as_mut_block(&mut self) -> &mut BlockFlow {
- &mut self.block_flow
- }
-
- fn as_block(&self) -> &BlockFlow {
- &self.block_flow
- }
-
- fn bubble_inline_sizes(&mut self) {
- // FIXME(SimonSapin) http://dev.w3.org/csswg/css-sizing/#multicol-intrinsic
- self.block_flow.bubble_inline_sizes();
- }
-
- fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
- debug!(
- "assign_inline_sizes({}): assigning inline_size for flow",
- "multicol"
- );
- let shared_context = layout_context.shared_context();
- self.block_flow.compute_inline_sizes(shared_context);
-
- // Move in from the inline-start border edge.
- let inline_start_content_edge = self.block_flow.fragment.border_box.start.i +
- self.block_flow.fragment.border_padding.inline_start;
-
- // Distance from the inline-end margin edge to the inline-end content edge.
- let inline_end_content_edge = self.block_flow.fragment.margin.inline_end +
- self.block_flow.fragment.border_padding.inline_end;
-
- self.block_flow.assign_inline_sizes(layout_context);
- let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
- let content_inline_size =
- self.block_flow.fragment.border_box.size.inline - padding_and_borders;
- let column_width;
- {
- let style = &self.block_flow.fragment.style;
- let column_gap = match style.get_position().column_gap {
- NonNegativeLengthPercentageOrNormal::LengthPercentage(len) => {
- len.0.to_pixel_length(content_inline_size).into()
- },
- NonNegativeLengthPercentageOrNormal::Normal => {
- self.block_flow.fragment.style.get_font().font_size.size()
- },
- };
-
- let column_style = style.get_column();
- let mut column_count;
- if let NonNegativeLengthOrAuto::LengthPercentage(column_width) =
- column_style.column_width
- {
- let column_width = Au::from(column_width);
- column_count = max(
- 1,
- (content_inline_size + column_gap).0 / (column_width + column_gap).0,
- );
- if let ColumnCount::Integer(specified_column_count) = column_style.column_count {
- column_count = min(column_count, specified_column_count.0 as i32);
- }
- } else {
- column_count = match column_style.column_count {
- ColumnCount::Integer(n) => n.0,
- _ => unreachable!(),
- }
- }
- column_width = max(
- Au(0),
- (content_inline_size + column_gap) / column_count - column_gap,
- );
- self.column_pitch = column_width + column_gap;
- }
-
- self.block_flow.fragment.border_box.size.inline = content_inline_size + padding_and_borders;
-
- self.block_flow.propagate_assigned_inline_size_to_children(
- shared_context,
- inline_start_content_edge,
- inline_end_content_edge,
- column_width,
- |_, _, _, _, _, _| {},
- );
- }
-
- fn assign_block_size(&mut self, ctx: &LayoutContext) {
- debug!("assign_block_size: assigning block_size for multicol");
-
- let fragmentation_context = Some(FragmentationContext {
- this_fragment_is_empty: true,
- available_block_size: {
- let style = &self.block_flow.fragment.style;
- let size = match style.content_block_size() {
- Size::Auto => None,
- Size::LengthPercentage(ref lp) => lp.maybe_to_used_value(None),
- };
- let size = size.or_else(|| match style.max_block_size() {
- MaxSize::None => None,
- MaxSize::LengthPercentage(ref lp) => lp.maybe_to_used_value(None),
- });
-
- size.unwrap_or_else(|| {
- // FIXME: do column balancing instead
- // FIXME: (until column balancing) substract margins/borders/padding
- LogicalSize::from_physical(
- self.block_flow.base.writing_mode,
- ctx.shared_context().viewport_size(),
- )
- .block
- })
- },
- });
-
- // Before layout, everything is in a single "column"
- assert_eq!(self.block_flow.base.children.len(), 1);
- let mut column = self.block_flow.base.children.pop_front_arc().unwrap();
-
- // Pretend there is no children for this:
- self.block_flow.assign_block_size(ctx);
-
- loop {
- let remaining = Arc::get_mut(&mut column)
- .unwrap()
- .fragment(ctx, fragmentation_context);
- self.block_flow.base.children.push_back_arc(column);
- column = match remaining {
- Some(remaining) => remaining,
- None => break,
- };
- }
- }
-
- fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
- self.block_flow
- .compute_stacking_relative_position(layout_context);
- let pitch = LogicalSize::new(self.block_flow.base.writing_mode, self.column_pitch, Au(0));
- let pitch = pitch.to_physical(self.block_flow.base.writing_mode);
- for (i, child) in self.block_flow.base.children.iter_mut().enumerate() {
- let point = &mut child.mut_base().stacking_relative_position;
- *point = *point + Vector2D::new(pitch.width * i as i32, pitch.height * i as i32);
- }
- }
-
- fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
- self.block_flow
- .update_late_computed_inline_position_if_necessary(inline_position)
- }
-
- fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
- self.block_flow
- .update_late_computed_block_position_if_necessary(block_position)
- }
-
- fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
- debug!("build_display_list_multicol");
- self.block_flow.build_display_list(state);
- }
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
- self.block_flow.collect_stacking_contexts(state);
- }
-
- fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) {
- self.block_flow.repair_style(new_style)
- }
-
- fn compute_overflow(&self) -> Overflow {
- self.block_flow.compute_overflow()
- }
-
- fn contains_roots_of_absolute_flow_tree(&self) -> bool {
- self.block_flow.contains_roots_of_absolute_flow_tree()
- }
-
- fn is_absolute_containing_block(&self) -> bool {
- self.block_flow.is_absolute_containing_block()
- }
-
- fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
- self.block_flow.generated_containing_block_size(flow)
- }
-
- fn iterate_through_fragment_border_boxes(
- &self,
- iterator: &mut dyn FragmentBorderBoxIterator,
- level: i32,
- stacking_context_position: &Point2D<Au>,
- ) {
- self.block_flow.iterate_through_fragment_border_boxes(
- iterator,
- level,
- stacking_context_position,
- );
- }
-
- fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
- self.block_flow.mutate_fragments(mutator);
- }
-
- fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
- self.block_flow.print_extra_flow_children(print_tree);
- }
-}
-
-impl Flow for MulticolColumnFlow {
- fn class(&self) -> FlowClass {
- FlowClass::MulticolColumn
- }
-
- fn as_mut_block(&mut self) -> &mut BlockFlow {
- &mut self.block_flow
- }
-
- fn as_block(&self) -> &BlockFlow {
- &self.block_flow
- }
-
- fn bubble_inline_sizes(&mut self) {
- self.block_flow.bubble_inline_sizes();
- }
-
- fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
- debug!(
- "assign_inline_sizes({}): assigning inline_size for flow",
- "multicol column"
- );
- self.block_flow.assign_inline_sizes(layout_context);
- }
-
- fn assign_block_size(&mut self, ctx: &LayoutContext) {
- debug!("assign_block_size: assigning block_size for multicol column");
- self.block_flow.assign_block_size(ctx);
- }
-
- fn fragment(
- &mut self,
- layout_context: &LayoutContext,
- fragmentation_context: Option<FragmentationContext>,
- ) -> Option<Arc<dyn Flow>> {
- Flow::fragment(&mut self.block_flow, layout_context, fragmentation_context)
- }
-
- fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
- self.block_flow
- .compute_stacking_relative_position(layout_context)
- }
-
- fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
- self.block_flow
- .update_late_computed_inline_position_if_necessary(inline_position)
- }
-
- fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
- self.block_flow
- .update_late_computed_block_position_if_necessary(block_position)
- }
-
- fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
- debug!("build_display_list_multicol column");
- self.block_flow.build_display_list(state);
- }
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
- self.block_flow.collect_stacking_contexts(state);
- }
-
- fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) {
- self.block_flow.repair_style(new_style)
- }
-
- fn compute_overflow(&self) -> Overflow {
- self.block_flow.compute_overflow()
- }
-
- fn contains_roots_of_absolute_flow_tree(&self) -> bool {
- self.block_flow.contains_roots_of_absolute_flow_tree()
- }
-
- fn is_absolute_containing_block(&self) -> bool {
- self.block_flow.is_absolute_containing_block()
- }
-
- fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
- self.block_flow.generated_containing_block_size(flow)
- }
-
- fn iterate_through_fragment_border_boxes(
- &self,
- iterator: &mut dyn FragmentBorderBoxIterator,
- level: i32,
- stacking_context_position: &Point2D<Au>,
- ) {
- self.block_flow.iterate_through_fragment_border_boxes(
- iterator,
- level,
- stacking_context_position,
- );
- }
-
- fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
- self.block_flow.mutate_fragments(mutator);
- }
-
- fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
- self.block_flow.print_extra_flow_children(print_tree);
- }
-}
-
-impl fmt::Debug for MulticolFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "MulticolFlow: {:?}", self.block_flow)
- }
-}
-
-impl fmt::Debug for MulticolColumnFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "MulticolColumnFlow: {:?}", self.block_flow)
- }
-}
diff --git a/components/layout_2020/opaque_node.rs b/components/layout_2020/opaque_node.rs
index b48820052e9..3d56ef21dae 100644
--- a/components/layout_2020/opaque_node.rs
+++ b/components/layout_2020/opaque_node.rs
@@ -7,8 +7,6 @@ use libc::c_void;
use script_traits::UntrustedNodeAddress;
pub trait OpaqueNodeMethods {
- /// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type
- /// of node that script expects to receive in a hit test.
fn to_untrusted_node_address(&self) -> UntrustedNodeAddress;
}
diff --git a/components/layout_2020/parallel.rs b/components/layout_2020/parallel.rs
deleted file mode 100644
index 4ad10b3db11..00000000000
--- a/components/layout_2020/parallel.rs
+++ /dev/null
@@ -1,232 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Implements parallel traversals over the DOM and flow trees.
-//!
-//! This code is highly unsafe. Keep this file small and easy to audit.
-
-#![allow(unsafe_code)]
-
-use crate::block::BlockFlow;
-use crate::context::LayoutContext;
-use crate::flow::{Flow, GetBaseFlow};
-use crate::flow_ref::FlowRef;
-use crate::traversal::{AssignBSizes, AssignISizes, BubbleISizes};
-use crate::traversal::{PostorderFlowTraversal, PreorderFlowTraversal};
-use profile_traits::time::{self, profile, TimerMetadata};
-use servo_config::opts;
-use smallvec::SmallVec;
-use std::mem;
-use std::ptr;
-use std::sync::atomic::{AtomicIsize, Ordering};
-
-/// Traversal chunk size.
-const CHUNK_SIZE: usize = 16;
-
-pub type FlowList = SmallVec<[UnsafeFlow; CHUNK_SIZE]>;
-
-/// Vtable + pointer representation of a Flow trait object.
-#[derive(Clone, Copy, Eq, PartialEq)]
-pub struct UnsafeFlow(*const dyn Flow);
-
-unsafe impl Sync for UnsafeFlow {}
-unsafe impl Send for UnsafeFlow {}
-
-fn null_unsafe_flow() -> UnsafeFlow {
- UnsafeFlow(ptr::null::<BlockFlow>())
-}
-
-pub fn mut_owned_flow_to_unsafe_flow(flow: *mut FlowRef) -> UnsafeFlow {
- unsafe { UnsafeFlow(&**flow) }
-}
-
-/// Information that we need stored in each flow.
-pub struct FlowParallelInfo {
- /// The number of children that still need work done.
- pub children_count: AtomicIsize,
- /// The address of the parent flow.
- pub parent: UnsafeFlow,
-}
-
-impl FlowParallelInfo {
- pub fn new() -> FlowParallelInfo {
- FlowParallelInfo {
- children_count: AtomicIsize::new(0),
- parent: null_unsafe_flow(),
- }
- }
-}
-
-/// Process current flow and potentially traverse its ancestors.
-///
-/// If we are the last child that finished processing, recursively process
-/// our parent. Else, stop. Also, stop at the root.
-///
-/// Thus, if we start with all the leaves of a tree, we end up traversing
-/// the whole tree bottom-up because each parent will be processed exactly
-/// once (by the last child that finishes processing).
-///
-/// The only communication between siblings is that they both
-/// fetch-and-subtract the parent's children count.
-fn bottom_up_flow(mut unsafe_flow: UnsafeFlow, assign_bsize_traversal: &AssignBSizes) {
- loop {
- // Get a real flow.
- let flow: &mut dyn Flow = unsafe { mem::transmute(unsafe_flow) };
-
- // Perform the appropriate traversal.
- if assign_bsize_traversal.should_process(flow) {
- assign_bsize_traversal.process(flow);
- }
-
- let base = flow.mut_base();
-
- // Reset the count of children for the next layout traversal.
- base.parallel
- .children_count
- .store(base.children.len() as isize, Ordering::Relaxed);
-
- // Possibly enqueue the parent.
- let unsafe_parent = base.parallel.parent;
- if unsafe_parent == null_unsafe_flow() {
- // We're done!
- break;
- }
-
- // No, we're not at the root yet. Then are we the last child
- // of our parent to finish processing? If so, we can continue
- // on with our parent; otherwise, we've gotta wait.
- let parent: &mut dyn Flow = unsafe { &mut *(unsafe_parent.0 as *mut dyn Flow) };
- let parent_base = parent.mut_base();
- if parent_base
- .parallel
- .children_count
- .fetch_sub(1, Ordering::Relaxed) ==
- 1
- {
- // We were the last child of our parent. Reflow our parent.
- unsafe_flow = unsafe_parent
- } else {
- // Stop.
- break;
- }
- }
-}
-
-fn top_down_flow<'scope>(
- unsafe_flows: &[UnsafeFlow],
- pool: &'scope rayon::ThreadPool,
- scope: &rayon::ScopeFifo<'scope>,
- assign_isize_traversal: &'scope AssignISizes,
- assign_bsize_traversal: &'scope AssignBSizes,
-) {
- let mut discovered_child_flows = FlowList::new();
-
- for unsafe_flow in unsafe_flows {
- let mut had_children = false;
- unsafe {
- // Get a real flow.
- let flow: &mut dyn Flow = mem::transmute(*unsafe_flow);
- flow.mut_base().thread_id = pool.current_thread_index().unwrap() as u8;
-
- if assign_isize_traversal.should_process(flow) {
- // Perform the appropriate traversal.
- assign_isize_traversal.process(flow);
- }
-
- // Possibly enqueue the children.
- for kid in flow.mut_base().child_iter_mut() {
- had_children = true;
- discovered_child_flows.push(UnsafeFlow(kid));
- }
- }
-
- // If there were no more children, start assigning block-sizes.
- if !had_children {
- bottom_up_flow(*unsafe_flow, &assign_bsize_traversal)
- }
- }
-
- if discovered_child_flows.is_empty() {
- return;
- }
-
- if discovered_child_flows.len() <= CHUNK_SIZE {
- // We can handle all the children in this work unit.
- top_down_flow(
- &discovered_child_flows,
- pool,
- scope,
- &assign_isize_traversal,
- &assign_bsize_traversal,
- );
- } else {
- // Spawn a new work unit for each chunk after the first.
- let mut chunks = discovered_child_flows.chunks(CHUNK_SIZE);
- let first_chunk = chunks.next();
- for chunk in chunks {
- let nodes = chunk.iter().cloned().collect::<FlowList>();
- scope.spawn_fifo(move |scope| {
- top_down_flow(
- &nodes,
- pool,
- scope,
- &assign_isize_traversal,
- &assign_bsize_traversal,
- );
- });
- }
- if let Some(chunk) = first_chunk {
- top_down_flow(
- chunk,
- pool,
- scope,
- &assign_isize_traversal,
- &assign_bsize_traversal,
- );
- }
- }
-}
-
-/// Run the main layout passes in parallel.
-pub fn reflow(
- root: &mut dyn Flow,
- profiler_metadata: Option<TimerMetadata>,
- time_profiler_chan: time::ProfilerChan,
- context: &LayoutContext,
- queue: &rayon::ThreadPool,
-) {
- if opts::get().bubble_inline_sizes_separately {
- let bubble_inline_sizes = BubbleISizes {
- layout_context: &context,
- };
- bubble_inline_sizes.traverse(root);
- }
-
- let assign_isize_traversal = &AssignISizes {
- layout_context: &context,
- };
- let assign_bsize_traversal = &AssignBSizes {
- layout_context: &context,
- };
- let nodes = [UnsafeFlow(root)];
-
- queue.install(move || {
- rayon::scope_fifo(move |scope| {
- profile(
- time::ProfilerCategory::LayoutParallelWarmup,
- profiler_metadata,
- time_profiler_chan,
- move || {
- top_down_flow(
- &nodes,
- queue,
- scope,
- assign_isize_traversal,
- assign_bsize_traversal,
- );
- },
- );
- });
- });
-}
diff --git a/components/layout_2020/persistent_list.rs b/components/layout_2020/persistent_list.rs
deleted file mode 100644
index 16bbc319ea0..00000000000
--- a/components/layout_2020/persistent_list.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A persistent, thread-safe singly-linked list.
-
-use std::sync::Arc;
-
-pub struct PersistentList<T> {
- head: PersistentListLink<T>,
- length: usize,
-}
-
-struct PersistentListEntry<T> {
- value: T,
- next: PersistentListLink<T>,
-}
-
-type PersistentListLink<T> = Option<Arc<PersistentListEntry<T>>>;
-
-impl<T> PersistentList<T>
-where
- T: Send + Sync,
-{
- #[inline]
- pub fn new() -> PersistentList<T> {
- PersistentList {
- head: None,
- length: 0,
- }
- }
-
- #[inline]
- pub fn len(&self) -> usize {
- self.length
- }
-
- #[inline]
- pub fn front(&self) -> Option<&T> {
- self.head.as_ref().map(|head| &head.value)
- }
-
- #[inline]
- pub fn prepend_elem(&self, value: T) -> PersistentList<T> {
- PersistentList {
- head: Some(Arc::new(PersistentListEntry {
- value: value,
- next: self.head.clone(),
- })),
- length: self.length + 1,
- }
- }
-
- #[inline]
- pub fn iter(&self) -> PersistentListIterator<T> {
- // This could clone (and would not need the lifetime if it did), but then it would incur
- // atomic operations on every call to `.next()`. Bad.
- PersistentListIterator {
- entry: self.head.as_ref().map(|head| &**head),
- }
- }
-}
-
-impl<T> Clone for PersistentList<T>
-where
- T: Send + Sync,
-{
- fn clone(&self) -> PersistentList<T> {
- // This establishes the persistent nature of this list: we can clone a list by just cloning
- // its head.
- PersistentList {
- head: self.head.clone(),
- length: self.length,
- }
- }
-}
-
-pub struct PersistentListIterator<'a, T>
-where
- T: Send + Sync,
-{
- entry: Option<&'a PersistentListEntry<T>>,
-}
-
-impl<'a, T> Iterator for PersistentListIterator<'a, T>
-where
- T: Send + Sync + 'static,
-{
- type Item = &'a T;
-
- #[inline]
- fn next(&mut self) -> Option<&'a T> {
- let entry = self.entry?;
- let value = &entry.value;
- self.entry = match entry.next {
- None => None,
- Some(ref entry) => Some(&**entry),
- };
- Some(value)
- }
-}
diff --git a/components/layout_2020/query.rs b/components/layout_2020/query.rs
index 789056753d5..2dfb2eebaa0 100644
--- a/components/layout_2020/query.rs
+++ b/components/layout_2020/query.rs
@@ -4,16 +4,11 @@
//! Utilities for querying the layout, as needed by the layout thread.
-use crate::construct::ConstructionResult;
use crate::context::LayoutContext;
use crate::display_list::items::{DisplayList, OpaqueNode, ScrollOffsetMap};
use crate::display_list::IndexableText;
-use crate::flow::{Flow, GetBaseFlow};
-use crate::fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
-use crate::inline::InlineFragmentNodeFlags;
+use crate::fragment::{Fragment, FragmentBorderBoxIterator};
use crate::opaque_node::OpaqueNodeMethods;
-use crate::sequential;
-use crate::wrapper::LayoutNodeLayoutData;
use app_units::Au;
use euclid::{Point2D, Rect, Size2D, Vector2D};
use ipc_channel::ipc::IpcSender;
@@ -30,7 +25,6 @@ use script_layout_interface::{LayoutElementType, LayoutNodeType};
use script_traits::LayoutMsg as ConstellationMsg;
use script_traits::UntrustedNodeAddress;
use std::cmp::{max, min};
-use std::ops::Deref;
use std::sync::{Arc, Mutex};
use style::computed_values::display::T as Display;
use style::computed_values::position::T as Position;
@@ -366,24 +360,16 @@ impl FragmentBorderBoxIterator for MarginRetrievingFragmentBorderBoxIterator {
pub fn process_content_box_request(
requested_node: OpaqueNode,
- layout_root: &mut dyn Flow,
) -> Option<Rect<Au>> {
- // FIXME(pcwalton): This has not been updated to handle the stacking context relative
- // stuff. So the position is wrong in most cases.
- let mut iterator = UnioningFragmentBorderBoxIterator::new(requested_node);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
- iterator.rect
+ UnioningFragmentBorderBoxIterator::new(requested_node).rect
}
pub fn process_content_boxes_request(
requested_node: OpaqueNode,
- layout_root: &mut dyn Flow,
) -> Vec<Rect<Au>> {
// FIXME(pcwalton): This has not been updated to handle the stacking context relative
// stuff. So the position is wrong in most cases.
- let mut iterator = CollectingFragmentBorderBoxIterator::new(requested_node);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
- iterator.rects
+ CollectingFragmentBorderBoxIterator::new(requested_node).rects
}
struct FragmentLocatingFragmentIterator {
@@ -581,45 +567,6 @@ impl FragmentBorderBoxIterator for ParentOffsetBorderBoxIterator {
if fragment.style.get_box().position == Position::Fixed {
self.parent_nodes.clear();
}
- } else if let Some(node) = fragment.inline_context.as_ref().and_then(|inline_context| {
- inline_context
- .nodes
- .iter()
- .find(|node| node.address == self.node_address)
- }) {
- // TODO: Handle cases where the `offsetParent` is an inline
- // element. This will likely be impossible until
- // https://github.com/servo/servo/issues/13982 is fixed.
-
- // Found a fragment in the flow tree whose inline context
- // contains the DOM node we're looking for, i.e. the node
- // is inline and contains this fragment.
- match self.node_offset_box {
- Some(NodeOffsetBoxInfo {
- ref mut rectangle, ..
- }) => {
- *rectangle = rectangle.union(border_box);
- },
- None => {
- // https://github.com/servo/servo/issues/13982 will
- // cause this assertion to fail sometimes, so it's
- // commented out for now.
- /*assert!(node.flags.contains(FIRST_FRAGMENT_OF_ELEMENT),
- "First fragment of inline node found wasn't its first fragment!");*/
-
- self.node_offset_box = Some(NodeOffsetBoxInfo {
- offset: border_box.origin,
- rectangle: *border_box,
- });
- },
- }
-
- if node
- .flags
- .contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT)
- {
- self.has_processed_node = true;
- }
} else if self.node_offset_box.is_none() {
// TODO(gw): Is there a less fragile way of checking whether this
// fragment is the body element, rather than just checking that
@@ -636,8 +583,6 @@ impl FragmentBorderBoxIterator for ParentOffsetBorderBoxIterator {
// 2) Is static position *and* is a table or table cell
// 3) Is not static position
(true, _, _) |
- (false, Position::Static, &SpecificFragmentInfo::Table) |
- (false, Position::Static, &SpecificFragmentInfo::TableCell) |
(false, Position::Sticky, _) |
(false, Position::Absolute, _) |
(false, Position::Relative, _) |
@@ -671,11 +616,8 @@ impl FragmentBorderBoxIterator for ParentOffsetBorderBoxIterator {
pub fn process_node_geometry_request(
requested_node: OpaqueNode,
- layout_root: &mut dyn Flow,
) -> Rect<i32> {
- let mut iterator = FragmentLocatingFragmentIterator::new(requested_node);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
- iterator.client_rect
+ FragmentLocatingFragmentIterator::new(requested_node).client_rect
}
pub fn process_node_scroll_id_request<N: LayoutNode>(
@@ -689,10 +631,8 @@ pub fn process_node_scroll_id_request<N: LayoutNode>(
/// https://drafts.csswg.org/cssom-view/#scrolling-area
pub fn process_node_scroll_area_request(
requested_node: OpaqueNode,
- layout_root: &mut dyn Flow,
) -> Rect<i32> {
- let mut iterator = UnioningFragmentScrollAreaIterator::new(requested_node);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
+ let iterator = UnioningFragmentScrollAreaIterator::new(requested_node);
match iterator.overflow_direction {
OverflowDirection::RightAndDown => {
let right = max(
@@ -742,7 +682,6 @@ pub fn process_resolved_style_request<'a, N>(
node: N,
pseudo: &Option<PseudoElement>,
property: &PropertyId,
- layout_root: &mut dyn Flow,
) -> String
where
N: LayoutNode,
@@ -755,7 +694,7 @@ where
// We call process_resolved_style_request after performing a whole-document
// traversal, so in the common case, the element is styled.
if element.get_data().is_some() {
- return process_resolved_style_request_internal(node, pseudo, property, layout_root);
+ return process_resolved_style_request_internal(node, pseudo, property);
}
// In a display: none subtree. No pseudo-element exists.
@@ -791,7 +730,6 @@ fn process_resolved_style_request_internal<'a, N>(
requested_node: N,
pseudo: &Option<PseudoElement>,
property: &PropertyId,
- layout_root: &mut dyn Flow,
) -> String
where
N: LayoutNode,
@@ -842,24 +780,11 @@ where
let applies = true;
fn used_value_for_position_property<N: LayoutNode>(
- layout_el: <N::ConcreteThreadSafeLayoutNode as ThreadSafeLayoutNode>::ConcreteThreadSafeLayoutElement,
- layout_root: &mut dyn Flow,
+ _layout_el: <N::ConcreteThreadSafeLayoutNode as ThreadSafeLayoutNode>::ConcreteThreadSafeLayoutElement,
requested_node: N,
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 {
- ConstructionResult::Flow(ref flow_ref, _) => flow_ref
- .deref()
- .base()
- .stacking_relative_position
- .to_point(),
- // TODO(dzbarsky) search parents until we find node with a flow ref.
- // https://github.com/servo/servo/issues/8307
- _ => Point2D::zero(),
- }
- });
+ let position = Point2D::zero();
let property = match longhand_id {
LonghandId::Bottom => PositionProperty::Bottom,
LonghandId::Top => PositionProperty::Top,
@@ -869,12 +794,11 @@ where
LonghandId::Height => PositionProperty::Height,
_ => unreachable!(),
};
- let mut iterator = PositionRetrievingFragmentBorderBoxIterator::new(
+ let iterator = PositionRetrievingFragmentBorderBoxIterator::new(
requested_node.opaque(),
property,
position,
);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
iterator
.result
.map(|r| r.to_css_string())
@@ -904,13 +828,12 @@ where
LonghandId::PaddingRight => (MarginPadding::Padding, Side::Right),
_ => unreachable!(),
};
- let mut iterator = MarginRetrievingFragmentBorderBoxIterator::new(
+ let iterator = MarginRetrievingFragmentBorderBoxIterator::new(
requested_node.opaque(),
side,
margin_padding,
style.writing_mode,
);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
iterator
.result
.map(|r| r.to_css_string())
@@ -920,12 +843,12 @@ where
LonghandId::Bottom | LonghandId::Top | LonghandId::Right | LonghandId::Left
if applies && positioned && style.get_box().display != Display::None =>
{
- used_value_for_position_property(layout_el, layout_root, requested_node, longhand_id)
+ used_value_for_position_property(layout_el, requested_node, longhand_id)
},
LonghandId::Width | LonghandId::Height
if applies && style.get_box().display != Display::None =>
{
- used_value_for_position_property(layout_el, layout_root, requested_node, longhand_id)
+ used_value_for_position_property(layout_el, requested_node, longhand_id)
},
// FIXME: implement used value computation for line-height
_ => style.computed_value_to_string(PropertyDeclarationId::Longhand(longhand_id)),
@@ -934,10 +857,8 @@ where
pub fn process_offset_parent_query(
requested_node: OpaqueNode,
- layout_root: &mut dyn Flow,
) -> OffsetParentResponse {
- let mut iterator = ParentOffsetBorderBoxIterator::new(requested_node);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
+ let iterator = ParentOffsetBorderBoxIterator::new(requested_node);
let node_offset_box = iterator.node_offset_box;
let parent_info = iterator
diff --git a/components/layout_2020/sequential.rs b/components/layout_2020/sequential.rs
deleted file mode 100644
index 16dd3b8d4da..00000000000
--- a/components/layout_2020/sequential.rs
+++ /dev/null
@@ -1,193 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Implements sequential traversals over the DOM and flow trees.
-
-use crate::context::LayoutContext;
-use crate::display_list::items::{self, CommonDisplayItem, DisplayItem, DisplayListSection};
-use crate::display_list::{DisplayListBuildState, StackingContextCollectionState};
-use crate::floats::SpeculatedFloatPlacement;
-use crate::flow::{Flow, FlowFlags, GetBaseFlow, ImmutableFlowUtils};
-use crate::fragment::{CoordinateSystem, FragmentBorderBoxIterator};
-use crate::generated_content::ResolveGeneratedContent;
-use crate::incremental::RelayoutMode;
-use crate::traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList};
-use crate::traversal::{InorderFlowTraversal, PostorderFlowTraversal, PreorderFlowTraversal};
-use app_units::Au;
-use euclid::{Point2D, Rect, Size2D, Vector2D};
-use servo_config::opts;
-use style::servo::restyle_damage::ServoRestyleDamage;
-use webrender_api::units::LayoutPoint;
-
-pub fn resolve_generated_content(root: &mut dyn Flow, layout_context: &LayoutContext) {
- ResolveGeneratedContent::new(&layout_context).traverse(root, 0);
-}
-
-/// Run the main layout passes sequentially.
-pub fn reflow(root: &mut dyn Flow, layout_context: &LayoutContext, relayout_mode: RelayoutMode) {
- fn doit(
- flow: &mut dyn Flow,
- assign_inline_sizes: AssignISizes,
- assign_block_sizes: AssignBSizes,
- relayout_mode: RelayoutMode,
- ) {
- // Force reflow children during this traversal. This is needed when we failed
- // the float speculation of a block formatting context and need to fix it.
- if relayout_mode == RelayoutMode::Force {
- flow.mut_base()
- .restyle_damage
- .insert(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
- }
-
- if assign_inline_sizes.should_process(flow) {
- assign_inline_sizes.process(flow);
- }
-
- for kid in flow.mut_base().child_iter_mut() {
- doit(kid, assign_inline_sizes, assign_block_sizes, relayout_mode);
- }
-
- if assign_block_sizes.should_process(flow) {
- assign_block_sizes.process(flow);
- }
- }
-
- if opts::get().bubble_inline_sizes_separately {
- let bubble_inline_sizes = BubbleISizes {
- layout_context: &layout_context,
- };
- bubble_inline_sizes.traverse(root);
- }
-
- let assign_inline_sizes = AssignISizes {
- layout_context: &layout_context,
- };
- let assign_block_sizes = AssignBSizes {
- layout_context: &layout_context,
- };
-
- doit(root, assign_inline_sizes, assign_block_sizes, relayout_mode);
-}
-
-pub fn build_display_list_for_subtree<'a>(
- flow_root: &mut dyn Flow,
- layout_context: &'a LayoutContext,
- background_color: webrender_api::ColorF,
- client_size: Size2D<Au>,
-) -> DisplayListBuildState<'a> {
- let mut state = StackingContextCollectionState::new(layout_context.id);
- flow_root.collect_stacking_contexts(&mut state);
-
- let mut state = DisplayListBuildState::new(layout_context, state);
-
- // Create a base rectangle for the page background based on the root
- // background color.
- let base = state.create_base_display_item(
- Rect::new(Point2D::new(Au::new(0), Au::new(0)), client_size),
- flow_root.as_block().fragment.node,
- None,
- DisplayListSection::BackgroundAndBorders,
- );
- state.add_display_item(DisplayItem::Rectangle(CommonDisplayItem::new(
- base,
- webrender_api::RectangleDisplayItem {
- color: background_color,
- common: items::empty_common_item_properties(),
- },
- )));
-
- let mut build_display_list = BuildDisplayList { state: state };
- build_display_list.traverse(flow_root);
- build_display_list.state
-}
-
-pub fn iterate_through_flow_tree_fragment_border_boxes(
- root: &mut dyn Flow,
- iterator: &mut dyn FragmentBorderBoxIterator,
-) {
- fn doit(
- flow: &mut dyn Flow,
- level: i32,
- iterator: &mut dyn FragmentBorderBoxIterator,
- stacking_context_position: &Point2D<Au>,
- ) {
- flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position);
-
- for kid in flow.mut_base().child_iter_mut() {
- let mut stacking_context_position = *stacking_context_position;
- if kid.is_block_flow() && kid.as_block().fragment.establishes_stacking_context() {
- stacking_context_position =
- Point2D::new(kid.as_block().fragment.margin.inline_start, Au(0)) +
- kid.base().stacking_relative_position +
- stacking_context_position.to_vector();
- let relative_position = kid
- .as_block()
- .stacking_relative_border_box(CoordinateSystem::Own);
- if let Some(matrix) = kid.as_block().fragment.transform_matrix(&relative_position) {
- let transform_matrix = matrix.transform_point2d(&LayoutPoint::zero()).unwrap();
- stacking_context_position = stacking_context_position +
- Vector2D::new(
- Au::from_f32_px(transform_matrix.x),
- Au::from_f32_px(transform_matrix.y),
- )
- }
- }
- doit(kid, level + 1, iterator, &stacking_context_position);
- }
- }
-
- doit(root, 0, iterator, &Point2D::zero());
-}
-
-pub fn store_overflow(layout_context: &LayoutContext, flow: &mut dyn Flow) {
- if !flow
- .base()
- .restyle_damage
- .contains(ServoRestyleDamage::STORE_OVERFLOW)
- {
- return;
- }
-
- for kid in flow.mut_base().child_iter_mut() {
- store_overflow(layout_context, kid);
- }
-
- flow.store_overflow(layout_context);
-
- flow.mut_base()
- .restyle_damage
- .remove(ServoRestyleDamage::STORE_OVERFLOW);
-}
-
-/// Guesses how much inline size will be taken up by floats on the left and right sides of the
-/// given flow. This is needed to speculatively calculate the inline sizes of block formatting
-/// contexts. The speculation typically succeeds, but if it doesn't we have to lay it out again.
-pub fn guess_float_placement(flow: &mut dyn Flow) {
- if !flow
- .base()
- .restyle_damage
- .intersects(ServoRestyleDamage::REFLOW)
- {
- return;
- }
-
- let mut floats_in = SpeculatedFloatPlacement::compute_floats_in_for_first_child(flow);
- for kid in flow.mut_base().child_iter_mut() {
- if kid
- .base()
- .flags
- .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
- {
- // Do not propagate floats in or out, but do propogate between kids.
- guess_float_placement(kid);
- } else {
- floats_in.compute_floats_in(kid);
- kid.mut_base().speculated_float_placement_in = floats_in;
- guess_float_placement(kid);
- floats_in = kid.base().speculated_float_placement_out;
- }
- }
- floats_in.compute_floats_out(flow);
- flow.mut_base().speculated_float_placement_out = floats_in
-}
diff --git a/components/layout_2020/table.rs b/components/layout_2020/table.rs
deleted file mode 100644
index 9f6219f0980..00000000000
--- a/components/layout_2020/table.rs
+++ /dev/null
@@ -1,1378 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS table formatting contexts.
-
-use crate::block::{BlockFlow, CandidateBSizeIterator, ISizeAndMarginsComputer};
-use crate::block::{ISizeConstraintInput, ISizeConstraintSolution};
-use crate::context::LayoutContext;
-use crate::display_list::{
- BorderPaintingMode, DisplayListBuildState, StackingContextCollectionFlags,
- StackingContextCollectionState,
-};
-use crate::flow::{
- BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, GetBaseFlow, ImmutableFlowUtils,
- OpaqueFlow,
-};
-use crate::flow_list::{FlowListIterator, MutFlowListIterator};
-use crate::fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
-use crate::layout_debug;
-use crate::model::{IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto};
-use crate::table_cell::TableCellFlow;
-use crate::table_row::{self, CellIntrinsicInlineSize, CollapsedBorder, CollapsedBorderProvenance};
-use crate::table_row::{TableRowFlow, TableRowSizeData};
-use crate::table_wrapper::TableLayout;
-use app_units::Au;
-use euclid::Point2D;
-use gfx_traits::print_tree::PrintTree;
-use std::{cmp, fmt};
-use style::computed_values::{border_collapse, border_spacing, table_layout};
-use style::context::SharedStyleContext;
-use style::logical_geometry::LogicalSize;
-use style::properties::style_structs::Background;
-use style::properties::ComputedValues;
-use style::servo::restyle_damage::ServoRestyleDamage;
-use style::values::computed::Size;
-use style::values::CSSFloat;
-
-#[allow(unsafe_code)]
-unsafe impl crate::flow::HasBaseFlow for TableFlow {}
-
-/// A table flow corresponded to the table's internal table fragment under a table wrapper flow.
-/// The properties `position`, `float`, and `margin-*` are used on the table wrapper fragment,
-/// not table fragment per CSS 2.1 § 10.5.
-#[derive(Serialize)]
-#[repr(C)]
-pub struct TableFlow {
- pub block_flow: BlockFlow,
-
- /// Information about the intrinsic inline-sizes of each column, computed bottom-up during
- /// intrinsic inline-size bubbling.
- pub column_intrinsic_inline_sizes: Vec<ColumnIntrinsicInlineSize>,
-
- /// Information about the actual inline sizes of each column, computed top-down during actual
- /// inline-size bubbling.
- pub column_computed_inline_sizes: Vec<ColumnComputedInlineSize>,
-
- /// The final width of the borders in the inline direction for each cell, computed by the
- /// entire table and pushed down into each row during inline size computation.
- pub collapsed_inline_direction_border_widths_for_table: Vec<Au>,
-
- /// The final width of the borders in the block direction for each cell, computed by the
- /// entire table and pushed down into each row during inline size computation.
- pub collapsed_block_direction_border_widths_for_table: Vec<Au>,
-
- /// Table-layout property
- pub table_layout: TableLayout,
-}
-
-impl TableFlow {
- pub fn from_fragment(fragment: Fragment) -> TableFlow {
- let mut block_flow = BlockFlow::from_fragment(fragment);
- let table_layout =
- if block_flow.fragment().style().get_table().table_layout == table_layout::T::Fixed {
- TableLayout::Fixed
- } else {
- TableLayout::Auto
- };
- TableFlow {
- block_flow: block_flow,
- column_intrinsic_inline_sizes: Vec::new(),
- column_computed_inline_sizes: Vec::new(),
- collapsed_inline_direction_border_widths_for_table: Vec::new(),
- collapsed_block_direction_border_widths_for_table: Vec::new(),
- table_layout: table_layout,
- }
- }
-
- /// Update the corresponding value of `self_inline_sizes` if a value of `kid_inline_sizes` has
- /// a larger value than one of `self_inline_sizes`. Returns the minimum and preferred inline
- /// sizes.
- fn update_automatic_column_inline_sizes(
- parent_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
- child_cell_inline_sizes: &[CellIntrinsicInlineSize],
- surrounding_size: Au,
- ) -> IntrinsicISizes {
- let mut total_inline_sizes = IntrinsicISizes {
- minimum_inline_size: surrounding_size,
- preferred_inline_size: surrounding_size,
- };
- let mut column_index = 0;
- let mut incoming_rowspan = vec![];
-
- for child_cell_inline_size in child_cell_inline_sizes {
- // Skip any column occupied by a cell from a previous row.
- while column_index < incoming_rowspan.len() && incoming_rowspan[column_index] != 1 {
- if incoming_rowspan[column_index] > 1 {
- incoming_rowspan[column_index] -= 1;
- }
- column_index += 1;
- }
- for _ in 0..child_cell_inline_size.column_span {
- if column_index < parent_inline_sizes.len() {
- // We already have some intrinsic size information for this column. Merge it in
- // according to the rules specified in INTRINSIC § 4.
- let parent_sizes = &mut parent_inline_sizes[column_index];
- if child_cell_inline_size.column_span > 1 {
- // TODO(pcwalton): Perform the recursive algorithm specified in INTRINSIC §
- // 4. For now we make this column contribute no width.
- } else {
- let column_size = &child_cell_inline_size.column_size;
- *parent_sizes = ColumnIntrinsicInlineSize {
- minimum_length: cmp::max(
- parent_sizes.minimum_length,
- column_size.minimum_length,
- ),
- percentage: parent_sizes.greatest_percentage(column_size),
- preferred: cmp::max(parent_sizes.preferred, column_size.preferred),
- constrained: parent_sizes.constrained || column_size.constrained,
- }
- }
- } else {
- // We discovered a new column. Initialize its data.
- debug_assert_eq!(column_index, parent_inline_sizes.len());
- if child_cell_inline_size.column_span > 1 {
- // TODO(pcwalton): Perform the recursive algorithm specified in INTRINSIC §
- // 4. For now we make this column contribute no width.
- parent_inline_sizes.push(ColumnIntrinsicInlineSize::new())
- } else {
- parent_inline_sizes.push(child_cell_inline_size.column_size)
- }
- }
-
- total_inline_sizes.minimum_inline_size +=
- parent_inline_sizes[column_index].minimum_length;
- total_inline_sizes.preferred_inline_size +=
- parent_inline_sizes[column_index].preferred;
-
- // If this cell spans later rows, record its rowspan.
- if child_cell_inline_size.row_span > 1 {
- if incoming_rowspan.len() < column_index + 1 {
- incoming_rowspan.resize(column_index + 1, 0);
- }
- incoming_rowspan[column_index] = child_cell_inline_size.row_span;
- }
-
- column_index += 1
- }
- }
-
- total_inline_sizes
- }
-
- /// Updates the minimum and preferred inline-size calculation for a single row. This is
- /// factored out into a separate function because we process children of rowgroups too.
- fn update_column_inline_sizes_for_row(
- row: &TableRowFlow,
- column_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
- computation: &mut IntrinsicISizesContribution,
- first_row: bool,
- table_layout: TableLayout,
- surrounding_inline_size: Au,
- ) {
- // Read column inline-sizes from the table-row, and assign inline-size=0 for the columns
- // not defined in the column group.
- //
- // FIXME: Need to read inline-sizes from either table-header-group OR the first table-row.
- match table_layout {
- TableLayout::Fixed => {
- // Fixed table layout only looks at the first row.
- //
- // FIXME(pcwalton): This is really inefficient. We should stop after the first row!
- if first_row {
- for cell_inline_size in &row.cell_intrinsic_inline_sizes {
- column_inline_sizes.push(cell_inline_size.column_size);
- }
- }
- },
- TableLayout::Auto => {
- computation.union_block(&TableFlow::update_automatic_column_inline_sizes(
- column_inline_sizes,
- &row.cell_intrinsic_inline_sizes,
- surrounding_inline_size,
- ))
- },
- }
- }
-
- /// Returns the effective spacing per cell, taking the value of `border-collapse` into account.
- pub fn spacing(&self) -> border_spacing::T {
- let style = self.block_flow.fragment.style();
- match style.get_inherited_table().border_collapse {
- border_collapse::T::Separate => style.get_inherited_table().border_spacing,
- border_collapse::T::Collapse => border_spacing::T::zero(),
- }
- }
-
- pub fn total_horizontal_spacing(&self) -> Au {
- let num_columns = self.column_intrinsic_inline_sizes.len();
- if num_columns == 0 {
- return Au(0);
- }
- self.spacing().horizontal() * (num_columns as i32 + 1)
- }
-
- fn column_styles(&self) -> Vec<ColumnStyle> {
- let mut styles = vec![];
- for group in self
- .block_flow
- .base
- .child_iter()
- .filter(|kid| kid.is_table_colgroup())
- {
- // XXXManishearth these as_foo methods should return options
- // so that we can filter_map
- let group = group.as_table_colgroup();
- let colgroup_style = group.fragment.as_ref().map(|f| f.style());
-
- // The colgroup's span attribute is only relevant when
- // it has no children
- // https://html.spec.whatwg.org/multipage/#forming-a-table
- if group.cols.is_empty() {
- let span = group
- .fragment
- .as_ref()
- .map(|f| f.column_span())
- .unwrap_or(1);
- styles.push(ColumnStyle {
- span,
- colgroup_style,
- col_style: None,
- });
- } else {
- for col in &group.cols {
- // XXXManishearth Arc-cloning colgroup_style is suboptimal
- styles.push(ColumnStyle {
- span: col.column_span(),
- colgroup_style: colgroup_style,
- col_style: Some(col.style()),
- })
- }
- }
- }
- styles
- }
-}
-
-impl Flow for TableFlow {
- fn class(&self) -> FlowClass {
- FlowClass::Table
- }
-
- fn as_mut_table(&mut self) -> &mut TableFlow {
- self
- }
-
- fn as_table(&self) -> &TableFlow {
- self
- }
-
- fn as_mut_block(&mut self) -> &mut BlockFlow {
- &mut self.block_flow
- }
-
- fn as_block(&self) -> &BlockFlow {
- &self.block_flow
- }
-
- fn mark_as_root(&mut self) {
- self.block_flow.mark_as_root();
- }
-
- /// The specified column inline-sizes are set from column group and the first row for the fixed
- /// table layout calculation.
- /// The maximum min/pref inline-sizes of each column are set from the rows for the automatic
- /// table layout calculation.
- fn bubble_inline_sizes(&mut self) {
- let _scope = layout_debug_scope!(
- "table::bubble_inline_sizes {:x}",
- self.block_flow.base.debug_id()
- );
-
- // Get column inline sizes from colgroups
- for kid in self
- .block_flow
- .base
- .child_iter_mut()
- .filter(|kid| kid.is_table_colgroup())
- {
- for specified_inline_size in &kid.as_mut_table_colgroup().inline_sizes {
- self.column_intrinsic_inline_sizes
- .push(ColumnIntrinsicInlineSize {
- minimum_length: match *specified_inline_size {
- Size::Auto => Au(0),
- Size::LengthPercentage(ref lp) => {
- lp.maybe_to_used_value(None).unwrap_or(Au(0))
- },
- },
- percentage: match *specified_inline_size {
- Size::Auto => 0.0,
- Size::LengthPercentage(ref lp) => {
- lp.0.as_percentage().map_or(0.0, |p| p.0)
- },
- },
- preferred: Au(0),
- constrained: false,
- })
- }
- }
-
- self.collapsed_inline_direction_border_widths_for_table = Vec::new();
- self.collapsed_block_direction_border_widths_for_table = vec![Au(0)];
-
- let collapsing_borders = self
- .block_flow
- .fragment
- .style
- .get_inherited_table()
- .border_collapse ==
- border_collapse::T::Collapse;
- let table_inline_collapsed_borders = if collapsing_borders {
- Some(TableInlineCollapsedBorders {
- start: CollapsedBorder::inline_start(
- &*self.block_flow.fragment.style,
- CollapsedBorderProvenance::FromTable,
- ),
- end: CollapsedBorder::inline_end(
- &*self.block_flow.fragment.style,
- CollapsedBorderProvenance::FromTable,
- ),
- })
- } else {
- None
- };
-
- let mut computation = IntrinsicISizesContribution::new();
- let mut previous_collapsed_block_end_borders =
- PreviousBlockCollapsedBorders::FromTable(CollapsedBorder::block_start(
- &*self.block_flow.fragment.style,
- CollapsedBorderProvenance::FromTable,
- ));
- let mut first_row = true;
- let (border_padding, _) = self.block_flow.fragment.surrounding_intrinsic_inline_size();
-
- {
- let mut iterator = TableRowIterator::new(&mut self.block_flow.base).peekable();
- while let Some(row) = iterator.next() {
- TableFlow::update_column_inline_sizes_for_row(
- row,
- &mut self.column_intrinsic_inline_sizes,
- &mut computation,
- first_row,
- self.table_layout,
- border_padding,
- );
- if collapsing_borders {
- let next_index_and_sibling = iterator.peek();
- let next_collapsed_borders_in_block_direction = match next_index_and_sibling {
- Some(next_sibling) => NextBlockCollapsedBorders::FromNextRow(
- &next_sibling
- .as_table_row()
- .preliminary_collapsed_borders
- .block_start,
- ),
- None => NextBlockCollapsedBorders::FromTable(CollapsedBorder::block_end(
- &*self.block_flow.fragment.style,
- CollapsedBorderProvenance::FromTable,
- )),
- };
- perform_border_collapse_for_row(
- row,
- table_inline_collapsed_borders.as_ref().unwrap(),
- previous_collapsed_block_end_borders,
- next_collapsed_borders_in_block_direction,
- &mut self.collapsed_inline_direction_border_widths_for_table,
- &mut self.collapsed_block_direction_border_widths_for_table,
- );
- previous_collapsed_block_end_borders =
- PreviousBlockCollapsedBorders::FromPreviousRow(
- row.final_collapsed_borders.block_end.clone(),
- );
- }
- first_row = false
- }
- }
-
- let total_horizontal_spacing = self.total_horizontal_spacing();
- let mut style_specified_intrinsic_inline_size = self
- .block_flow
- .fragment
- .style_specified_intrinsic_inline_size()
- .finish();
- style_specified_intrinsic_inline_size.minimum_inline_size -= total_horizontal_spacing;
- style_specified_intrinsic_inline_size.preferred_inline_size -= total_horizontal_spacing;
- computation.union_block(&style_specified_intrinsic_inline_size);
- computation.surrounding_size += total_horizontal_spacing;
-
- self.block_flow.base.intrinsic_inline_sizes = computation.finish()
- }
-
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
- /// When called on this context, the context has had its inline-size set by the parent context.
- fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
- let _scope = layout_debug_scope!(
- "table::assign_inline_sizes {:x}",
- self.block_flow.base.debug_id()
- );
- debug!(
- "assign_inline_sizes({}): assigning inline_size for flow",
- "table"
- );
-
- let shared_context = layout_context.shared_context();
- // The position was set to the containing block by the flow's parent.
- // FIXME: The code for distributing column widths should really be placed under table_wrapper.rs.
- let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
-
- let mut constrained_column_inline_sizes_indices = vec![];
- let mut unspecified_inline_sizes_indices = vec![];
- for (idx, column_inline_size) in self.column_intrinsic_inline_sizes.iter().enumerate() {
- if column_inline_size.constrained {
- constrained_column_inline_sizes_indices.push(idx);
- } else if column_inline_size.percentage == 0.0 {
- unspecified_inline_sizes_indices.push(idx);
- }
- }
-
- let inline_size_computer = InternalTable;
- inline_size_computer.compute_used_inline_size(
- &mut self.block_flow,
- shared_context,
- containing_block_inline_size,
- );
-
- let inline_start_content_edge = self.block_flow.fragment.border_padding.inline_start;
- let inline_end_content_edge = self.block_flow.fragment.border_padding.inline_end;
- let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
- let spacing_per_cell = self.spacing();
- let total_horizontal_spacing = self.total_horizontal_spacing();
- let content_inline_size = self.block_flow.fragment.border_box.size.inline -
- padding_and_borders -
- total_horizontal_spacing;
- let mut remaining_inline_size = content_inline_size;
-
- match self.table_layout {
- TableLayout::Fixed => {
- self.column_computed_inline_sizes.clear();
-
- // https://drafts.csswg.org/css2/tables.html#fixed-table-layout
- for column_inline_size in &self.column_intrinsic_inline_sizes {
- if column_inline_size.constrained {
- self.column_computed_inline_sizes
- .push(ColumnComputedInlineSize {
- size: column_inline_size.minimum_length,
- });
- remaining_inline_size -= column_inline_size.minimum_length;
- } else if column_inline_size.percentage != 0.0 {
- let size = remaining_inline_size.scale_by(column_inline_size.percentage);
- self.column_computed_inline_sizes
- .push(ColumnComputedInlineSize { size: size });
- remaining_inline_size -= size;
- } else {
- // Set the size to 0 now, distribute the remaining widths later
- self.column_computed_inline_sizes
- .push(ColumnComputedInlineSize { size: Au(0) });
- }
- }
-
- // Distribute remaining content inline size
- if unspecified_inline_sizes_indices.len() > 0 {
- for &index in &unspecified_inline_sizes_indices {
- self.column_computed_inline_sizes[index].size = remaining_inline_size
- .scale_by(1.0 / unspecified_inline_sizes_indices.len() as f32);
- }
- } else {
- let total_minimum_size = self
- .column_intrinsic_inline_sizes
- .iter()
- .filter(|size| size.constrained)
- .map(|size| size.minimum_length.0 as f32)
- .sum::<f32>();
-
- for &index in &constrained_column_inline_sizes_indices {
- let inline_size = self.column_computed_inline_sizes[index].size.0;
- self.column_computed_inline_sizes[index].size +=
- remaining_inline_size.scale_by(inline_size as f32 / total_minimum_size);
- }
- }
- },
- _ => {
- // The table wrapper already computed the inline-sizes and propagated them down
- // to us.
- },
- }
-
- let column_computed_inline_sizes = &self.column_computed_inline_sizes;
- let collapsed_inline_direction_border_widths_for_table =
- &self.collapsed_inline_direction_border_widths_for_table;
- let mut collapsed_block_direction_border_widths_for_table = self
- .collapsed_block_direction_border_widths_for_table
- .iter()
- .peekable();
- let mut incoming_rowspan = vec![];
- self.block_flow.propagate_assigned_inline_size_to_children(
- shared_context,
- inline_start_content_edge,
- inline_end_content_edge,
- content_inline_size,
- |child_flow,
- _child_index,
- _content_inline_size,
- writing_mode,
- _inline_start_margin_edge,
- _inline_end_margin_edge| {
- table_row::propagate_column_inline_sizes_to_child(
- child_flow,
- writing_mode,
- column_computed_inline_sizes,
- &spacing_per_cell,
- &mut incoming_rowspan,
- );
- if child_flow.is_table_row() {
- let child_table_row = child_flow.as_mut_table_row();
- child_table_row.populate_collapsed_border_spacing(
- collapsed_inline_direction_border_widths_for_table,
- &mut collapsed_block_direction_border_widths_for_table,
- );
- } else if child_flow.is_table_rowgroup() {
- let child_table_rowgroup = child_flow.as_mut_table_rowgroup();
- child_table_rowgroup.populate_collapsed_border_spacing(
- collapsed_inline_direction_border_widths_for_table,
- &mut collapsed_block_direction_border_widths_for_table,
- );
- }
- },
- );
- }
-
- fn assign_block_size(&mut self, lc: &LayoutContext) {
- debug!("assign_block_size: assigning block_size for table");
- let vertical_spacing = self.spacing().vertical();
- self.block_flow
- .assign_block_size_for_table_like_flow(vertical_spacing, lc)
- }
-
- fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
- self.block_flow
- .compute_stacking_relative_position(layout_context)
- }
-
- fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
- self.block_flow.generated_containing_block_size(flow)
- }
-
- fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
- self.block_flow
- .update_late_computed_inline_position_if_necessary(inline_position)
- }
-
- fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
- self.block_flow
- .update_late_computed_block_position_if_necessary(block_position)
- }
-
- fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
- let border_painting_mode = match self
- .block_flow
- .fragment
- .style
- .get_inherited_table()
- .border_collapse
- {
- border_collapse::T::Separate => BorderPaintingMode::Separate,
- border_collapse::T::Collapse => BorderPaintingMode::Hidden,
- };
-
- self.block_flow
- .build_display_list_for_block(state, border_painting_mode);
-
- let iter = TableCellStyleIterator::new(&self);
- for style in iter {
- style.build_display_list(state)
- }
- }
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
- // Stacking contexts are collected by the table wrapper.
- self.block_flow.collect_stacking_contexts_for_block(
- state,
- StackingContextCollectionFlags::NEVER_CREATES_STACKING_CONTEXT,
- );
- }
-
- fn repair_style(&mut self, new_style: &crate::ServoArc<ComputedValues>) {
- self.block_flow.repair_style(new_style)
- }
-
- fn compute_overflow(&self) -> Overflow {
- self.block_flow.compute_overflow()
- }
-
- fn iterate_through_fragment_border_boxes(
- &self,
- iterator: &mut dyn FragmentBorderBoxIterator,
- level: i32,
- stacking_context_position: &Point2D<Au>,
- ) {
- self.block_flow.iterate_through_fragment_border_boxes(
- iterator,
- level,
- stacking_context_position,
- )
- }
-
- fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
- self.block_flow.mutate_fragments(mutator)
- }
-
- fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
- self.block_flow.print_extra_flow_children(print_tree);
- }
-}
-
-#[derive(Debug)]
-struct ColumnStyle<'table> {
- span: u32,
- colgroup_style: Option<&'table ComputedValues>,
- col_style: Option<&'table ComputedValues>,
-}
-
-impl fmt::Debug for TableFlow {
- /// Outputs a debugging string describing this table flow.
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "TableFlow: {:?}", self.block_flow)
- }
-}
-
-/// Table, TableRowGroup, TableRow, TableCell types.
-/// Their inline-sizes are calculated in the same way and do not have margins.
-pub struct InternalTable;
-
-impl ISizeAndMarginsComputer for InternalTable {
- /// Compute the used value of inline-size, taking care of min-inline-size and max-inline-size.
- ///
- /// CSS Section 10.4: Minimum and Maximum inline-sizes
- fn compute_used_inline_size(
- &self,
- block: &mut BlockFlow,
- shared_context: &SharedStyleContext,
- parent_flow_inline_size: Au,
- ) {
- let mut input = self.compute_inline_size_constraint_inputs(
- block,
- parent_flow_inline_size,
- shared_context,
- );
-
- // Tables are always at least as wide as their minimum inline size.
- let minimum_inline_size = block.base.intrinsic_inline_sizes.minimum_inline_size -
- block.fragment.border_padding.inline_start_end();
- input.available_inline_size = cmp::max(input.available_inline_size, minimum_inline_size);
-
- let solution = self.solve_inline_size_constraints(block, &input);
- self.set_inline_size_constraint_solutions(block, solution);
- }
-
- /// Solve the inline-size and margins constraints for this block flow.
- fn solve_inline_size_constraints(
- &self,
- _: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- ISizeConstraintSolution::new(input.available_inline_size, Au(0), Au(0))
- }
-}
-
-/// Information about the intrinsic inline sizes of columns within a table.
-///
-/// During table inline-size bubbling, we might need to store both a percentage constraint and a
-/// specific width constraint. For instance, one cell might say that it wants to be 100 pixels wide
-/// in the inline direction and another cell might say that it wants to take up 20% of the inline-
-/// size of the table. Now because we bubble up these constraints during the bubble-inline-sizes
-/// phase of layout, we don't know yet how wide the table is ultimately going to be in the inline
-/// direction. As we need to pick the maximum width of all cells for a column (in this case, the
-/// maximum of 100 pixels and 20% of the table), the preceding constraint means that we must
-/// potentially store both a specified width *and* a specified percentage, so that the inline-size
-/// assignment phase of layout will know which one to pick.
-#[derive(Clone, Copy, Debug, Serialize)]
-pub struct ColumnIntrinsicInlineSize {
- /// The preferred intrinsic inline size.
- pub preferred: Au,
- /// The largest specified size of this column as a length.
- pub minimum_length: Au,
- /// The largest specified size of this column as a percentage (`width` property).
- pub percentage: CSSFloat,
- /// Whether the column inline size is *constrained* per INTRINSIC § 4.1.
- pub constrained: bool,
-}
-
-impl ColumnIntrinsicInlineSize {
- /// Returns a newly-initialized `ColumnIntrinsicInlineSize` with all fields blank.
- pub fn new() -> ColumnIntrinsicInlineSize {
- ColumnIntrinsicInlineSize {
- preferred: Au(0),
- minimum_length: Au(0),
- percentage: 0.0,
- constrained: false,
- }
- }
-
- /// Returns the higher of the two percentages specified in `self` and `other`.
- pub fn greatest_percentage(&self, other: &ColumnIntrinsicInlineSize) -> CSSFloat {
- if self.percentage > other.percentage {
- self.percentage
- } else {
- other.percentage
- }
- }
-}
-
-/// The actual inline size for each column.
-///
-/// TODO(pcwalton): There will probably be some `border-collapse`-related info in here too
-/// eventually.
-#[derive(Clone, Copy, Debug, Serialize)]
-pub struct ColumnComputedInlineSize {
- /// The computed size of this inline column.
- pub size: Au,
-}
-
-pub trait VecExt<T> {
- fn push_or_set(&mut self, index: usize, value: T) -> &mut T;
- fn get_mut_or_push(&mut self, index: usize, zero: T) -> &mut T;
-}
-
-impl<T> VecExt<T> for Vec<T> {
- fn push_or_set(&mut self, index: usize, value: T) -> &mut T {
- if index < self.len() {
- self[index] = value
- } else {
- debug_assert_eq!(index, self.len());
- self.push(value)
- }
- &mut self[index]
- }
-
- fn get_mut_or_push(&mut self, index: usize, zero: T) -> &mut T {
- if index >= self.len() {
- debug_assert_eq!(index, self.len());
- self.push(zero)
- }
- &mut self[index]
- }
-}
-
-/// Updates the border styles in the block direction for a single row. This function should
-/// only be called if border collapsing is on. It is factored out into a separate function
-/// because we process children of rowgroups too.
-fn perform_border_collapse_for_row(
- child_table_row: &mut TableRowFlow,
- table_inline_borders: &TableInlineCollapsedBorders,
- previous_block_borders: PreviousBlockCollapsedBorders,
- next_block_borders: NextBlockCollapsedBorders,
- inline_spacing: &mut Vec<Au>,
- block_spacing: &mut Vec<Au>,
-) {
- // TODO mbrubeck: Take rowspan and colspan into account.
- let number_of_borders_inline_direction =
- child_table_row.preliminary_collapsed_borders.inline.len();
- // Compute interior inline borders.
- for (i, this_inline_border) in child_table_row
- .preliminary_collapsed_borders
- .inline
- .iter_mut()
- .enumerate()
- {
- child_table_row
- .final_collapsed_borders
- .inline
- .push_or_set(i, *this_inline_border);
- if i == 0 {
- child_table_row.final_collapsed_borders.inline[i].combine(&table_inline_borders.start);
- } else if i + 1 == number_of_borders_inline_direction {
- child_table_row.final_collapsed_borders.inline[i].combine(&table_inline_borders.end);
- }
-
- let inline_spacing = inline_spacing.get_mut_or_push(i, Au(0));
- *inline_spacing = cmp::max(
- *inline_spacing,
- child_table_row.final_collapsed_borders.inline[i].width,
- )
- }
-
- // Compute block-start borders.
- let block_start_borders = &mut child_table_row.final_collapsed_borders.block_start;
- *block_start_borders = child_table_row
- .preliminary_collapsed_borders
- .block_start
- .clone();
- for (i, this_border) in block_start_borders.iter_mut().enumerate() {
- match previous_block_borders {
- PreviousBlockCollapsedBorders::FromPreviousRow(ref previous_block_borders) => {
- if previous_block_borders.len() > i {
- this_border.combine(&previous_block_borders[i]);
- }
- },
- PreviousBlockCollapsedBorders::FromTable(table_border) => {
- this_border.combine(&table_border);
- },
- }
- }
-
- // Compute block-end borders.
- let next_block = &mut child_table_row.final_collapsed_borders.block_end;
- block_spacing.push(Au(0));
- let block_spacing = block_spacing.last_mut().unwrap();
- for (i, this_block_border) in child_table_row
- .preliminary_collapsed_borders
- .block_end
- .iter()
- .enumerate()
- {
- let next_block = next_block.push_or_set(i, *this_block_border);
- match next_block_borders {
- NextBlockCollapsedBorders::FromNextRow(next_block_borders) => {
- if next_block_borders.len() > i {
- next_block.combine(&next_block_borders[i])
- }
- },
- NextBlockCollapsedBorders::FromTable(ref next_block_borders) => {
- next_block.combine(next_block_borders);
- },
- }
- *block_spacing = cmp::max(*block_spacing, next_block.width)
- }
-}
-
-/// Encapsulates functionality shared among all table-like flows: for now, tables and table
-/// rowgroups.
-pub trait TableLikeFlow {
- /// Lays out the rows of a table.
- fn assign_block_size_for_table_like_flow(
- &mut self,
- block_direction_spacing: Au,
- layout_context: &LayoutContext,
- );
-}
-
-impl TableLikeFlow for BlockFlow {
- fn assign_block_size_for_table_like_flow(
- &mut self,
- block_direction_spacing: Au,
- layout_context: &LayoutContext,
- ) {
- debug_assert!(
- self.fragment.style.get_inherited_table().border_collapse ==
- border_collapse::T::Separate ||
- block_direction_spacing == Au(0)
- );
-
- fn border_spacing_for_row(
- fragment: &Fragment,
- row: &TableRowFlow,
- block_direction_spacing: Au,
- ) -> Au {
- match fragment.style.get_inherited_table().border_collapse {
- border_collapse::T::Separate => block_direction_spacing,
- border_collapse::T::Collapse => row.collapsed_border_spacing.block_start,
- }
- }
-
- if self
- .base
- .restyle_damage
- .contains(ServoRestyleDamage::REFLOW)
- {
- let mut sizes = vec![Default::default()];
- // The amount of border spacing up to and including this row,
- // but not including the spacing beneath it
- let mut cumulative_border_spacing = Au(0);
- let mut incoming_rowspan_data = vec![];
- let mut rowgroup_id = 0;
- let mut first = true;
-
- // First pass: Compute block-direction border spacings
- // XXXManishearth this can be done in tandem with the second pass,
- // provided we never hit any rowspan cases
- for kid in self.base.child_iter_mut() {
- if kid.is_table_row() {
- // skip the first row, it is accounted for
- if first {
- first = false;
- continue;
- }
- cumulative_border_spacing += border_spacing_for_row(
- &self.fragment,
- kid.as_table_row(),
- block_direction_spacing,
- );
- sizes.push(TableRowSizeData {
- // we haven't calculated sizes yet
- size: Au(0),
- cumulative_border_spacing,
- rowgroup_id,
- });
- } else if kid.is_table_rowgroup() && !first {
- rowgroup_id += 1;
- }
- }
-
- // Second pass: Compute row block sizes
- // [expensive: iterates over cells]
- let mut i = 0;
- for kid in self.base.child_iter_mut() {
- if kid.is_table_row() {
- let size = kid.as_mut_table_row().compute_block_size_table_row_base(
- layout_context,
- &mut incoming_rowspan_data,
- &sizes,
- i,
- );
- sizes[i].size = size;
- i += 1;
- }
- }
-
- // Our current border-box position.
- let block_start_border_padding = self.fragment.border_padding.block_start;
- let mut current_block_offset = block_start_border_padding;
- let mut has_rows = false;
-
- // Third pass: Assign block sizes and positions to rows, cells, and other children
- // [expensive: iterates over cells]
- // At this point, `current_block_offset` is at the content edge of our box. Now iterate
- // over children.
- let mut i = 0;
- for kid in self.base.child_iter_mut() {
- if kid.is_table_row() {
- has_rows = true;
- let row = kid.as_mut_table_row();
- row.assign_block_size_to_self_and_children(&sizes, i);
- row.mut_base().restyle_damage.remove(
- ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW,
- );
- current_block_offset = current_block_offset +
- border_spacing_for_row(&self.fragment, row, block_direction_spacing);
- i += 1;
- }
-
- // At this point, `current_block_offset` is at the border edge of the child.
- kid.mut_base().position.start.b = current_block_offset;
-
- // Move past the child's border box. Do not use the `translate_including_floats`
- // function here because the child has already translated floats past its border
- // box.
- let kid_base = kid.mut_base();
- current_block_offset = current_block_offset + kid_base.position.size.block;
- }
-
- // Compute any explicitly-specified block size.
- // Can't use `for` because we assign to
- // `candidate_block_size_iterator.candidate_value`.
- let mut block_size = current_block_offset - block_start_border_padding;
- let mut candidate_block_size_iterator = CandidateBSizeIterator::new(
- &self.fragment,
- self.base.block_container_explicit_block_size,
- );
- while let Some(candidate_block_size) = candidate_block_size_iterator.next() {
- candidate_block_size_iterator.candidate_value = match candidate_block_size {
- MaybeAuto::Auto => block_size,
- MaybeAuto::Specified(value) => value,
- };
- }
-
- // Adjust `current_block_offset` as necessary to account for the explicitly-specified
- // block-size.
- block_size = candidate_block_size_iterator.candidate_value;
- let delta = block_size - (current_block_offset - block_start_border_padding);
- current_block_offset = current_block_offset + delta;
-
- // Take border, padding, and spacing into account.
- let block_end_offset = self.fragment.border_padding.block_end +
- if has_rows {
- block_direction_spacing
- } else {
- Au(0)
- };
- current_block_offset = current_block_offset + block_end_offset;
-
- // Now that `current_block_offset` is at the block-end of the border box, compute the
- // final border box position.
- self.fragment.border_box.size.block = current_block_offset;
- self.fragment.border_box.start.b = Au(0);
- self.base.position.size.block = current_block_offset;
-
- // Fourth pass: Assign absolute position info
- // Write in the size of the relative containing block for children. (This information
- // is also needed to handle RTL.)
- for kid in self.base.child_iter_mut() {
- kid.mut_base().early_absolute_position_info = EarlyAbsolutePositionInfo {
- relative_containing_block_size: self.fragment.content_box().size,
- relative_containing_block_mode: self.fragment.style().writing_mode,
- };
- }
- }
-
- self.base
- .restyle_damage
- .remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
- }
-}
-
-/// Inline collapsed borders for the table itself.
-#[derive(Debug)]
-struct TableInlineCollapsedBorders {
- /// The table border at the start of the inline direction.
- start: CollapsedBorder,
- /// The table border at the end of the inline direction.
- end: CollapsedBorder,
-}
-
-enum PreviousBlockCollapsedBorders {
- FromPreviousRow(Vec<CollapsedBorder>),
- FromTable(CollapsedBorder),
-}
-
-enum NextBlockCollapsedBorders<'a> {
- FromNextRow(&'a [CollapsedBorder]),
- FromTable(CollapsedBorder),
-}
-
-/// Iterator over all the rows of a table, which also
-/// provides the Fragment for rowgroups if any
-struct TableRowAndGroupIterator<'a> {
- kids: FlowListIterator<'a>,
- group: Option<(&'a Fragment, FlowListIterator<'a>)>,
-}
-
-impl<'a> TableRowAndGroupIterator<'a> {
- fn new(base: &'a BaseFlow) -> Self {
- TableRowAndGroupIterator {
- kids: base.child_iter(),
- group: None,
- }
- }
-}
-
-impl<'a> Iterator for TableRowAndGroupIterator<'a> {
- type Item = (Option<&'a Fragment>, &'a TableRowFlow);
- #[inline]
- fn next(&mut self) -> Option<Self::Item> {
- // If we're inside a rowgroup, iterate through the rowgroup's children.
- if let Some(ref mut group) = self.group {
- if let Some(grandkid) = group.1.next() {
- return Some((Some(group.0), grandkid.as_table_row()));
- }
- }
- // Otherwise, iterate through the table's children.
- self.group = None;
- match self.kids.next() {
- Some(kid) => {
- if kid.is_table_rowgroup() {
- let rowgroup = kid.as_table_rowgroup();
- let iter = rowgroup.block_flow.base.child_iter();
- self.group = Some((&rowgroup.block_flow.fragment, iter));
- self.next()
- } else if kid.is_table_row() {
- Some((None, kid.as_table_row()))
- } else {
- self.next() // Skip children that are not rows or rowgroups
- }
- },
- None => None,
- }
- }
-}
-
-/// Iterator over all the rows of a table, which also
-/// provides the Fragment for rowgroups if any
-struct MutTableRowAndGroupIterator<'a> {
- kids: MutFlowListIterator<'a>,
- group: Option<(&'a Fragment, MutFlowListIterator<'a>)>,
-}
-
-impl<'a> MutTableRowAndGroupIterator<'a> {
- fn new(base: &'a mut BaseFlow) -> Self {
- MutTableRowAndGroupIterator {
- kids: base.child_iter_mut(),
- group: None,
- }
- }
-}
-
-impl<'a> Iterator for MutTableRowAndGroupIterator<'a> {
- type Item = (Option<&'a Fragment>, &'a mut TableRowFlow);
- #[inline]
- fn next(&mut self) -> Option<Self::Item> {
- // If we're inside a rowgroup, iterate through the rowgroup's children.
- if let Some(ref mut group) = self.group {
- if let Some(grandkid) = group.1.next() {
- return Some((Some(group.0), grandkid.as_mut_table_row()));
- }
- }
- // Otherwise, iterate through the table's children.
- self.group = None;
- match self.kids.next() {
- Some(kid) => {
- if kid.is_table_rowgroup() {
- let rowgroup = kid.as_mut_table_rowgroup();
- let iter = rowgroup.block_flow.base.child_iter_mut();
- self.group = Some((&rowgroup.block_flow.fragment, iter));
- self.next()
- } else if kid.is_table_row() {
- Some((None, kid.as_mut_table_row()))
- } else {
- self.next() // Skip children that are not rows or rowgroups
- }
- },
- None => None,
- }
- }
-}
-
-/// Iterator over all the rows of a table
-struct TableRowIterator<'a>(MutTableRowAndGroupIterator<'a>);
-
-impl<'a> TableRowIterator<'a> {
- fn new(base: &'a mut BaseFlow) -> Self {
- TableRowIterator(MutTableRowAndGroupIterator::new(base))
- }
-}
-
-impl<'a> Iterator for TableRowIterator<'a> {
- type Item = &'a mut TableRowFlow;
- #[inline]
- fn next(&mut self) -> Option<Self::Item> {
- self.0.next().map(|n| n.1)
- }
-}
-
-/// An iterator over table cells, yielding all relevant style objects
-/// for each cell
-///
-/// Used for correctly handling table layers from
-/// https://drafts.csswg.org/css2/tables.html#table-layers
-struct TableCellStyleIterator<'table> {
- column_styles: Vec<ColumnStyle<'table>>,
- row_iterator: TableRowAndGroupIterator<'table>,
- row_info: Option<TableCellStyleIteratorRowInfo<'table>>,
- column_index: TableCellColumnIndexData,
-}
-
-struct TableCellStyleIteratorRowInfo<'table> {
- row: &'table TableRowFlow,
- rowgroup: Option<&'table Fragment>,
- cell_iterator: FlowListIterator<'table>,
-}
-
-impl<'table> TableCellStyleIterator<'table> {
- fn new(table: &'table TableFlow) -> Self {
- let column_styles = table.column_styles();
- let mut row_iterator = TableRowAndGroupIterator::new(&table.block_flow.base);
- let row_info = if let Some((group, row)) = row_iterator.next() {
- Some(TableCellStyleIteratorRowInfo {
- row: &row,
- rowgroup: group,
- cell_iterator: row.block_flow.base.child_iter(),
- })
- } else {
- None
- };
- TableCellStyleIterator {
- column_styles,
- row_iterator,
- row_info,
- column_index: Default::default(),
- }
- }
-}
-
-struct TableCellStyleInfo<'table> {
- cell: &'table TableCellFlow,
- colgroup_style: Option<&'table ComputedValues>,
- col_style: Option<&'table ComputedValues>,
- rowgroup_style: Option<&'table ComputedValues>,
- row_style: &'table ComputedValues,
-}
-
-struct TableCellColumnIndexData {
- /// Which column this is in the table
- pub absolute: u32,
- /// The index of the current column in column_styles
- /// (i.e. which <col> element it is)
- pub relative: u32,
- /// In case of multispan <col>s, where we are in the
- /// span of the current <col> element
- pub relative_offset: u32,
-}
-
-impl Default for TableCellColumnIndexData {
- fn default() -> Self {
- TableCellColumnIndexData {
- absolute: 0,
- relative: 0,
- relative_offset: 0,
- }
- }
-}
-
-impl TableCellColumnIndexData {
- /// Moves forward by `amount` columns, updating the various indices used
- ///
- /// This totally ignores rowspan -- if colspan and rowspan clash,
- /// they just overlap, so we ignore it.
- fn advance(&mut self, amount: u32, column_styles: &[ColumnStyle]) {
- self.absolute += amount;
- self.relative_offset += amount;
- if let Some(mut current_col) = column_styles.get(self.relative as usize) {
- while self.relative_offset >= current_col.span {
- // move to the next column
- self.relative += 1;
- self.relative_offset -= current_col.span;
- if let Some(column_style) = column_styles.get(self.relative as usize) {
- current_col = column_style;
- } else {
- // we ran out of column_styles,
- // so we don't need to update the indices
- break;
- }
- }
- }
- }
-}
-
-impl<'table> Iterator for TableCellStyleIterator<'table> {
- type Item = TableCellStyleInfo<'table>;
- #[inline]
- fn next(&mut self) -> Option<Self::Item> {
- // FIXME We do this awkward .take() followed by shoving it back in
- // because without NLL the row_info borrow lasts too long
- if let Some(mut row_info) = self.row_info.take() {
- if let Some(rowspan) = row_info
- .row
- .incoming_rowspan
- .get(self.column_index.absolute as usize)
- {
- // we are not allowed to use this column as a starting point. Try the next one.
- if *rowspan > 1 {
- self.column_index.advance(1, &self.column_styles);
- // put row_info back in
- self.row_info = Some(row_info);
- // try again
- return self.next();
- }
- }
- if let Some(cell) = row_info.cell_iterator.next() {
- let rowgroup_style = row_info.rowgroup.map(|r| r.style());
- let row_style = row_info.row.block_flow.fragment.style();
- let cell = cell.as_table_cell();
- let (col_style, colgroup_style) = if let Some(column_style) =
- self.column_styles.get(self.column_index.relative as usize)
- {
- let styles = (
- column_style.col_style.clone(),
- column_style.colgroup_style.clone(),
- );
- self.column_index
- .advance(cell.column_span, &self.column_styles);
-
- styles
- } else {
- (None, None)
- };
- // put row_info back in
- self.row_info = Some(row_info);
- return Some(TableCellStyleInfo {
- cell,
- colgroup_style,
- col_style,
- rowgroup_style,
- row_style,
- });
- } else {
- // next row
- if let Some((group, row)) = self.row_iterator.next() {
- self.row_info = Some(TableCellStyleIteratorRowInfo {
- row: &row,
- rowgroup: group,
- cell_iterator: row.block_flow.base.child_iter(),
- });
- self.column_index = Default::default();
- self.next()
- } else {
- // out of rows
- // row_info stays None
- None
- }
- }
- } else {
- // empty table
- None
- }
- }
-}
-
-impl<'table> TableCellStyleInfo<'table> {
- fn build_display_list(&self, mut state: &mut DisplayListBuildState) {
- use style::computed_values::visibility::T as Visibility;
-
- if !self.cell.visible ||
- self.cell
- .block_flow
- .fragment
- .style()
- .get_inherited_box()
- .visibility !=
- Visibility::Visible
- {
- return;
- }
- let border_painting_mode = match self
- .cell
- .block_flow
- .fragment
- .style
- .get_inherited_table()
- .border_collapse
- {
- border_collapse::T::Separate => BorderPaintingMode::Separate,
- border_collapse::T::Collapse => {
- BorderPaintingMode::Collapse(&self.cell.collapsed_borders)
- },
- };
- {
- let cell_flow = &self.cell.block_flow;
- let initial = ComputedValues::initial_values();
-
- let build_dl = |sty: &ComputedValues, state: &mut &mut DisplayListBuildState| {
- let background = sty.get_background();
- // Don't redraw backgrounds that we've already drawn
- if background as *const Background == initial.get_background() as *const _ {
- return;
- }
- let background_color = sty.resolve_color(background.background_color);
- cell_flow.build_display_list_for_background_if_applicable_with_background(
- state,
- background,
- background_color,
- );
- };
-
- if let Some(ref sty) = self.colgroup_style {
- build_dl(&sty, &mut state);
- }
- if let Some(ref sty) = self.col_style {
- build_dl(&sty, &mut state);
- }
- if let Some(ref sty) = self.rowgroup_style {
- build_dl(sty, &mut state);
- }
- build_dl(self.row_style, &mut state);
- }
- // the restyle damage will be set in TableCellFlow::build_display_list()
- self.cell
- .block_flow
- .build_display_list_for_block_no_damage(state, border_painting_mode)
- }
-}
diff --git a/components/layout_2020/table_caption.rs b/components/layout_2020/table_caption.rs
deleted file mode 100644
index 3d44190f986..00000000000
--- a/components/layout_2020/table_caption.rs
+++ /dev/null
@@ -1,139 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS table formatting contexts.
-
-use crate::block::BlockFlow;
-use crate::context::LayoutContext;
-use crate::display_list::{
- DisplayListBuildState, StackingContextCollectionFlags, StackingContextCollectionState,
-};
-use crate::flow::{Flow, FlowClass, OpaqueFlow};
-use crate::fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
-use app_units::Au;
-use euclid::Point2D;
-use gfx_traits::print_tree::PrintTree;
-use std::fmt;
-use style::logical_geometry::LogicalSize;
-use style::properties::ComputedValues;
-
-#[allow(unsafe_code)]
-unsafe impl crate::flow::HasBaseFlow for TableCaptionFlow {}
-
-/// A table formatting context.
-#[repr(C)]
-pub struct TableCaptionFlow {
- pub block_flow: BlockFlow,
-}
-
-impl TableCaptionFlow {
- pub fn from_fragment(fragment: Fragment) -> TableCaptionFlow {
- TableCaptionFlow {
- block_flow: BlockFlow::from_fragment(fragment),
- }
- }
-}
-
-impl Flow for TableCaptionFlow {
- fn class(&self) -> FlowClass {
- FlowClass::TableCaption
- }
-
- fn as_mut_block(&mut self) -> &mut BlockFlow {
- &mut self.block_flow
- }
-
- fn as_block(&self) -> &BlockFlow {
- &self.block_flow
- }
-
- fn bubble_inline_sizes(&mut self) {
- self.block_flow.bubble_inline_sizes();
- }
-
- fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
- debug!(
- "assign_inline_sizes({}): assigning inline_size for flow",
- "table_caption"
- );
- self.block_flow.assign_inline_sizes(layout_context);
- }
-
- fn assign_block_size(&mut self, layout_context: &LayoutContext) {
- debug!("assign_block_size: assigning block_size for table_caption");
- self.block_flow.assign_block_size(layout_context);
- }
-
- fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
- self.block_flow
- .compute_stacking_relative_position(layout_context)
- }
-
- fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
- self.block_flow
- .update_late_computed_inline_position_if_necessary(inline_position)
- }
-
- fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
- self.block_flow
- .update_late_computed_block_position_if_necessary(block_position)
- }
-
- fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
- debug!("build_display_list_table_caption: same process as block flow");
- self.block_flow.build_display_list(state);
- }
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
- self.block_flow
- .collect_stacking_contexts_for_block(state, StackingContextCollectionFlags::empty());
- }
-
- fn repair_style(&mut self, new_style: &crate::ServoArc<ComputedValues>) {
- self.block_flow.repair_style(new_style)
- }
-
- fn compute_overflow(&self) -> Overflow {
- self.block_flow.compute_overflow()
- }
-
- fn contains_roots_of_absolute_flow_tree(&self) -> bool {
- self.block_flow.contains_roots_of_absolute_flow_tree()
- }
-
- fn is_absolute_containing_block(&self) -> bool {
- self.block_flow.is_absolute_containing_block()
- }
-
- fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
- self.block_flow.generated_containing_block_size(flow)
- }
-
- fn iterate_through_fragment_border_boxes(
- &self,
- iterator: &mut dyn FragmentBorderBoxIterator,
- level: i32,
- stacking_context_position: &Point2D<Au>,
- ) {
- self.block_flow.iterate_through_fragment_border_boxes(
- iterator,
- level,
- stacking_context_position,
- )
- }
-
- fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
- self.block_flow.mutate_fragments(mutator)
- }
-
- fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
- self.block_flow.print_extra_flow_children(print_tree);
- }
-}
-
-impl fmt::Debug for TableCaptionFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "TableCaptionFlow: {:?}", self.block_flow)
- }
-}
diff --git a/components/layout_2020/table_cell.rs b/components/layout_2020/table_cell.rs
deleted file mode 100644
index ec79f9f4540..00000000000
--- a/components/layout_2020/table_cell.rs
+++ /dev/null
@@ -1,506 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS table formatting contexts.
-
-use crate::block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag};
-use crate::context::LayoutContext;
-use crate::display_list::{
- DisplayListBuildState, StackingContextCollectionFlags, StackingContextCollectionState,
-};
-use crate::flow::{Flow, FlowClass, FlowFlags, GetBaseFlow, OpaqueFlow};
-use crate::fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
-use crate::layout_debug;
-use crate::table::InternalTable;
-use crate::table_row::{CollapsedBorder, CollapsedBorderProvenance};
-use app_units::Au;
-use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
-use gfx_traits::print_tree::PrintTree;
-use script_layout_interface::wrapper_traits::ThreadSafeLayoutNode;
-use std::fmt;
-use style::logical_geometry::{LogicalMargin, LogicalRect, LogicalSize, WritingMode};
-use style::properties::ComputedValues;
-use style::values::computed::length::Size;
-use style::values::computed::Color;
-use style::values::generics::box_::{VerticalAlign, VerticalAlignKeyword};
-use style::values::specified::BorderStyle;
-
-#[allow(unsafe_code)]
-unsafe impl crate::flow::HasBaseFlow for TableCellFlow {}
-
-/// A table formatting context.
-#[derive(Serialize)]
-#[repr(C)]
-pub struct TableCellFlow {
- /// Data common to all block flows.
- pub block_flow: BlockFlow,
-
- /// Border collapse information for the cell.
- pub collapsed_borders: CollapsedBordersForCell,
-
- /// The column span of this cell.
- pub column_span: u32,
-
- /// The rows spanned by this cell.
- pub row_span: u32,
-
- /// Whether this cell is visible. If false, the value of `empty-cells` means that we must not
- /// display this cell.
- pub visible: bool,
-}
-
-impl TableCellFlow {
- pub fn from_fragment(fragment: Fragment) -> TableCellFlow {
- TableCellFlow {
- block_flow: BlockFlow::from_fragment(fragment),
- collapsed_borders: CollapsedBordersForCell::new(),
- column_span: 1,
- row_span: 1,
- visible: true,
- }
- }
-
- pub fn from_node_fragment_and_visibility_flag<N: ThreadSafeLayoutNode>(
- node: &N,
- fragment: Fragment,
- visible: bool,
- ) -> TableCellFlow {
- TableCellFlow {
- block_flow: BlockFlow::from_fragment(fragment),
- collapsed_borders: CollapsedBordersForCell::new(),
- column_span: node.get_colspan(),
- row_span: node.get_rowspan(),
- visible: visible,
- }
- }
-
- pub fn fragment(&mut self) -> &Fragment {
- &self.block_flow.fragment
- }
-
- pub fn mut_fragment(&mut self) -> &mut Fragment {
- &mut self.block_flow.fragment
- }
-
- /// Assign block-size for table-cell flow.
- ///
- /// inline(always) because this is only ever called by in-order or non-in-order top-level
- /// methods.
- #[inline(always)]
- fn assign_block_size_table_cell_base(&mut self, layout_context: &LayoutContext) {
- let remaining = self.block_flow.assign_block_size_block_base(
- layout_context,
- None,
- MarginsMayCollapseFlag::MarginsMayNotCollapse,
- );
- debug_assert!(remaining.is_none());
- }
-
- /// Position this cell's children according to vertical-align.
- pub fn valign_children(&mut self) {
- // Note to the reader: this code has been tested with negative margins.
- // We end up with a "end" that's before the "start," but the math still works out.
- let mut extents = None;
- for kid in self.base().children.iter() {
- let kid_base = kid.base();
- if kid_base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
- continue;
- }
- let start = kid_base.position.start.b -
- kid_base
- .collapsible_margins
- .block_start_margin_for_noncollapsible_context();
- let end = kid_base.position.start.b +
- kid_base.position.size.block +
- kid_base
- .collapsible_margins
- .block_end_margin_for_noncollapsible_context();
- match extents {
- Some((ref mut first_start, ref mut last_end)) => {
- if start < *first_start {
- *first_start = start
- }
- if end > *last_end {
- *last_end = end
- }
- },
- None => extents = Some((start, end)),
- }
- }
- let (first_start, last_end) = match extents {
- Some(extents) => extents,
- None => return,
- };
-
- let kids_size = last_end - first_start;
- let self_size = self.base().position.size.block -
- self.block_flow.fragment.border_padding.block_start_end();
- let kids_self_gap = self_size - kids_size;
-
- // This offset should also account for VerticalAlign::baseline.
- // Need max cell ascent from the first row of this cell.
- let offset = match self.block_flow.fragment.style().get_box().vertical_align {
- VerticalAlign::Keyword(VerticalAlignKeyword::Middle) => kids_self_gap / 2,
- VerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => kids_self_gap,
- _ => Au(0),
- };
- if offset == Au(0) {
- return;
- }
-
- for kid in self.mut_base().children.iter_mut() {
- let kid_base = kid.mut_base();
- if !kid_base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
- kid_base.position.start.b += offset
- }
- }
- }
-
- // Total block size of child
- //
- // Call after block size calculation
- pub fn total_block_size(&mut self) -> Au {
- // TODO: Percentage block-size
- let specified = self
- .fragment()
- .style()
- .content_block_size()
- .to_used_value(Au(0))
- .unwrap_or(Au(0));
- specified + self.fragment().border_padding.block_start_end()
- }
-}
-
-impl Flow for TableCellFlow {
- fn class(&self) -> FlowClass {
- FlowClass::TableCell
- }
-
- fn as_mut_table_cell(&mut self) -> &mut TableCellFlow {
- self
- }
-
- fn as_table_cell(&self) -> &TableCellFlow {
- self
- }
-
- fn as_mut_block(&mut self) -> &mut BlockFlow {
- &mut self.block_flow
- }
-
- fn as_block(&self) -> &BlockFlow {
- &self.block_flow
- }
-
- /// Minimum/preferred inline-sizes set by this function are used in automatic table layout
- /// calculation.
- fn bubble_inline_sizes(&mut self) {
- let _scope = layout_debug_scope!(
- "table_cell::bubble_inline_sizes {:x}",
- self.block_flow.base.debug_id()
- );
-
- self.block_flow.bubble_inline_sizes_for_block(true);
- let specified_inline_size = match self.block_flow.fragment.style().content_inline_size() {
- Size::Auto => Au(0),
- Size::LengthPercentage(ref lp) => lp.to_used_value(Au(0)),
- };
-
- if self
- .block_flow
- .base
- .intrinsic_inline_sizes
- .minimum_inline_size <
- specified_inline_size
- {
- self.block_flow
- .base
- .intrinsic_inline_sizes
- .minimum_inline_size = specified_inline_size
- }
- if self
- .block_flow
- .base
- .intrinsic_inline_sizes
- .preferred_inline_size <
- self.block_flow
- .base
- .intrinsic_inline_sizes
- .minimum_inline_size
- {
- self.block_flow
- .base
- .intrinsic_inline_sizes
- .preferred_inline_size = self
- .block_flow
- .base
- .intrinsic_inline_sizes
- .minimum_inline_size;
- }
- }
-
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
- /// When called on this context, the context has had its inline-size set by the parent table
- /// row.
- fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
- let _scope = layout_debug_scope!(
- "table_cell::assign_inline_sizes {:x}",
- self.block_flow.base.debug_id()
- );
- debug!(
- "assign_inline_sizes({}): assigning inline_size for flow",
- "table_cell"
- );
-
- let shared_context = layout_context.shared_context();
- // The position was set to the column inline-size by the parent flow, table row flow.
- let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
-
- let inline_size_computer = InternalTable;
- inline_size_computer.compute_used_inline_size(
- &mut self.block_flow,
- shared_context,
- containing_block_inline_size,
- );
-
- let inline_start_content_edge = self.block_flow.fragment.border_box.start.i +
- self.block_flow.fragment.border_padding.inline_start;
- let inline_end_content_edge = self.block_flow.base.block_container_inline_size -
- self.block_flow.fragment.border_padding.inline_start_end() -
- self.block_flow.fragment.border_box.size.inline;
- let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
- let content_inline_size =
- self.block_flow.fragment.border_box.size.inline - padding_and_borders;
-
- self.block_flow.propagate_assigned_inline_size_to_children(
- shared_context,
- inline_start_content_edge,
- inline_end_content_edge,
- content_inline_size,
- |_, _, _, _, _, _| {},
- );
- }
-
- fn assign_block_size(&mut self, layout_context: &LayoutContext) {
- debug!("assign_block_size: assigning block_size for table_cell");
- self.assign_block_size_table_cell_base(layout_context);
- }
-
- fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
- self.block_flow
- .compute_stacking_relative_position(layout_context)
- }
-
- fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
- self.block_flow
- .update_late_computed_inline_position_if_necessary(inline_position)
- }
-
- fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
- self.block_flow
- .update_late_computed_block_position_if_necessary(block_position)
- }
-
- fn build_display_list(&mut self, _: &mut DisplayListBuildState) {
- use style::servo::restyle_damage::ServoRestyleDamage;
- // This is handled by TableCellStyleInfo::build_display_list()
- // when the containing table builds its display list
-
- // we skip setting the damage in TableCellStyleInfo::build_display_list()
- // because we only have immutable access
- self.block_flow
- .fragment
- .restyle_damage
- .remove(ServoRestyleDamage::REPAINT);
- }
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
- self.block_flow
- .collect_stacking_contexts_for_block(state, StackingContextCollectionFlags::empty());
- }
-
- fn repair_style(&mut self, new_style: &crate::ServoArc<ComputedValues>) {
- self.block_flow.repair_style(new_style)
- }
-
- fn compute_overflow(&self) -> Overflow {
- self.block_flow.compute_overflow()
- }
-
- fn contains_roots_of_absolute_flow_tree(&self) -> bool {
- self.block_flow.contains_roots_of_absolute_flow_tree()
- }
-
- fn is_absolute_containing_block(&self) -> bool {
- self.block_flow.is_absolute_containing_block()
- }
-
- fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
- self.block_flow.generated_containing_block_size(flow)
- }
-
- fn iterate_through_fragment_border_boxes(
- &self,
- iterator: &mut dyn FragmentBorderBoxIterator,
- level: i32,
- stacking_context_position: &Point2D<Au>,
- ) {
- self.block_flow.iterate_through_fragment_border_boxes(
- iterator,
- level,
- stacking_context_position,
- )
- }
-
- fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
- self.block_flow.mutate_fragments(mutator)
- }
-
- fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
- self.block_flow.print_extra_flow_children(print_tree);
- }
-}
-
-impl fmt::Debug for TableCellFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "TableCellFlow: {:?}", self.block_flow)
- }
-}
-
-#[derive(Clone, Copy, Debug, Serialize)]
-pub struct CollapsedBordersForCell {
- pub inline_start_border: CollapsedBorder,
- pub inline_end_border: CollapsedBorder,
- pub block_start_border: CollapsedBorder,
- pub block_end_border: CollapsedBorder,
- pub inline_start_width: Au,
- pub inline_end_width: Au,
- pub block_start_width: Au,
- pub block_end_width: Au,
-}
-
-impl CollapsedBordersForCell {
- fn new() -> CollapsedBordersForCell {
- CollapsedBordersForCell {
- inline_start_border: CollapsedBorder::new(),
- inline_end_border: CollapsedBorder::new(),
- block_start_border: CollapsedBorder::new(),
- block_end_border: CollapsedBorder::new(),
- inline_start_width: Au(0),
- inline_end_width: Au(0),
- block_start_width: Au(0),
- block_end_width: Au(0),
- }
- }
-
- fn should_paint_inline_start_border(&self) -> bool {
- self.inline_start_border.provenance != CollapsedBorderProvenance::FromPreviousTableCell
- }
-
- fn should_paint_inline_end_border(&self) -> bool {
- self.inline_end_border.provenance != CollapsedBorderProvenance::FromNextTableCell
- }
-
- fn should_paint_block_start_border(&self) -> bool {
- self.block_start_border.provenance != CollapsedBorderProvenance::FromPreviousTableCell
- }
-
- fn should_paint_block_end_border(&self) -> bool {
- self.block_end_border.provenance != CollapsedBorderProvenance::FromNextTableCell
- }
-
- pub fn adjust_border_widths_for_painting(&self, border_widths: &mut LogicalMargin<Au>) {
- border_widths.inline_start = if !self.should_paint_inline_start_border() {
- Au(0)
- } else {
- self.inline_start_border.width
- };
- border_widths.inline_end = if !self.should_paint_inline_end_border() {
- Au(0)
- } else {
- self.inline_end_border.width
- };
- border_widths.block_start = if !self.should_paint_block_start_border() {
- Au(0)
- } else {
- self.block_start_border.width
- };
- border_widths.block_end = if !self.should_paint_block_end_border() {
- Au(0)
- } else {
- self.block_end_border.width
- }
- }
-
- pub fn adjust_border_bounds_for_painting(
- &self,
- border_bounds: &mut Rect<Au>,
- writing_mode: WritingMode,
- ) {
- let inline_start_divisor = if self.should_paint_inline_start_border() {
- 2
- } else {
- -2
- };
- let inline_start_offset =
- self.inline_start_width / 2 + self.inline_start_border.width / inline_start_divisor;
- let inline_end_divisor = if self.should_paint_inline_end_border() {
- 2
- } else {
- -2
- };
- let inline_end_offset =
- self.inline_end_width / 2 + self.inline_end_border.width / inline_end_divisor;
- let block_start_divisor = if self.should_paint_block_start_border() {
- 2
- } else {
- -2
- };
- let block_start_offset =
- self.block_start_width / 2 + self.block_start_border.width / block_start_divisor;
- let block_end_divisor = if self.should_paint_block_end_border() {
- 2
- } else {
- -2
- };
- let block_end_offset =
- self.block_end_width / 2 + self.block_end_border.width / block_end_divisor;
-
- // FIXME(pcwalton): Get the real container size.
- let mut logical_bounds =
- LogicalRect::from_physical(writing_mode, *border_bounds, Size2D::new(Au(0), Au(0)));
- logical_bounds.start.i = logical_bounds.start.i - inline_start_offset;
- logical_bounds.start.b = logical_bounds.start.b - block_start_offset;
- logical_bounds.size.inline =
- logical_bounds.size.inline + inline_start_offset + inline_end_offset;
- logical_bounds.size.block =
- logical_bounds.size.block + block_start_offset + block_end_offset;
- *border_bounds = logical_bounds.to_physical(writing_mode, Size2D::new(Au(0), Au(0)))
- }
-
- pub fn adjust_border_colors_and_styles_for_painting(
- &self,
- border_colors: &mut SideOffsets2D<Color>,
- border_styles: &mut SideOffsets2D<BorderStyle>,
- writing_mode: WritingMode,
- ) {
- let logical_border_colors = LogicalMargin::new(
- writing_mode,
- self.block_start_border.color,
- self.inline_end_border.color,
- self.block_end_border.color,
- self.inline_start_border.color,
- );
- *border_colors = logical_border_colors.to_physical(writing_mode);
-
- let logical_border_styles = LogicalMargin::new(
- writing_mode,
- self.block_start_border.style,
- self.inline_end_border.style,
- self.block_end_border.style,
- self.inline_start_border.style,
- );
- *border_styles = logical_border_styles.to_physical(writing_mode);
- }
-}
diff --git a/components/layout_2020/table_colgroup.rs b/components/layout_2020/table_colgroup.rs
deleted file mode 100644
index c07ad56f95a..00000000000
--- a/components/layout_2020/table_colgroup.rs
+++ /dev/null
@@ -1,131 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS table formatting contexts.
-
-use crate::context::LayoutContext;
-use crate::display_list::{DisplayListBuildState, StackingContextCollectionState};
-use crate::flow::{BaseFlow, Flow, FlowClass, ForceNonfloatedFlag, OpaqueFlow};
-use crate::fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
-use crate::layout_debug;
-use app_units::Au;
-use euclid::Point2D;
-use std::fmt;
-use style::logical_geometry::LogicalSize;
-use style::properties::ComputedValues;
-use style::values::computed::Size;
-
-#[allow(unsafe_code)]
-unsafe impl crate::flow::HasBaseFlow for TableColGroupFlow {}
-
-/// A table formatting context.
-#[repr(C)]
-pub struct TableColGroupFlow {
- /// Data common to all flows.
- pub base: BaseFlow,
-
- /// The associated fragment.
- pub fragment: Option<Fragment>,
-
- /// The table column fragments
- pub cols: Vec<Fragment>,
-
- /// The specified inline-sizes of table columns. (We use `LengthPercentageOrAuto` here in
- /// lieu of `ColumnInlineSize` because column groups do not establish minimum or preferred
- /// inline sizes.)
- pub inline_sizes: Vec<Size>,
-}
-
-impl TableColGroupFlow {
- pub fn from_fragments(fragment: Fragment, fragments: Vec<Fragment>) -> TableColGroupFlow {
- let writing_mode = fragment.style().writing_mode;
- TableColGroupFlow {
- base: BaseFlow::new(
- Some(fragment.style()),
- writing_mode,
- ForceNonfloatedFlag::ForceNonfloated,
- ),
- fragment: Some(fragment),
- cols: fragments,
- inline_sizes: vec![],
- }
- }
-}
-
-impl Flow for TableColGroupFlow {
- fn class(&self) -> FlowClass {
- FlowClass::TableColGroup
- }
-
- fn as_mut_table_colgroup(&mut self) -> &mut TableColGroupFlow {
- self
- }
-
- fn as_table_colgroup(&self) -> &TableColGroupFlow {
- self
- }
-
- fn bubble_inline_sizes(&mut self) {
- let _scope = layout_debug_scope!(
- "table_colgroup::bubble_inline_sizes {:x}",
- self.base.debug_id()
- );
-
- for fragment in &self.cols {
- // Retrieve the specified value from the appropriate CSS property.
- let inline_size = fragment.style().content_inline_size();
- for _ in 0..fragment.column_span() {
- self.inline_sizes.push(inline_size)
- }
- }
- }
-
- /// Table column inline-sizes are assigned in the table flow and propagated to table row flows
- /// and/or rowgroup flows. Therefore, table colgroup flows do not need to assign inline-sizes.
- fn assign_inline_sizes(&mut self, _: &LayoutContext) {}
-
- /// Table columns do not have block-size.
- fn assign_block_size(&mut self, _: &LayoutContext) {}
-
- fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {}
-
- fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {}
-
- // Table columns are invisible.
- fn build_display_list(&mut self, _: &mut DisplayListBuildState) {}
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
- self.base.stacking_context_id = state.current_stacking_context_id;
- self.base.clipping_and_scrolling = Some(state.current_clipping_and_scrolling);
- }
-
- fn repair_style(&mut self, _: &crate::ServoArc<ComputedValues>) {}
-
- fn compute_overflow(&self) -> Overflow {
- Overflow::new()
- }
-
- fn generated_containing_block_size(&self, _: OpaqueFlow) -> LogicalSize<Au> {
- panic!("Table column groups can't be containing blocks!")
- }
-
- fn iterate_through_fragment_border_boxes(
- &self,
- _: &mut dyn FragmentBorderBoxIterator,
- _: i32,
- _: &Point2D<Au>,
- ) {
- }
-
- fn mutate_fragments(&mut self, _: &mut dyn FnMut(&mut Fragment)) {}
-}
-
-impl fmt::Debug for TableColGroupFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self.fragment {
- Some(ref rb) => write!(f, "TableColGroupFlow: {:?}", rb),
- None => write!(f, "TableColGroupFlow"),
- }
- }
-}
diff --git a/components/layout_2020/table_row.rs b/components/layout_2020/table_row.rs
deleted file mode 100644
index 824da31edad..00000000000
--- a/components/layout_2020/table_row.rs
+++ /dev/null
@@ -1,1158 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS table formatting contexts.
-
-use crate::block::{BlockFlow, ISizeAndMarginsComputer};
-use crate::context::LayoutContext;
-use crate::display_list::{
- DisplayListBuildState, StackingContextCollectionFlags, StackingContextCollectionState,
-};
-use crate::flow::{
- EarlyAbsolutePositionInfo, Flow, FlowClass, GetBaseFlow, ImmutableFlowUtils, OpaqueFlow,
-};
-use crate::flow_list::MutFlowListIterator;
-use crate::fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
-use crate::layout_debug;
-use crate::table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable, VecExt};
-use crate::table_cell::{CollapsedBordersForCell, TableCellFlow};
-use app_units::Au;
-use euclid::Point2D;
-use gfx_traits::print_tree::PrintTree;
-use serde::{Serialize, Serializer};
-use std::cmp::max;
-use std::fmt;
-use std::iter::{Enumerate, IntoIterator, Peekable};
-use style::computed_values::border_collapse::T as BorderCollapse;
-use style::computed_values::border_spacing::T as BorderSpacing;
-use style::computed_values::border_top_style::T as BorderStyle;
-use style::logical_geometry::{LogicalSize, PhysicalSide, WritingMode};
-use style::properties::ComputedValues;
-use style::values::computed::{Color, Size};
-
-#[allow(unsafe_code)]
-unsafe impl crate::flow::HasBaseFlow for TableRowFlow {}
-
-/// A single row of a table.
-#[repr(C)]
-pub struct TableRowFlow {
- /// Fields common to all block flows.
- pub block_flow: BlockFlow,
-
- /// Information about the intrinsic inline-sizes of each cell.
- pub cell_intrinsic_inline_sizes: Vec<CellIntrinsicInlineSize>,
-
- /// Information about the computed inline-sizes of each column.
- pub column_computed_inline_sizes: Vec<ColumnComputedInlineSize>,
-
- /// The number of remaining rows spanned by cells in previous rows, indexed by column.
- ///
- /// Columns that are not included in this vector have the default rowspan of "1". If there are
- /// no cells with rowspan != 1 in previous rows, this vector may be empty.
- pub incoming_rowspan: Vec<u32>,
-
- /// The spacing for this row, propagated down from the table during the inline-size assignment
- /// phase.
- pub spacing: BorderSpacing,
-
- /// The direction of the columns, propagated down from the table during the inline-size
- /// assignment phase.
- pub table_writing_mode: WritingMode,
-
- /// Information about the borders for each cell that we bubble up to our parent. This is only
- /// computed if `border-collapse` is `collapse`.
- pub preliminary_collapsed_borders: CollapsedBordersForRow,
-
- /// Information about the borders for each cell, post-collapse. This is only computed if
- /// `border-collapse` is `collapse`.
- pub final_collapsed_borders: CollapsedBordersForRow,
-
- /// The computed cell spacing widths post-collapse.
- pub collapsed_border_spacing: CollapsedBorderSpacingForRow,
-}
-
-impl Serialize for TableRowFlow {
- fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
- self.block_flow.serialize(serializer)
- }
-}
-
-/// Information about the column inline size and span for each cell.
-#[derive(Clone, Copy, Serialize)]
-pub struct CellIntrinsicInlineSize {
- /// Inline sizes that this cell contributes to the column.
- pub column_size: ColumnIntrinsicInlineSize,
- /// The column span of this cell.
- pub column_span: u32,
- /// The row span of this cell.
- pub row_span: u32,
-}
-
-impl TableRowFlow {
- pub fn from_fragment(fragment: Fragment) -> TableRowFlow {
- let writing_mode = fragment.style().writing_mode;
- TableRowFlow {
- block_flow: BlockFlow::from_fragment(fragment),
- cell_intrinsic_inline_sizes: Vec::new(),
- column_computed_inline_sizes: Vec::new(),
- incoming_rowspan: Vec::new(),
- spacing: BorderSpacing::zero(),
- table_writing_mode: writing_mode,
- preliminary_collapsed_borders: CollapsedBordersForRow::new(),
- final_collapsed_borders: CollapsedBordersForRow::new(),
- collapsed_border_spacing: CollapsedBorderSpacingForRow::new(),
- }
- }
-
- /// Compute block-size for table-row flow.
- ///
- /// TODO(pcwalton): This doesn't handle floats and positioned elements right.
- ///
- /// Returns the block size
- pub fn compute_block_size_table_row_base<'a>(
- &'a mut self,
- layout_context: &LayoutContext,
- incoming_rowspan_data: &mut Vec<Au>,
- border_info: &[TableRowSizeData],
- row_index: usize,
- ) -> Au {
- fn include_sizes_from_previous_rows(
- col: &mut usize,
- incoming_rowspan: &[u32],
- incoming_rowspan_data: &mut Vec<Au>,
- max_block_size: &mut Au,
- ) {
- while let Some(span) = incoming_rowspan.get(*col) {
- if *span == 1 {
- break;
- }
- let incoming = if let Some(incoming) = incoming_rowspan_data.get(*col) {
- *incoming
- } else {
- // This happens when we have a cell with both rowspan and colspan
- // incoming_rowspan_data only records the data for the first column,
- // but that's ok because we only need to account for each spanning cell
- // once. So we skip ahead.
- *col += 1;
- continue;
- };
- *max_block_size = max(*max_block_size, incoming);
- *col += 1;
- }
- }
- // Per CSS 2.1 § 17.5.3, find max_y = max(computed `block-size`, minimum block-size of
- // all cells).
- let mut max_block_size = Au(0);
- let thread_id = self.block_flow.base.thread_id;
- let content_box = self.block_flow.base.position -
- self.block_flow.fragment.border_padding -
- self.block_flow.fragment.margin;
-
- let mut col = 0;
- for kid in self.block_flow.base.child_iter_mut() {
- include_sizes_from_previous_rows(
- &mut col,
- &self.incoming_rowspan,
- incoming_rowspan_data,
- &mut max_block_size,
- );
- kid.place_float_if_applicable();
- debug_assert!(
- !kid.base().flags.is_float(),
- "table cells should never float"
- );
- kid.assign_block_size_for_inorder_child_if_necessary(
- layout_context,
- thread_id,
- content_box,
- );
-
- let mut row_span;
- let column_span;
- let cell_total;
- {
- let cell = kid.as_mut_table_cell();
- row_span = cell.row_span;
- column_span = cell.column_span as usize;
- cell_total = cell.total_block_size();
- }
- let child_node = kid.mut_base();
- child_node.position.start.b = Au(0);
- let mut cell_block_size_pressure = max(cell_total, child_node.position.size.block);
-
- if row_span != 1 {
- if incoming_rowspan_data.len() <= col {
- incoming_rowspan_data.resize(col + 1, Au(0));
- }
- let border_sizes_spanned =
- get_spanned_border_size(border_info, row_index, &mut row_span);
-
- cell_block_size_pressure -= border_sizes_spanned;
-
- // XXXManishearth in case this row covers more than cell_block_size_pressure / row_span
- // anyway, we should use that to reduce the pressure on future rows. This will
- // require an extra slow-path loop, sadly.
- cell_block_size_pressure /= row_span as i32;
- incoming_rowspan_data[col] = cell_block_size_pressure;
- }
-
- max_block_size = max(max_block_size, cell_block_size_pressure);
- col += column_span;
- }
- include_sizes_from_previous_rows(
- &mut col,
- &self.incoming_rowspan,
- incoming_rowspan_data,
- &mut max_block_size,
- );
-
- // TODO: Percentage block-size
- let block_size = self
- .block_flow
- .fragment
- .style()
- .content_block_size()
- .to_used_value(Au(0))
- .unwrap_or(max_block_size);
- max(block_size, max_block_size)
- }
-
- pub fn assign_block_size_to_self_and_children(
- &mut self,
- sizes: &[TableRowSizeData],
- index: usize,
- ) {
- // Assign the block-size of kid fragments, which is the same value as own block-size.
- let block_size = sizes[index].size;
- for kid in self.block_flow.base.child_iter_mut() {
- let child_table_cell = kid.as_mut_table_cell();
- let block_size = if child_table_cell.row_span != 1 {
- let mut row_span = child_table_cell.row_span;
- let border_sizes_spanned = get_spanned_border_size(sizes, index, &mut row_span);
- let row_sizes = sizes[index..]
- .iter()
- .take(row_span as usize)
- .fold(Au(0), |accum, r| accum + r.size);
- row_sizes + border_sizes_spanned
- } else {
- block_size
- };
- {
- let kid_fragment = child_table_cell.mut_fragment();
- let mut position = kid_fragment.border_box;
- position.size.block = block_size;
- kid_fragment.border_box = position;
- }
-
- // Assign the child's block size.
- child_table_cell.block_flow.base.position.size.block = block_size;
-
- // Now we know the cell height, vertical align the cell's children.
- child_table_cell.valign_children();
-
- // Write in the size of the relative containing block for children. (This
- // information is also needed to handle RTL.)
- child_table_cell
- .block_flow
- .base
- .early_absolute_position_info = EarlyAbsolutePositionInfo {
- relative_containing_block_size: self.block_flow.fragment.content_box().size,
- relative_containing_block_mode: self.block_flow.fragment.style().writing_mode,
- };
- }
-
- // Assign the block-size of own fragment
- let mut position = self.block_flow.fragment.border_box;
- position.size.block = block_size;
- self.block_flow.fragment.border_box = position;
- self.block_flow.base.position.size.block = block_size;
- }
-
- pub fn populate_collapsed_border_spacing<'a, I>(
- &mut self,
- collapsed_inline_direction_border_widths_for_table: &[Au],
- collapsed_block_direction_border_widths_for_table: &mut Peekable<I>,
- ) where
- I: Iterator<Item = &'a Au>,
- {
- self.collapsed_border_spacing.inline.clear();
- self.collapsed_border_spacing.inline.extend(
- collapsed_inline_direction_border_widths_for_table
- .into_iter()
- .map(|x| *x),
- );
-
- if let Some(collapsed_block_direction_border_width_for_table) =
- collapsed_block_direction_border_widths_for_table.next()
- {
- self.collapsed_border_spacing.block_start =
- *collapsed_block_direction_border_width_for_table
- }
- if let Some(collapsed_block_direction_border_width_for_table) =
- collapsed_block_direction_border_widths_for_table.peek()
- {
- self.collapsed_border_spacing.block_end =
- **collapsed_block_direction_border_width_for_table
- }
- }
-}
-
-#[derive(Debug, Default)]
-pub struct TableRowSizeData {
- /// The block-size of the row.
- pub size: Au,
- /// Border spacing up to this row (not including spacing below the row)
- pub cumulative_border_spacing: Au,
- /// The "segment" of the table it is in. Tables containing
- /// both row groups and rows have the bare rows grouped in
- /// segments separated by row groups. It's helpful to look
- /// at these as if they are rowgroups themselves.
- ///
- /// This is enough information for us to be able to check whether we
- /// are in a case where we are overflowing a rowgroup with rowspan,
- /// however calculating the amount of overflow requires lookahead.
- pub rowgroup_id: u32,
-}
-
-/// Given an array of (_, cumulative_border_size), the index of the
-/// current row, and the >1 row_span of the cell, calculate the amount of
-/// border-spacing spanned by the row. In case the rowspan was larger
-/// than required, this will fix it up.
-fn get_spanned_border_size(sizes: &[TableRowSizeData], row_index: usize, row_span: &mut u32) -> Au {
- // A zero rowspan is functionally equivalent to rowspan=infinity
- if *row_span == 0 || row_index + *row_span as usize > sizes.len() {
- *row_span = (sizes.len() - row_index) as u32;
- }
- let mut last_row_idx = row_index + *row_span as usize - 1;
- // This is a slow path and should be rare -- this should only get triggered
- // when you use `rowspan=0` or an overlarge rowspan in a table with
- // mixed rows + rowgroups
- if sizes[last_row_idx].rowgroup_id != sizes[row_index].rowgroup_id {
- // XXXManishearth this loop can be avoided by also storing
- // a "last_rowgroup_at" index so we can leapfrog back quickly
- *row_span = sizes[row_index..last_row_idx + 1]
- .iter()
- .position(|s| s.rowgroup_id != sizes[row_index].rowgroup_id)
- .unwrap() as u32;
- last_row_idx = row_index + *row_span as usize - 1;
- }
- sizes[last_row_idx].cumulative_border_spacing - sizes[row_index].cumulative_border_spacing
-}
-
-impl Flow for TableRowFlow {
- fn class(&self) -> FlowClass {
- FlowClass::TableRow
- }
-
- fn as_mut_table_row(&mut self) -> &mut TableRowFlow {
- self
- }
-
- fn as_table_row(&self) -> &TableRowFlow {
- self
- }
-
- fn as_mut_block(&mut self) -> &mut BlockFlow {
- &mut self.block_flow
- }
-
- fn as_block(&self) -> &BlockFlow {
- &self.block_flow
- }
-
- /// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When
- /// called on this context, all child contexts have had their min/pref inline-sizes set. This
- /// function must decide min/pref inline-sizes based on child context inline-sizes and
- /// dimensions of any fragments it is responsible for flowing.
- /// Min/pref inline-sizes set by this function are used in automatic table layout calculation.
- /// The specified column inline-sizes of children cells are used in fixed table layout
- /// calculation.
- fn bubble_inline_sizes(&mut self) {
- let _scope = layout_debug_scope!(
- "table_row::bubble_inline_sizes {:x}",
- self.block_flow.base.debug_id()
- );
-
- // Bubble up the specified inline-sizes from child table cells.
- let (mut min_inline_size, mut pref_inline_size) = (Au(0), Au(0));
- let collapsing_borders = self
- .block_flow
- .fragment
- .style()
- .get_inherited_table()
- .border_collapse ==
- BorderCollapse::Collapse;
- let row_style = &*self.block_flow.fragment.style;
- self.preliminary_collapsed_borders
- .reset(CollapsedBorder::inline_start(
- &row_style,
- CollapsedBorderProvenance::FromTableRow,
- ));
-
- {
- let children_count = self.block_flow.base.children.len();
- let mut iterator = self.block_flow.base.child_iter_mut().enumerate().peekable();
- while let Some((i, kid)) = iterator.next() {
- assert!(kid.is_table_cell());
-
- // Collect the specified column inline-size of the cell. This is used in both
- // fixed and automatic table layout calculation.
- let child_specified_inline_size;
- let child_column_span;
- let child_row_span;
- {
- let child_table_cell = kid.as_mut_table_cell();
- child_specified_inline_size = child_table_cell
- .block_flow
- .fragment
- .style
- .content_inline_size();
- child_column_span = child_table_cell.column_span;
- child_row_span = child_table_cell.row_span;
-
- // Perform border collapse if necessary.
- if collapsing_borders {
- perform_inline_direction_border_collapse_for_row(
- row_style,
- children_count,
- i,
- child_table_cell,
- &mut iterator,
- &mut self.preliminary_collapsed_borders,
- )
- }
- }
-
- // Collect minimum and preferred inline-sizes of the cell for automatic table layout
- // calculation.
- let child_base = kid.mut_base();
- let child_column_inline_size = ColumnIntrinsicInlineSize {
- minimum_length: match child_specified_inline_size {
- Size::Auto => None,
- Size::LengthPercentage(ref lp) => lp.0.maybe_to_used_value(None),
- }
- .unwrap_or(child_base.intrinsic_inline_sizes.minimum_inline_size),
- percentage: match child_specified_inline_size {
- Size::Auto => 0.0,
- Size::LengthPercentage(ref lp) => lp.0.as_percentage().map_or(0.0, |p| p.0),
- },
- preferred: child_base.intrinsic_inline_sizes.preferred_inline_size,
- constrained: match child_specified_inline_size {
- Size::Auto => false,
- Size::LengthPercentage(ref lp) => lp.0.maybe_to_used_value(None).is_some(),
- },
- };
- min_inline_size = min_inline_size + child_column_inline_size.minimum_length;
- pref_inline_size = pref_inline_size + child_column_inline_size.preferred;
- self.cell_intrinsic_inline_sizes
- .push(CellIntrinsicInlineSize {
- column_size: child_column_inline_size,
- column_span: child_column_span,
- row_span: child_row_span,
- });
- }
- }
-
- self.block_flow
- .base
- .intrinsic_inline_sizes
- .minimum_inline_size = min_inline_size;
- self.block_flow
- .base
- .intrinsic_inline_sizes
- .preferred_inline_size = max(min_inline_size, pref_inline_size);
- }
-
- fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
- let _scope = layout_debug_scope!(
- "table_row::assign_inline_sizes {:x}",
- self.block_flow.base.debug_id()
- );
- debug!(
- "assign_inline_sizes({}): assigning inline_size for flow",
- "table_row"
- );
-
- let shared_context = layout_context.shared_context();
- // The position was set to the containing block by the flow's parent.
- let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
- // FIXME: In case of border-collapse: collapse, inline_start_content_edge should be
- // border_inline_start.
- let inline_start_content_edge = Au(0);
- let inline_end_content_edge = Au(0);
-
- let inline_size_computer = InternalTable;
- inline_size_computer.compute_used_inline_size(
- &mut self.block_flow,
- shared_context,
- containing_block_inline_size,
- );
-
- // Spread out the completed inline sizes among columns with spans > 1.
- let num_columns = self.column_computed_inline_sizes.len();
- let mut computed_inline_size_for_cells = Vec::with_capacity(num_columns);
- let mut col = 0;
-
- for cell_intrinsic_inline_size in &self.cell_intrinsic_inline_sizes {
- // Skip any column occupied by a cell from a previous row.
- while col < self.incoming_rowspan.len() && self.incoming_rowspan[col] != 1 {
- let size = match self.column_computed_inline_sizes.get(col) {
- Some(column_computed_inline_size) => *column_computed_inline_size,
- None => ColumnComputedInlineSize { size: Au(0) }, // See FIXME below.
- };
- computed_inline_size_for_cells.push(size);
- col += 1;
- }
- // Start with the computed inline size for the first column in the span.
- let mut column_computed_inline_size = match self.column_computed_inline_sizes.get(col) {
- Some(column_computed_inline_size) => *column_computed_inline_size,
- None => {
- // We're in fixed layout mode and there are more cells in this row than
- // columns we know about. According to CSS 2.1 § 17.5.2.1, the behavior is
- // now undefined. So just use zero.
- //
- // FIXME(pcwalton): $10 says this isn't Web compatible.
- ColumnComputedInlineSize { size: Au(0) }
- },
- };
- col += 1;
-
- // Add in computed inline sizes for any extra columns in the span.
- for _ in 1..cell_intrinsic_inline_size.column_span {
- let extra_column_computed_inline_size =
- match self.column_computed_inline_sizes.get(col) {
- Some(column_computed_inline_size) => column_computed_inline_size,
- None => break,
- };
- column_computed_inline_size.size = column_computed_inline_size.size +
- extra_column_computed_inline_size.size +
- self.spacing.horizontal();
- col += 1;
- }
-
- computed_inline_size_for_cells.push(column_computed_inline_size)
- }
-
- // Set up border collapse info.
- let border_collapse_info = match self
- .block_flow
- .fragment
- .style()
- .get_inherited_table()
- .border_collapse
- {
- BorderCollapse::Collapse => Some(BorderCollapseInfoForChildTableCell {
- collapsed_borders_for_row: &self.final_collapsed_borders,
- collapsed_border_spacing_for_row: &self.collapsed_border_spacing,
- }),
- BorderCollapse::Separate => None,
- };
-
- // Push those inline sizes down to the cells.
- let spacing = self.spacing;
- let row_writing_mode = self.block_flow.base.writing_mode;
- let table_writing_mode = self.table_writing_mode;
- let incoming_rowspan = &self.incoming_rowspan;
- let mut column_index = 0;
-
- self.block_flow.propagate_assigned_inline_size_to_children(
- shared_context,
- inline_start_content_edge,
- inline_end_content_edge,
- containing_block_inline_size,
- |child_flow,
- child_index,
- content_inline_size,
- _writing_mode,
- inline_start_margin_edge,
- inline_end_margin_edge| {
- set_inline_position_of_child_flow(
- child_flow,
- child_index,
- &mut column_index,
- incoming_rowspan,
- row_writing_mode,
- table_writing_mode,
- &computed_inline_size_for_cells,
- &spacing,
- &border_collapse_info,
- content_inline_size,
- inline_start_margin_edge,
- inline_end_margin_edge,
- );
- },
- )
- }
-
- fn assign_block_size(&mut self, _: &LayoutContext) {
- // the surrounding table or rowgroup does this
- }
-
- fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
- self.block_flow
- .compute_stacking_relative_position(layout_context)
- }
-
- fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
- self.block_flow
- .update_late_computed_inline_position_if_necessary(inline_position)
- }
-
- fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
- self.block_flow
- .update_late_computed_block_position_if_necessary(block_position)
- }
-
- fn build_display_list(&mut self, _: &mut DisplayListBuildState) {
- use style::servo::restyle_damage::ServoRestyleDamage;
- // handled in TableCellStyleInfo::build_display_list
- // we skip setting the damage in TableCellStyleInfo::build_display_list()
- // because we only have immutable access
- self.block_flow
- .fragment
- .restyle_damage
- .remove(ServoRestyleDamage::REPAINT);
- }
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
- self.block_flow
- .collect_stacking_contexts_for_block(state, StackingContextCollectionFlags::empty());
- }
-
- fn repair_style(&mut self, new_style: &crate::ServoArc<ComputedValues>) {
- self.block_flow.repair_style(new_style)
- }
-
- fn compute_overflow(&self) -> Overflow {
- self.block_flow.compute_overflow()
- }
-
- fn contains_roots_of_absolute_flow_tree(&self) -> bool {
- self.block_flow.contains_roots_of_absolute_flow_tree()
- }
-
- fn is_absolute_containing_block(&self) -> bool {
- self.block_flow.is_absolute_containing_block()
- }
-
- fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
- self.block_flow.generated_containing_block_size(flow)
- }
-
- fn iterate_through_fragment_border_boxes(
- &self,
- iterator: &mut dyn FragmentBorderBoxIterator,
- level: i32,
- stacking_context_position: &Point2D<Au>,
- ) {
- self.block_flow.iterate_through_fragment_border_boxes(
- iterator,
- level,
- stacking_context_position,
- )
- }
-
- fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
- self.block_flow.mutate_fragments(mutator)
- }
-
- fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
- self.block_flow.print_extra_flow_children(print_tree);
- }
-}
-
-impl fmt::Debug for TableRowFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "TableRowFlow: {:?}", self.block_flow)
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct CollapsedBordersForRow {
- /// The size of this vector should be equal to the number of cells plus one.
- pub inline: Vec<CollapsedBorder>,
- /// The size of this vector should be equal to the number of cells.
- pub block_start: Vec<CollapsedBorder>,
- /// The size of this vector should be equal to the number of cells.
- pub block_end: Vec<CollapsedBorder>,
-}
-
-impl CollapsedBordersForRow {
- pub fn new() -> CollapsedBordersForRow {
- CollapsedBordersForRow {
- inline: Vec::new(),
- block_start: Vec::new(),
- block_end: Vec::new(),
- }
- }
-
- pub fn reset(&mut self, first_inline_border: CollapsedBorder) {
- self.inline.clear();
- self.inline.push(first_inline_border);
- self.block_start.clear();
- self.block_end.clear()
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct CollapsedBorderSpacingForRow {
- /// The spacing in between each column.
- inline: Vec<Au>,
- /// The spacing above this row.
- pub block_start: Au,
- /// The spacing below this row.
- block_end: Au,
-}
-
-impl CollapsedBorderSpacingForRow {
- fn new() -> CollapsedBorderSpacingForRow {
- CollapsedBorderSpacingForRow {
- inline: Vec::new(),
- block_start: Au(0),
- block_end: Au(0),
- }
- }
-}
-
-/// All aspects of a border that can collapse with adjacent borders. See CSS 2.1 § 17.6.2.1.
-#[derive(Clone, Copy, Debug)]
-pub struct CollapsedBorder {
- /// The style of the border.
- pub style: BorderStyle,
- /// The width of the border.
- pub width: Au,
- /// The color of the border.
- pub color: Color,
- /// The type of item that this border comes from.
- pub provenance: CollapsedBorderProvenance,
-}
-
-impl Serialize for CollapsedBorder {
- fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
- serializer.serialize_unit()
- }
-}
-
-/// Where a border style comes from.
-///
-/// The integer values here correspond to the border conflict resolution rules in CSS 2.1 §
-/// 17.6.2.1. Higher values override lower values.
-// FIXME(#8586): FromTableRow, FromTableRowGroup, FromTableColumn,
-// FromTableColumnGroup are unused
-#[allow(dead_code)]
-#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
-pub enum CollapsedBorderProvenance {
- FromPreviousTableCell = 6,
- FromNextTableCell = 5,
- FromTableRow = 4,
- FromTableRowGroup = 3,
- FromTableColumn = 2,
- FromTableColumnGroup = 1,
- FromTable = 0,
-}
-
-impl CollapsedBorder {
- /// Creates a collapsible border style for no border.
- pub fn new() -> CollapsedBorder {
- CollapsedBorder {
- style: BorderStyle::None,
- width: Au(0),
- color: Color::transparent(),
- provenance: CollapsedBorderProvenance::FromTable,
- }
- }
-
- /// Creates a collapsed border from the block-start border described in the given CSS style
- /// object.
- fn top(css_style: &ComputedValues, provenance: CollapsedBorderProvenance) -> CollapsedBorder {
- CollapsedBorder {
- style: css_style.get_border().border_top_style,
- width: Au::from(css_style.get_border().border_top_width),
- color: css_style.get_border().border_top_color,
- provenance: provenance,
- }
- }
-
- /// Creates a collapsed border style from the right border described in the given CSS style
- /// object.
- fn right(css_style: &ComputedValues, provenance: CollapsedBorderProvenance) -> CollapsedBorder {
- CollapsedBorder {
- style: css_style.get_border().border_right_style,
- width: Au::from(css_style.get_border().border_right_width),
- color: css_style.get_border().border_right_color,
- provenance: provenance,
- }
- }
-
- /// Creates a collapsed border style from the bottom border described in the given CSS style
- /// object.
- fn bottom(
- css_style: &ComputedValues,
- provenance: CollapsedBorderProvenance,
- ) -> CollapsedBorder {
- CollapsedBorder {
- style: css_style.get_border().border_bottom_style,
- width: Au::from(css_style.get_border().border_bottom_width),
- color: css_style.get_border().border_bottom_color,
- provenance: provenance,
- }
- }
-
- /// Creates a collapsed border style from the left border described in the given CSS style
- /// object.
- fn left(css_style: &ComputedValues, provenance: CollapsedBorderProvenance) -> CollapsedBorder {
- CollapsedBorder {
- style: css_style.get_border().border_left_style,
- width: Au::from(css_style.get_border().border_left_width),
- color: css_style.get_border().border_left_color,
- provenance: provenance,
- }
- }
-
- /// Creates a collapsed border style from the given physical side.
- fn from_side(
- side: PhysicalSide,
- css_style: &ComputedValues,
- provenance: CollapsedBorderProvenance,
- ) -> CollapsedBorder {
- match side {
- PhysicalSide::Top => CollapsedBorder::top(css_style, provenance),
- PhysicalSide::Right => CollapsedBorder::right(css_style, provenance),
- PhysicalSide::Bottom => CollapsedBorder::bottom(css_style, provenance),
- PhysicalSide::Left => CollapsedBorder::left(css_style, provenance),
- }
- }
-
- /// Creates a collapsed border style from the inline-start border described in the given CSS
- /// style object.
- pub fn inline_start(
- css_style: &ComputedValues,
- provenance: CollapsedBorderProvenance,
- ) -> CollapsedBorder {
- CollapsedBorder::from_side(
- css_style.writing_mode.inline_start_physical_side(),
- css_style,
- provenance,
- )
- }
-
- /// Creates a collapsed border style from the inline-start border described in the given CSS
- /// style object.
- pub fn inline_end(
- css_style: &ComputedValues,
- provenance: CollapsedBorderProvenance,
- ) -> CollapsedBorder {
- CollapsedBorder::from_side(
- css_style.writing_mode.inline_end_physical_side(),
- css_style,
- provenance,
- )
- }
-
- /// Creates a collapsed border style from the block-start border described in the given CSS
- /// style object.
- pub fn block_start(
- css_style: &ComputedValues,
- provenance: CollapsedBorderProvenance,
- ) -> CollapsedBorder {
- CollapsedBorder::from_side(
- css_style.writing_mode.block_start_physical_side(),
- css_style,
- provenance,
- )
- }
-
- /// Creates a collapsed border style from the block-end border described in the given CSS style
- /// object.
- pub fn block_end(
- css_style: &ComputedValues,
- provenance: CollapsedBorderProvenance,
- ) -> CollapsedBorder {
- CollapsedBorder::from_side(
- css_style.writing_mode.block_end_physical_side(),
- css_style,
- provenance,
- )
- }
-
- /// If `other` has a higher priority per CSS 2.1 § 17.6.2.1, replaces `self` with it.
- pub fn combine(&mut self, other: &CollapsedBorder) {
- match (self.style, other.style) {
- // Step 1.
- (BorderStyle::Hidden, _) => {},
- (_, BorderStyle::Hidden) => *self = *other,
- // Step 2.
- (BorderStyle::None, _) => *self = *other,
- (_, BorderStyle::None) => {},
- // Step 3.
- _ if self.width > other.width => {},
- _ if self.width < other.width => *self = *other,
- (this_style, other_style) if this_style > other_style => {},
- (this_style, other_style) if this_style < other_style => *self = *other,
- // Step 4.
- _ if (self.provenance as i8) >= other.provenance as i8 => {},
- _ => *self = *other,
- }
- }
-}
-
-/// Pushes column inline size, incoming rowspan, and border collapse info down to a child.
-pub fn propagate_column_inline_sizes_to_child(
- child_flow: &mut dyn Flow,
- table_writing_mode: WritingMode,
- column_computed_inline_sizes: &[ColumnComputedInlineSize],
- border_spacing: &BorderSpacing,
- incoming_rowspan: &mut Vec<u32>,
-) {
- // If the child is a row group or a row, the column inline-size and rowspan info should be copied from its
- // parent.
- //
- // FIXME(pcwalton): This seems inefficient. Reference count it instead?
- match child_flow.class() {
- FlowClass::TableRowGroup => {
- incoming_rowspan.clear();
- let child_table_rowgroup_flow = child_flow.as_mut_table_rowgroup();
- child_table_rowgroup_flow.spacing = *border_spacing;
- for kid in child_table_rowgroup_flow.block_flow.base.child_iter_mut() {
- propagate_column_inline_sizes_to_child(
- kid,
- table_writing_mode,
- column_computed_inline_sizes,
- border_spacing,
- incoming_rowspan,
- );
- }
- },
- FlowClass::TableRow => {
- let child_table_row_flow = child_flow.as_mut_table_row();
- child_table_row_flow.column_computed_inline_sizes =
- column_computed_inline_sizes.to_vec();
- child_table_row_flow.spacing = *border_spacing;
- child_table_row_flow.table_writing_mode = table_writing_mode;
- child_table_row_flow.incoming_rowspan = incoming_rowspan.clone();
-
- // Update the incoming rowspan for the next row.
- let mut col = 0;
- for cell in &child_table_row_flow.cell_intrinsic_inline_sizes {
- // Skip any column occupied by a cell from a previous row.
- while col < incoming_rowspan.len() && incoming_rowspan[col] != 1 {
- if incoming_rowspan[col] > 1 {
- incoming_rowspan[col] -= 1;
- }
- col += 1;
- }
- for _ in 0..cell.column_span {
- if col < incoming_rowspan.len() && incoming_rowspan[col] > 1 {
- incoming_rowspan[col] -= 1;
- }
- // If this cell spans later rows, record its rowspan.
- if cell.row_span != 1 {
- if incoming_rowspan.len() < col + 1 {
- incoming_rowspan.resize(col + 1, 1);
- }
- // HTML § 4.9.11: For rowspan, the value 0 means the cell is to span all
- // the remaining rows in the rowgroup.
- if cell.row_span > incoming_rowspan[col] || cell.row_span == 0 {
- incoming_rowspan[col] = cell.row_span;
- }
- }
- col += 1;
- }
- }
- },
- c => warn!("unexpected flow in table {:?}", c),
- }
-}
-
-/// Lay out table cells inline according to the computer column sizes.
-fn set_inline_position_of_child_flow(
- child_flow: &mut dyn Flow,
- child_index: usize,
- column_index: &mut usize,
- incoming_rowspan: &[u32],
- row_writing_mode: WritingMode,
- table_writing_mode: WritingMode,
- column_computed_inline_sizes: &[ColumnComputedInlineSize],
- border_spacing: &BorderSpacing,
- border_collapse_info: &Option<BorderCollapseInfoForChildTableCell>,
- parent_content_inline_size: Au,
- inline_start_margin_edge: &mut Au,
- inline_end_margin_edge: &mut Au,
-) {
- if !child_flow.is_table_cell() {
- return;
- }
-
- let reverse_column_order = table_writing_mode.is_bidi_ltr() != row_writing_mode.is_bidi_ltr();
-
- // Advance past any column occupied by a cell from a previous row.
- while *column_index < incoming_rowspan.len() && incoming_rowspan[*column_index] != 1 {
- let column_inline_size = column_computed_inline_sizes[*column_index].size;
- let border_inline_size = match *border_collapse_info {
- Some(_) => Au(0), // FIXME: Make collapsed borders account for colspan/rowspan.
- None => border_spacing.horizontal(),
- };
- if reverse_column_order {
- *inline_end_margin_edge += column_inline_size + border_inline_size;
- } else {
- *inline_start_margin_edge += column_inline_size + border_inline_size;
- }
- *column_index += 1;
- }
-
- // Handle border collapsing, if necessary.
- let child_table_cell = child_flow.as_mut_table_cell();
- match *border_collapse_info {
- Some(ref border_collapse_info) => {
- // Write in the child's border collapse state.
- child_table_cell.collapsed_borders = CollapsedBordersForCell {
- inline_start_border: border_collapse_info
- .collapsed_borders_for_row
- .inline
- .get(child_index)
- .map_or(CollapsedBorder::new(), |x| *x),
- inline_end_border: border_collapse_info
- .collapsed_borders_for_row
- .inline
- .get(child_index + 1)
- .map_or(CollapsedBorder::new(), |x| *x),
- block_start_border: border_collapse_info
- .collapsed_borders_for_row
- .block_start
- .get(child_index)
- .map_or(CollapsedBorder::new(), |x| *x),
- block_end_border: border_collapse_info
- .collapsed_borders_for_row
- .block_end
- .get(child_index)
- .map_or(CollapsedBorder::new(), |x| *x),
- inline_start_width: border_collapse_info
- .collapsed_border_spacing_for_row
- .inline
- .get(child_index)
- .map_or(Au(0), |x| *x),
- inline_end_width: border_collapse_info
- .collapsed_border_spacing_for_row
- .inline
- .get(child_index + 1)
- .map_or(Au(0), |x| *x),
- block_start_width: border_collapse_info
- .collapsed_border_spacing_for_row
- .block_start,
- block_end_width: border_collapse_info
- .collapsed_border_spacing_for_row
- .block_end,
- };
-
- // Move over past the collapsed border.
- if reverse_column_order {
- *inline_end_margin_edge += child_table_cell.collapsed_borders.inline_start_width;
- } else {
- *inline_start_margin_edge += child_table_cell.collapsed_borders.inline_start_width;
- }
- },
- None => {
- // Take spacing into account.
- if reverse_column_order {
- *inline_end_margin_edge += border_spacing.horizontal();
- } else {
- *inline_start_margin_edge += border_spacing.horizontal();
- }
- },
- }
-
- let column_inline_size = column_computed_inline_sizes[*column_index].size;
- *column_index += 1;
-
- let kid_base = &mut child_table_cell.block_flow.base;
- kid_base.block_container_inline_size = column_inline_size;
-
- if reverse_column_order {
- // Columns begin from the inline-end edge.
- kid_base.position.start.i =
- parent_content_inline_size - *inline_end_margin_edge - column_inline_size;
- *inline_end_margin_edge += column_inline_size;
- } else {
- // Columns begin from the inline-start edge.
- kid_base.position.start.i = *inline_start_margin_edge;
- *inline_start_margin_edge += column_inline_size;
- }
-}
-
-#[derive(Clone, Copy)]
-pub struct BorderCollapseInfoForChildTableCell<'a> {
- collapsed_borders_for_row: &'a CollapsedBordersForRow,
- collapsed_border_spacing_for_row: &'a CollapsedBorderSpacingForRow,
-}
-
-/// Performs border-collapse in the inline direction for all the cells' inside borders in the
-/// inline-direction cells and propagates the outside borders (the far left and right) up to the
-/// table row. This is done eagerly here so that at least the inline inside border collapse
-/// computations can be parallelized across all the rows of the table.
-fn perform_inline_direction_border_collapse_for_row(
- row_style: &ComputedValues,
- children_count: usize,
- child_index: usize,
- child_table_cell: &mut TableCellFlow,
- iterator: &mut Peekable<Enumerate<MutFlowListIterator>>,
- preliminary_collapsed_borders: &mut CollapsedBordersForRow,
-) {
- // In the first cell, combine its border with the one coming from the row.
- if child_index == 0 {
- let first_inline_border = &mut preliminary_collapsed_borders.inline[0];
- first_inline_border.combine(&CollapsedBorder::inline_start(
- &*child_table_cell.block_flow.fragment.style,
- CollapsedBorderProvenance::FromNextTableCell,
- ));
- }
-
- let inline_collapsed_border = preliminary_collapsed_borders.inline.push_or_set(
- child_index + 1,
- CollapsedBorder::inline_end(
- &*child_table_cell.block_flow.fragment.style,
- CollapsedBorderProvenance::FromPreviousTableCell,
- ),
- );
-
- if let Some(&(_, ref next_child_flow)) = iterator.peek() {
- let next_child_flow = next_child_flow.as_block();
- inline_collapsed_border.combine(&CollapsedBorder::inline_start(
- &*next_child_flow.fragment.style,
- CollapsedBorderProvenance::FromNextTableCell,
- ))
- };
-
- // In the last cell, also take into account the border that may
- // come from the row.
- if child_index + 1 == children_count {
- inline_collapsed_border.combine(&CollapsedBorder::inline_end(
- &row_style,
- CollapsedBorderProvenance::FromTableRow,
- ));
- }
-
- let mut block_start_border = CollapsedBorder::block_start(
- &*child_table_cell.block_flow.fragment.style,
- CollapsedBorderProvenance::FromNextTableCell,
- );
- block_start_border.combine(&CollapsedBorder::block_start(
- row_style,
- CollapsedBorderProvenance::FromTableRow,
- ));
- preliminary_collapsed_borders
- .block_start
- .push_or_set(child_index, block_start_border);
- let mut block_end_border = CollapsedBorder::block_end(
- &*child_table_cell.block_flow.fragment.style,
- CollapsedBorderProvenance::FromPreviousTableCell,
- );
- block_end_border.combine(&CollapsedBorder::block_end(
- row_style,
- CollapsedBorderProvenance::FromTableRow,
- ));
-
- preliminary_collapsed_borders
- .block_end
- .push_or_set(child_index, block_end_border);
-}
diff --git a/components/layout_2020/table_rowgroup.rs b/components/layout_2020/table_rowgroup.rs
deleted file mode 100644
index 814cbe0d2d2..00000000000
--- a/components/layout_2020/table_rowgroup.rs
+++ /dev/null
@@ -1,274 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS table formatting contexts.
-
-use crate::block::{BlockFlow, ISizeAndMarginsComputer};
-use crate::context::LayoutContext;
-use crate::display_list::{
- DisplayListBuildState, StackingContextCollectionFlags, StackingContextCollectionState,
-};
-use crate::flow::{Flow, FlowClass, OpaqueFlow};
-use crate::fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
-use crate::layout_debug;
-use crate::table::{ColumnIntrinsicInlineSize, InternalTable, TableLikeFlow};
-use app_units::Au;
-use euclid::Point2D;
-use gfx_traits::print_tree::PrintTree;
-use serde::{Serialize, Serializer};
-use std::fmt;
-use std::iter::{IntoIterator, Iterator, Peekable};
-use style::computed_values::{border_collapse, border_spacing};
-use style::logical_geometry::LogicalSize;
-use style::properties::ComputedValues;
-
-#[allow(unsafe_code)]
-unsafe impl crate::flow::HasBaseFlow for TableRowGroupFlow {}
-
-/// A table formatting context.
-#[repr(C)]
-pub struct TableRowGroupFlow {
- /// Fields common to all block flows.
- pub block_flow: BlockFlow,
-
- /// Information about the intrinsic inline-sizes of each column.
- pub column_intrinsic_inline_sizes: Vec<ColumnIntrinsicInlineSize>,
-
- /// The spacing for this rowgroup.
- pub spacing: border_spacing::T,
-
- /// The final width of the borders in the inline direction for each cell, computed by the
- /// entire table and pushed down into each row during inline size computation.
- pub collapsed_inline_direction_border_widths_for_table: Vec<Au>,
-
- /// The final width of the borders in the block direction for each cell, computed by the
- /// entire table and pushed down into each row during inline size computation.
- pub collapsed_block_direction_border_widths_for_table: Vec<Au>,
-}
-
-impl Serialize for TableRowGroupFlow {
- fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
- self.block_flow.serialize(serializer)
- }
-}
-
-impl TableRowGroupFlow {
- pub fn from_fragment(fragment: Fragment) -> TableRowGroupFlow {
- TableRowGroupFlow {
- block_flow: BlockFlow::from_fragment(fragment),
- column_intrinsic_inline_sizes: Vec::new(),
- spacing: border_spacing::T::zero(),
- collapsed_inline_direction_border_widths_for_table: Vec::new(),
- collapsed_block_direction_border_widths_for_table: Vec::new(),
- }
- }
-
- pub fn populate_collapsed_border_spacing<'a, I>(
- &mut self,
- collapsed_inline_direction_border_widths_for_table: &[Au],
- collapsed_block_direction_border_widths_for_table: &mut Peekable<I>,
- ) where
- I: Iterator<Item = &'a Au>,
- {
- self.collapsed_inline_direction_border_widths_for_table
- .clear();
- self.collapsed_inline_direction_border_widths_for_table
- .extend(
- collapsed_inline_direction_border_widths_for_table
- .into_iter()
- .map(|x| *x),
- );
-
- for _ in 0..self.block_flow.base.children.len() {
- if let Some(collapsed_block_direction_border_width_for_table) =
- collapsed_block_direction_border_widths_for_table.next()
- {
- self.collapsed_block_direction_border_widths_for_table
- .push(*collapsed_block_direction_border_width_for_table)
- }
- }
- if let Some(collapsed_block_direction_border_width_for_table) =
- collapsed_block_direction_border_widths_for_table.peek()
- {
- self.collapsed_block_direction_border_widths_for_table
- .push(**collapsed_block_direction_border_width_for_table)
- }
- }
-}
-
-impl Flow for TableRowGroupFlow {
- fn class(&self) -> FlowClass {
- FlowClass::TableRowGroup
- }
-
- fn as_mut_table_rowgroup(&mut self) -> &mut TableRowGroupFlow {
- self
- }
-
- fn as_table_rowgroup(&self) -> &TableRowGroupFlow {
- self
- }
-
- fn as_mut_block(&mut self) -> &mut BlockFlow {
- &mut self.block_flow
- }
-
- fn as_block(&self) -> &BlockFlow {
- &self.block_flow
- }
-
- fn bubble_inline_sizes(&mut self) {
- let _scope = layout_debug_scope!(
- "table_rowgroup::bubble_inline_sizes {:x}",
- self.block_flow.base.debug_id()
- );
- // Proper calculation of intrinsic sizes in table layout requires access to the entire
- // table, which we don't have yet. Defer to our parent.
- }
-
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
- /// When called on this context, the context has had its inline-size set by the parent context.
- fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
- let _scope = layout_debug_scope!(
- "table_rowgroup::assign_inline_sizes {:x}",
- self.block_flow.base.debug_id()
- );
- debug!(
- "assign_inline_sizes({}): assigning inline_size for flow",
- "table_rowgroup"
- );
-
- let shared_context = layout_context.shared_context();
- // The position was set to the containing block by the flow's parent.
- let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
- let (inline_start_content_edge, inline_end_content_edge) = (Au(0), Au(0));
- let content_inline_size = containing_block_inline_size;
-
- let border_collapse = self
- .block_flow
- .fragment
- .style
- .get_inherited_table()
- .border_collapse;
- let inline_size_computer = InternalTable;
- inline_size_computer.compute_used_inline_size(
- &mut self.block_flow,
- shared_context,
- containing_block_inline_size,
- );
-
- let collapsed_inline_direction_border_widths_for_table =
- &self.collapsed_inline_direction_border_widths_for_table;
- let mut collapsed_block_direction_border_widths_for_table = self
- .collapsed_block_direction_border_widths_for_table
- .iter()
- .peekable();
- self.block_flow.propagate_assigned_inline_size_to_children(
- shared_context,
- inline_start_content_edge,
- inline_end_content_edge,
- content_inline_size,
- |child_flow,
- _child_index,
- _content_inline_size,
- _writing_mode,
- _inline_start_margin_edge,
- _inline_end_margin_edge| {
- if border_collapse == border_collapse::T::Collapse {
- let child_table_row = child_flow.as_mut_table_row();
- child_table_row.populate_collapsed_border_spacing(
- collapsed_inline_direction_border_widths_for_table,
- &mut collapsed_block_direction_border_widths_for_table,
- );
- }
- },
- );
- }
-
- fn assign_block_size(&mut self, lc: &LayoutContext) {
- debug!("assign_block_size: assigning block_size for table_rowgroup");
- self.block_flow
- .assign_block_size_for_table_like_flow(self.spacing.vertical(), lc);
- }
-
- fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
- self.block_flow
- .compute_stacking_relative_position(layout_context)
- }
-
- fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
- self.block_flow
- .update_late_computed_inline_position_if_necessary(inline_position)
- }
-
- fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
- self.block_flow
- .update_late_computed_block_position_if_necessary(block_position)
- }
-
- fn build_display_list(&mut self, _: &mut DisplayListBuildState) {
- use style::servo::restyle_damage::ServoRestyleDamage;
-
- // we skip setting the damage in TableCellStyleInfo::build_display_list()
- // because we only have immutable access
- self.block_flow
- .fragment
- .restyle_damage
- .remove(ServoRestyleDamage::REPAINT);
- }
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
- self.block_flow.collect_stacking_contexts_for_block(
- state,
- StackingContextCollectionFlags::POSITION_NEVER_CREATES_CONTAINING_BLOCK,
- );
- }
-
- fn repair_style(&mut self, new_style: &crate::ServoArc<ComputedValues>) {
- self.block_flow.repair_style(new_style)
- }
-
- fn compute_overflow(&self) -> Overflow {
- self.block_flow.compute_overflow()
- }
-
- fn contains_roots_of_absolute_flow_tree(&self) -> bool {
- self.block_flow.contains_roots_of_absolute_flow_tree()
- }
-
- fn is_absolute_containing_block(&self) -> bool {
- self.block_flow.is_absolute_containing_block()
- }
-
- fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
- self.block_flow.generated_containing_block_size(flow)
- }
-
- fn iterate_through_fragment_border_boxes(
- &self,
- iterator: &mut dyn FragmentBorderBoxIterator,
- level: i32,
- stacking_context_position: &Point2D<Au>,
- ) {
- self.block_flow.iterate_through_fragment_border_boxes(
- iterator,
- level,
- stacking_context_position,
- )
- }
-
- fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
- self.block_flow.mutate_fragments(mutator)
- }
-
- fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
- self.block_flow.print_extra_flow_children(print_tree);
- }
-}
-
-impl fmt::Debug for TableRowGroupFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "TableRowGroupFlow: {:?}", self.block_flow)
- }
-}
diff --git a/components/layout_2020/table_wrapper.rs b/components/layout_2020/table_wrapper.rs
deleted file mode 100644
index 65ecc07d3ab..00000000000
--- a/components/layout_2020/table_wrapper.rs
+++ /dev/null
@@ -1,996 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS tables.
-//!
-//! This follows the "More Precise Definitions of Inline Layout and Table Layout" proposal written
-//! by L. David Baron (Mozilla) here:
-//!
-//! http://dbaron.org/css/intrinsic/
-//!
-//! Hereafter this document is referred to as INTRINSIC.
-
-use crate::block::{
- AbsoluteNonReplaced, BlockFlow, FloatNonReplaced, ISizeAndMarginsComputer, ISizeConstraintInput,
-};
-use crate::block::{ISizeConstraintSolution, MarginsMayCollapseFlag};
-use crate::context::LayoutContext;
-use crate::display_list::StackingContextCollectionState;
-use crate::display_list::{DisplayListBuildState, StackingContextCollectionFlags};
-use crate::floats::FloatKind;
-use crate::flow::{Flow, FlowClass, FlowFlags, ImmutableFlowUtils, OpaqueFlow};
-use crate::fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
-use crate::model::MaybeAuto;
-use crate::table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize};
-use app_units::Au;
-use euclid::Point2D;
-use gfx_traits::print_tree::PrintTree;
-use std::cmp::{max, min};
-use std::fmt;
-use std::ops::Add;
-use style::computed_values::{position, table_layout};
-use style::context::SharedStyleContext;
-use style::logical_geometry::{LogicalRect, LogicalSize};
-use style::properties::ComputedValues;
-use style::values::computed::Size;
-use style::values::CSSFloat;
-
-#[derive(Clone, Copy, Debug, Serialize)]
-pub enum TableLayout {
- Fixed,
- Auto,
-}
-
-#[allow(unsafe_code)]
-unsafe impl crate::flow::HasBaseFlow for TableWrapperFlow {}
-
-/// A table wrapper flow based on a block formatting context.
-#[derive(Serialize)]
-#[repr(C)]
-pub struct TableWrapperFlow {
- pub block_flow: BlockFlow,
-
- /// Intrinsic column inline sizes according to INTRINSIC § 4.1
- pub column_intrinsic_inline_sizes: Vec<ColumnIntrinsicInlineSize>,
-
- /// Table-layout property
- pub table_layout: TableLayout,
-}
-
-impl TableWrapperFlow {
- pub fn from_fragment(fragment: Fragment) -> TableWrapperFlow {
- TableWrapperFlow::from_fragment_and_float_kind(fragment, None)
- }
-
- pub fn from_fragment_and_float_kind(
- fragment: Fragment,
- float_kind: Option<FloatKind>,
- ) -> TableWrapperFlow {
- let mut block_flow = BlockFlow::from_fragment_and_float_kind(fragment, float_kind);
- let table_layout =
- if block_flow.fragment().style().get_table().table_layout == table_layout::T::Fixed {
- TableLayout::Fixed
- } else {
- TableLayout::Auto
- };
- TableWrapperFlow {
- block_flow: block_flow,
- column_intrinsic_inline_sizes: vec![],
- table_layout: table_layout,
- }
- }
-
- fn border_padding_and_spacing(&mut self) -> (Au, Au) {
- let (mut table_border_padding, mut spacing) = (Au(0), Au(0));
- for kid in self.block_flow.base.child_iter_mut() {
- if kid.is_table() {
- let kid_table = kid.as_table();
- spacing = kid_table.total_horizontal_spacing();
- table_border_padding = kid_table
- .block_flow
- .fragment
- .border_padding
- .inline_start_end();
- break;
- }
- }
- (table_border_padding, spacing)
- }
-
- // Instructs our first child, which is the table itself, to compute its border and padding.
- //
- // This is a little weird because we're computing border/padding/margins for our child,
- // when normally the child computes it itself. But it has to be this way because the
- // padding will affect where we place the child. This is an odd artifact of the way that
- // tables are separated into table flows and table wrapper flows.
- fn compute_border_and_padding_of_table(&mut self) {
- let available_inline_size = self.block_flow.base.block_container_inline_size;
- for kid in self.block_flow.base.child_iter_mut() {
- if !kid.is_table() {
- continue;
- }
-
- let kid_table = kid.as_mut_table();
- let kid_block_flow = &mut kid_table.block_flow;
- kid_block_flow
- .fragment
- .compute_border_and_padding(available_inline_size);
- kid_block_flow
- .fragment
- .compute_block_direction_margins(available_inline_size);
- kid_block_flow
- .fragment
- .compute_inline_direction_margins(available_inline_size);
- return;
- }
- }
-
- /// Calculates table column sizes for automatic layout per INTRINSIC § 4.3.
- fn calculate_table_column_sizes_for_automatic_layout(
- &mut self,
- intermediate_column_inline_sizes: &mut [IntermediateColumnInlineSize],
- ) {
- let available_inline_size = self.available_inline_size();
-
- // Compute all the guesses for the column sizes, and sum them.
- let mut total_guess = AutoLayoutCandidateGuess::new();
- let guesses: Vec<AutoLayoutCandidateGuess> = self
- .column_intrinsic_inline_sizes
- .iter()
- .map(|column_intrinsic_inline_size| {
- let guess = AutoLayoutCandidateGuess::from_column_intrinsic_inline_size(
- column_intrinsic_inline_size,
- available_inline_size,
- );
- total_guess = &total_guess + &guess;
- guess
- })
- .collect();
-
- // Assign inline sizes.
- let selection =
- SelectedAutoLayoutCandidateGuess::select(&total_guess, available_inline_size);
- let mut total_used_inline_size = Au(0);
- for (intermediate_column_inline_size, guess) in intermediate_column_inline_sizes
- .iter_mut()
- .zip(guesses.iter())
- {
- intermediate_column_inline_size.size = guess.calculate(selection);
- intermediate_column_inline_size.percentage = 0.0;
- total_used_inline_size = total_used_inline_size + intermediate_column_inline_size.size
- }
-
- // Distribute excess inline-size if necessary per INTRINSIC § 4.4.
- //
- // FIXME(pcwalton, spec): How do I deal with fractional excess?
- let excess_inline_size = available_inline_size - total_used_inline_size;
- if excess_inline_size > Au(0) &&
- selection ==
- SelectedAutoLayoutCandidateGuess::UsePreferredGuessAndDistributeExcessInlineSize
- {
- let mut info = ExcessInlineSizeDistributionInfo::new();
- for column_intrinsic_inline_size in &self.column_intrinsic_inline_sizes {
- info.update(column_intrinsic_inline_size)
- }
-
- let mut total_distributed_excess_size = Au(0);
- for (intermediate_column_inline_size, column_intrinsic_inline_size) in
- intermediate_column_inline_sizes
- .iter_mut()
- .zip(self.column_intrinsic_inline_sizes.iter())
- {
- info.distribute_excess_inline_size_to_column(
- intermediate_column_inline_size,
- column_intrinsic_inline_size,
- excess_inline_size,
- &mut total_distributed_excess_size,
- )
- }
- total_used_inline_size = available_inline_size
- }
-
- self.set_inline_size(total_used_inline_size)
- }
-
- fn available_inline_size(&mut self) -> Au {
- let available_inline_size = self.block_flow.fragment.border_box.size.inline;
- let (table_border_padding, spacing) = self.border_padding_and_spacing();
-
- // FIXME(pcwalton, spec): INTRINSIC § 8 does not properly define how to compute this, but
- // says "the basic idea is the same as the shrink-to-fit width that CSS2.1 defines". So we
- // just use the shrink-to-fit inline size.
- let available_inline_size = match self.block_flow.fragment.style().content_inline_size() {
- Size::Auto => {
- self.block_flow
- .get_shrink_to_fit_inline_size(available_inline_size) -
- table_border_padding
- },
- // FIXME(mttr): This fixes #4421 without breaking our current reftests, but I'm not
- // completely sure this is "correct".
- //
- // That said, `available_inline_size` is, as far as I can tell, equal to the table's
- // computed width property (W) and is used from this point forward in a way that seems
- // to correspond with CSS 2.1 § 17.5.2.2 under "Column and caption widths influence the
- // final table width as follows: …"
- _ => available_inline_size,
- };
- available_inline_size - spacing
- }
-
- fn set_inline_size(&mut self, total_used_inline_size: Au) {
- let (table_border_padding, spacing) = self.border_padding_and_spacing();
- self.block_flow.fragment.border_box.size.inline =
- total_used_inline_size + table_border_padding + spacing;
- self.block_flow.base.position.size.inline = total_used_inline_size +
- table_border_padding +
- spacing +
- self.block_flow.fragment.margin.inline_start_end();
-
- let writing_mode = self.block_flow.base.writing_mode;
- let container_mode = self.block_flow.base.block_container_writing_mode;
-
- if writing_mode.is_bidi_ltr() != container_mode.is_bidi_ltr() {
- // If our "start" direction is different from our parent flow, then `border_box.start.i`
- // depends on `border_box.size.inline`.
- self.block_flow.fragment.border_box.start.i =
- self.block_flow.base.block_container_inline_size -
- self.block_flow.fragment.margin.inline_end -
- self.block_flow.fragment.border_box.size.inline;
- }
- }
-
- fn compute_used_inline_size(
- &mut self,
- shared_context: &SharedStyleContext,
- parent_flow_inline_size: Au,
- intermediate_column_inline_sizes: &[IntermediateColumnInlineSize],
- ) {
- let (border_padding, spacing) = self.border_padding_and_spacing();
- let minimum_width_of_all_columns = intermediate_column_inline_sizes.iter().fold(
- border_padding + spacing,
- |accumulator, intermediate_column_inline_sizes| {
- accumulator + intermediate_column_inline_sizes.size
- },
- );
- let preferred_width_of_all_columns = self.column_intrinsic_inline_sizes.iter().fold(
- border_padding + spacing,
- |accumulator, column_intrinsic_inline_sizes| {
- accumulator + column_intrinsic_inline_sizes.preferred
- },
- );
-
- // Delegate to the appropriate inline size computer to find the constraint inputs and write
- // the constraint solutions in.
- if self.block_flow.base.flags.is_float() {
- let inline_size_computer = FloatedTable {
- minimum_width_of_all_columns: minimum_width_of_all_columns,
- preferred_width_of_all_columns: preferred_width_of_all_columns,
- table_border_padding: border_padding,
- };
- let input = inline_size_computer.compute_inline_size_constraint_inputs(
- &mut self.block_flow,
- parent_flow_inline_size,
- shared_context,
- );
-
- let solution =
- inline_size_computer.solve_inline_size_constraints(&mut self.block_flow, &input);
- inline_size_computer
- .set_inline_size_constraint_solutions(&mut self.block_flow, solution);
- inline_size_computer
- .set_inline_position_of_flow_if_necessary(&mut self.block_flow, solution);
- return;
- }
-
- if !self
- .block_flow
- .base
- .flags
- .contains(FlowFlags::INLINE_POSITION_IS_STATIC)
- {
- let inline_size_computer = AbsoluteTable {
- minimum_width_of_all_columns: minimum_width_of_all_columns,
- preferred_width_of_all_columns: preferred_width_of_all_columns,
- table_border_padding: border_padding,
- };
- let input = inline_size_computer.compute_inline_size_constraint_inputs(
- &mut self.block_flow,
- parent_flow_inline_size,
- shared_context,
- );
-
- let solution =
- inline_size_computer.solve_inline_size_constraints(&mut self.block_flow, &input);
- inline_size_computer
- .set_inline_size_constraint_solutions(&mut self.block_flow, solution);
- inline_size_computer
- .set_inline_position_of_flow_if_necessary(&mut self.block_flow, solution);
- return;
- }
-
- let inline_size_computer = Table {
- minimum_width_of_all_columns: minimum_width_of_all_columns,
- preferred_width_of_all_columns: preferred_width_of_all_columns,
- table_border_padding: border_padding,
- };
- let input = inline_size_computer.compute_inline_size_constraint_inputs(
- &mut self.block_flow,
- parent_flow_inline_size,
- shared_context,
- );
-
- let solution =
- inline_size_computer.solve_inline_size_constraints(&mut self.block_flow, &input);
- inline_size_computer.set_inline_size_constraint_solutions(&mut self.block_flow, solution);
- inline_size_computer
- .set_inline_position_of_flow_if_necessary(&mut self.block_flow, solution);
- }
-}
-
-impl Flow for TableWrapperFlow {
- fn class(&self) -> FlowClass {
- FlowClass::TableWrapper
- }
-
- fn as_table_wrapper(&self) -> &TableWrapperFlow {
- self
- }
-
- fn as_mut_block(&mut self) -> &mut BlockFlow {
- &mut self.block_flow
- }
-
- fn as_block(&self) -> &BlockFlow {
- &self.block_flow
- }
-
- fn mark_as_root(&mut self) {
- self.block_flow.mark_as_root();
- }
-
- fn bubble_inline_sizes(&mut self) {
- // Get the intrinsic column inline-sizes info from the table flow.
- for kid in self.block_flow.base.child_iter_mut() {
- debug_assert!(kid.is_table_caption() || kid.is_table());
- if kid.is_table() {
- let table = kid.as_table();
- self.column_intrinsic_inline_sizes = table.column_intrinsic_inline_sizes.clone();
- }
- }
-
- self.block_flow.bubble_inline_sizes();
- }
-
- fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
- debug!(
- "assign_inline_sizes({}): assigning inline_size for flow",
- if self.block_flow.base.flags.is_float() {
- "floated table_wrapper"
- } else {
- "table_wrapper"
- }
- );
-
- let shared_context = layout_context.shared_context();
- self.block_flow
- .initialize_container_size_for_root(shared_context);
-
- let mut intermediate_column_inline_sizes = self
- .column_intrinsic_inline_sizes
- .iter()
- .map(
- |column_intrinsic_inline_size| IntermediateColumnInlineSize {
- size: column_intrinsic_inline_size.minimum_length,
- percentage: column_intrinsic_inline_size.percentage,
- },
- )
- .collect::<Vec<_>>();
-
- // Our inline-size was set to the inline-size of the containing block by the flow's parent.
- // Now compute the real value.
- let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
- if self.block_flow.base.flags.is_float() {
- self.block_flow
- .float
- .as_mut()
- .unwrap()
- .containing_inline_size = containing_block_inline_size;
- }
-
- // This has to be done before computing our inline size because `compute_used_inline_size`
- // internally consults the border and padding of the table.
- self.compute_border_and_padding_of_table();
-
- self.compute_used_inline_size(
- shared_context,
- containing_block_inline_size,
- &intermediate_column_inline_sizes,
- );
-
- match self.table_layout {
- TableLayout::Auto => self.calculate_table_column_sizes_for_automatic_layout(
- &mut intermediate_column_inline_sizes,
- ),
- TableLayout::Fixed => {},
- }
-
- let inline_start_content_edge = self.block_flow.fragment.border_box.start.i;
- let content_inline_size = self.block_flow.fragment.border_box.size.inline;
- let inline_end_content_edge = self.block_flow.fragment.border_padding.inline_end +
- self.block_flow.fragment.margin.inline_end;
-
- // In case of fixed layout, column inline-sizes are calculated in table flow.
- let assigned_column_inline_sizes = match self.table_layout {
- TableLayout::Fixed => None,
- TableLayout::Auto => Some(
- intermediate_column_inline_sizes
- .iter()
- .map(|sizes| ColumnComputedInlineSize { size: sizes.size })
- .collect::<Vec<_>>(),
- ),
- };
-
- match assigned_column_inline_sizes {
- None => self.block_flow.propagate_assigned_inline_size_to_children(
- shared_context,
- inline_start_content_edge,
- inline_end_content_edge,
- content_inline_size,
- |_, _, _, _, _, _| {},
- ),
- Some(ref assigned_column_inline_sizes) => {
- self.block_flow.propagate_assigned_inline_size_to_children(
- shared_context,
- inline_start_content_edge,
- inline_end_content_edge,
- content_inline_size,
- |child_flow, _, _, _, _, _| {
- if child_flow.class() == FlowClass::Table {
- child_flow.as_mut_table().column_computed_inline_sizes =
- assigned_column_inline_sizes.to_vec();
- }
- },
- )
- },
- }
- }
-
- fn assign_block_size(&mut self, layout_context: &LayoutContext) {
- debug!("assign_block_size: assigning block_size for table_wrapper");
- let remaining = self.block_flow.assign_block_size_block_base(
- layout_context,
- None,
- MarginsMayCollapseFlag::MarginsMayNotCollapse,
- );
- debug_assert!(remaining.is_none());
- }
-
- fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
- self.block_flow
- .compute_stacking_relative_position(layout_context)
- }
-
- fn place_float_if_applicable<'a>(&mut self) {
- self.block_flow.place_float_if_applicable()
- }
-
- fn assign_block_size_for_inorder_child_if_necessary(
- &mut self,
- layout_context: &LayoutContext,
- parent_thread_id: u8,
- content_box: LogicalRect<Au>,
- ) -> bool {
- self.block_flow
- .assign_block_size_for_inorder_child_if_necessary(
- layout_context,
- parent_thread_id,
- content_box,
- )
- }
-
- fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
- self.block_flow
- .update_late_computed_inline_position_if_necessary(inline_position)
- }
-
- fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
- self.block_flow
- .update_late_computed_block_position_if_necessary(block_position)
- }
-
- fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
- self.block_flow.generated_containing_block_size(flow)
- }
-
- fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
- self.block_flow.build_display_list(state);
- }
-
- fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
- self.block_flow.collect_stacking_contexts_for_block(
- state,
- StackingContextCollectionFlags::POSITION_NEVER_CREATES_CONTAINING_BLOCK |
- StackingContextCollectionFlags::NEVER_CREATES_CLIP_SCROLL_NODE,
- );
- }
-
- fn repair_style(&mut self, new_style: &crate::ServoArc<ComputedValues>) {
- self.block_flow.repair_style(new_style)
- }
-
- fn compute_overflow(&self) -> Overflow {
- self.block_flow.compute_overflow()
- }
-
- fn iterate_through_fragment_border_boxes(
- &self,
- iterator: &mut dyn FragmentBorderBoxIterator,
- level: i32,
- stacking_context_position: &Point2D<Au>,
- ) {
- self.block_flow.iterate_through_fragment_border_boxes(
- iterator,
- level,
- stacking_context_position,
- )
- }
-
- fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
- self.block_flow.mutate_fragments(mutator)
- }
-
- fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
- self.block_flow.print_extra_flow_children(print_tree);
- }
-
- fn positioning(&self) -> position::T {
- self.block_flow.positioning()
- }
-}
-
-impl fmt::Debug for TableWrapperFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if self.block_flow.base.flags.is_float() {
- write!(f, "TableWrapperFlow(Float): {:?}", self.block_flow)
- } else {
- write!(f, "TableWrapperFlow: {:?}", self.block_flow)
- }
- }
-}
-
-/// The layout "guesses" defined in INTRINSIC § 4.3.
-struct AutoLayoutCandidateGuess {
- /// The column inline-size assignment where each column is assigned its intrinsic minimum
- /// inline-size.
- minimum_guess: Au,
-
- /// The column inline-size assignment where:
- /// * A column with an intrinsic percentage inline-size greater than 0% is assigned the
- /// larger of:
- /// - Its intrinsic percentage inline-size times the assignable inline-size;
- /// - Its intrinsic minimum inline-size;
- /// * Other columns receive their intrinsic minimum inline-size.
- minimum_percentage_guess: Au,
-
- /// The column inline-size assignment where:
- /// * Each column with an intrinsic percentage inline-size greater than 0% is assigned the
- /// larger of:
- /// - Its intrinsic percentage inline-size times the assignable inline-size;
- /// - Its intrinsic minimum inline-size;
- /// * Any other column that is constrained is assigned its intrinsic preferred inline-size;
- /// * Other columns are assigned their intrinsic minimum inline-size.
- minimum_specified_guess: Au,
-
- /// The column inline-size assignment where:
- /// * Each column with an intrinsic percentage inline-size greater than 0% is assigned the
- /// larger of:
- /// - Its intrinsic percentage inline-size times the assignable inline-size;
- /// - Its intrinsic minimum inline-size;
- /// * Other columns are assigned their intrinsic preferred inline-size.
- preferred_guess: Au,
-}
-
-impl AutoLayoutCandidateGuess {
- /// Creates a guess with all elements initialized to zero.
- fn new() -> AutoLayoutCandidateGuess {
- AutoLayoutCandidateGuess {
- minimum_guess: Au(0),
- minimum_percentage_guess: Au(0),
- minimum_specified_guess: Au(0),
- preferred_guess: Au(0),
- }
- }
-
- /// Fills in the inline-size guesses for this column per INTRINSIC § 4.3.
- fn from_column_intrinsic_inline_size(
- column_intrinsic_inline_size: &ColumnIntrinsicInlineSize,
- assignable_inline_size: Au,
- ) -> AutoLayoutCandidateGuess {
- let minimum_percentage_guess = max(
- assignable_inline_size.scale_by(column_intrinsic_inline_size.percentage),
- column_intrinsic_inline_size.minimum_length,
- );
- AutoLayoutCandidateGuess {
- minimum_guess: column_intrinsic_inline_size.minimum_length,
- minimum_percentage_guess: minimum_percentage_guess,
- // FIXME(pcwalton): We need the notion of *constrainedness* per INTRINSIC § 4 to
- // implement this one correctly.
- minimum_specified_guess: if column_intrinsic_inline_size.percentage > 0.0 {
- minimum_percentage_guess
- } else if column_intrinsic_inline_size.constrained {
- column_intrinsic_inline_size.preferred
- } else {
- column_intrinsic_inline_size.minimum_length
- },
- preferred_guess: if column_intrinsic_inline_size.percentage > 0.0 {
- minimum_percentage_guess
- } else {
- column_intrinsic_inline_size.preferred
- },
- }
- }
-
- /// Calculates the inline-size, interpolating appropriately based on the value of `selection`.
- ///
- /// This does *not* distribute excess inline-size. That must be done later if necessary.
- fn calculate(&self, selection: SelectedAutoLayoutCandidateGuess) -> Au {
- match selection {
- SelectedAutoLayoutCandidateGuess::UseMinimumGuess => self.minimum_guess,
- SelectedAutoLayoutCandidateGuess::
- InterpolateBetweenMinimumGuessAndMinimumPercentageGuess(weight) => {
- interp(self.minimum_guess, self.minimum_percentage_guess, weight)
- }
- SelectedAutoLayoutCandidateGuess::
- InterpolateBetweenMinimumPercentageGuessAndMinimumSpecifiedGuess(weight) => {
- interp(self.minimum_percentage_guess, self.minimum_specified_guess, weight)
- }
- SelectedAutoLayoutCandidateGuess::
- InterpolateBetweenMinimumSpecifiedGuessAndPreferredGuess(weight) => {
- interp(self.minimum_specified_guess, self.preferred_guess, weight)
- }
- SelectedAutoLayoutCandidateGuess::UsePreferredGuessAndDistributeExcessInlineSize => {
- self.preferred_guess
- }
- }
- }
-}
-
-impl<'a> Add for &'a AutoLayoutCandidateGuess {
- type Output = AutoLayoutCandidateGuess;
- #[inline]
- fn add(self, other: &AutoLayoutCandidateGuess) -> AutoLayoutCandidateGuess {
- AutoLayoutCandidateGuess {
- minimum_guess: self.minimum_guess + other.minimum_guess,
- minimum_percentage_guess: self.minimum_percentage_guess +
- other.minimum_percentage_guess,
- minimum_specified_guess: self.minimum_specified_guess + other.minimum_specified_guess,
- preferred_guess: self.preferred_guess + other.preferred_guess,
- }
- }
-}
-
-/// The `CSSFloat` member specifies the weight of the smaller of the two guesses, on a scale from
-/// 0.0 to 1.0.
-#[derive(Clone, Copy, Debug, PartialEq)]
-enum SelectedAutoLayoutCandidateGuess {
- UseMinimumGuess,
- InterpolateBetweenMinimumGuessAndMinimumPercentageGuess(CSSFloat),
- InterpolateBetweenMinimumPercentageGuessAndMinimumSpecifiedGuess(CSSFloat),
- InterpolateBetweenMinimumSpecifiedGuessAndPreferredGuess(CSSFloat),
- UsePreferredGuessAndDistributeExcessInlineSize,
-}
-
-impl SelectedAutoLayoutCandidateGuess {
- /// See INTRINSIC § 4.3.
- ///
- /// FIXME(pcwalton, INTRINSIC spec): INTRINSIC doesn't specify whether these are exclusive or
- /// inclusive ranges.
- fn select(
- guess: &AutoLayoutCandidateGuess,
- assignable_inline_size: Au,
- ) -> SelectedAutoLayoutCandidateGuess {
- if assignable_inline_size < guess.minimum_guess {
- SelectedAutoLayoutCandidateGuess::UseMinimumGuess
- } else if assignable_inline_size < guess.minimum_percentage_guess {
- let weight = weight(
- guess.minimum_guess,
- assignable_inline_size,
- guess.minimum_percentage_guess,
- );
- SelectedAutoLayoutCandidateGuess::InterpolateBetweenMinimumGuessAndMinimumPercentageGuess(weight)
- } else if assignable_inline_size < guess.minimum_specified_guess {
- let weight = weight(
- guess.minimum_percentage_guess,
- assignable_inline_size,
- guess.minimum_specified_guess,
- );
- SelectedAutoLayoutCandidateGuess::InterpolateBetweenMinimumPercentageGuessAndMinimumSpecifiedGuess(weight)
- } else if assignable_inline_size < guess.preferred_guess {
- let weight = weight(
- guess.minimum_specified_guess,
- assignable_inline_size,
- guess.preferred_guess,
- );
- SelectedAutoLayoutCandidateGuess::InterpolateBetweenMinimumSpecifiedGuessAndPreferredGuess(weight)
- } else {
- SelectedAutoLayoutCandidateGuess::UsePreferredGuessAndDistributeExcessInlineSize
- }
- }
-}
-
-/// Computes the weight needed to linearly interpolate `middle` between two guesses `low` and
-/// `high` as specified by INTRINSIC § 4.3.
-fn weight(low: Au, middle: Au, high: Au) -> CSSFloat {
- (middle - low).to_f32_px() / (high - low).to_f32_px()
-}
-
-/// Linearly interpolates between two guesses, as specified by INTRINSIC § 4.3.
-fn interp(low: Au, high: Au, weight: CSSFloat) -> Au {
- low + (high - low).scale_by(weight)
-}
-
-struct ExcessInlineSizeDistributionInfo {
- preferred_inline_size_of_nonconstrained_columns_with_no_percentage: Au,
- count_of_nonconstrained_columns_with_no_percentage: u32,
- preferred_inline_size_of_constrained_columns_with_no_percentage: Au,
- total_percentage: CSSFloat,
- column_count: u32,
-}
-
-impl ExcessInlineSizeDistributionInfo {
- fn new() -> ExcessInlineSizeDistributionInfo {
- ExcessInlineSizeDistributionInfo {
- preferred_inline_size_of_nonconstrained_columns_with_no_percentage: Au(0),
- count_of_nonconstrained_columns_with_no_percentage: 0,
- preferred_inline_size_of_constrained_columns_with_no_percentage: Au(0),
- total_percentage: 0.0,
- column_count: 0,
- }
- }
-
- fn update(&mut self, column_intrinsic_inline_size: &ColumnIntrinsicInlineSize) {
- if !column_intrinsic_inline_size.constrained &&
- column_intrinsic_inline_size.percentage == 0.0
- {
- self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage = self
- .preferred_inline_size_of_nonconstrained_columns_with_no_percentage +
- column_intrinsic_inline_size.preferred;
- self.count_of_nonconstrained_columns_with_no_percentage += 1
- }
- if column_intrinsic_inline_size.constrained &&
- column_intrinsic_inline_size.percentage == 0.0
- {
- self.preferred_inline_size_of_constrained_columns_with_no_percentage = self
- .preferred_inline_size_of_constrained_columns_with_no_percentage +
- column_intrinsic_inline_size.preferred
- }
- self.total_percentage += column_intrinsic_inline_size.percentage;
- self.column_count += 1
- }
-
- /// Based on the information here, distributes excess inline-size to the given column per
- /// INTRINSIC § 4.4.
- ///
- /// `#[inline]` so the compiler will hoist out the branch, which is loop-invariant.
- #[inline]
- fn distribute_excess_inline_size_to_column(
- &self,
- intermediate_column_inline_size: &mut IntermediateColumnInlineSize,
- column_intrinsic_inline_size: &ColumnIntrinsicInlineSize,
- excess_inline_size: Au,
- total_distributed_excess_size: &mut Au,
- ) {
- let proportion =
- if self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage > Au(0) {
- // FIXME(spec, pcwalton): Gecko and WebKit do *something* here when there are
- // nonconstrained columns with no percentage *and* no preferred width. What do they
- // do?
- if !column_intrinsic_inline_size.constrained &&
- column_intrinsic_inline_size.percentage == 0.0
- {
- column_intrinsic_inline_size.preferred.to_f32_px() /
- self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage
- .to_f32_px()
- } else {
- 0.0
- }
- } else if self.count_of_nonconstrained_columns_with_no_percentage > 0 {
- 1.0 / (self.count_of_nonconstrained_columns_with_no_percentage as CSSFloat)
- } else if self.preferred_inline_size_of_constrained_columns_with_no_percentage > Au(0) {
- column_intrinsic_inline_size.preferred.to_f32_px() /
- self.preferred_inline_size_of_constrained_columns_with_no_percentage
- .to_f32_px()
- } else if self.total_percentage > 0.0 {
- column_intrinsic_inline_size.percentage / self.total_percentage
- } else {
- 1.0 / (self.column_count as CSSFloat)
- };
-
- // The `min` here has the effect of throwing away fractional excess at the end of the
- // table.
- let amount_to_distribute = min(
- excess_inline_size.scale_by(proportion),
- excess_inline_size - *total_distributed_excess_size,
- );
- *total_distributed_excess_size = *total_distributed_excess_size + amount_to_distribute;
- intermediate_column_inline_size.size =
- intermediate_column_inline_size.size + amount_to_distribute
- }
-}
-
-/// An intermediate column size assignment.
-struct IntermediateColumnInlineSize {
- size: Au,
- percentage: f32,
-}
-
-/// Returns the computed inline size of the table wrapper represented by `block`.
-///
-/// `table_border_padding` is the sum of the sizes of all border and padding in the inline
-/// direction of the table contained within this table wrapper.
-fn initial_computed_inline_size(
- block: &mut BlockFlow,
- containing_block_inline_size: Au,
- minimum_width_of_all_columns: Au,
- preferred_width_of_all_columns: Au,
- table_border_padding: Au,
-) -> MaybeAuto {
- match block.fragment.style.content_inline_size() {
- Size::Auto => {
- if preferred_width_of_all_columns + table_border_padding <= containing_block_inline_size
- {
- MaybeAuto::Specified(preferred_width_of_all_columns + table_border_padding)
- } else if minimum_width_of_all_columns > containing_block_inline_size {
- MaybeAuto::Specified(minimum_width_of_all_columns)
- } else {
- MaybeAuto::Auto
- }
- },
- Size::LengthPercentage(ref lp) => {
- let used = lp.to_used_value(containing_block_inline_size);
- MaybeAuto::Specified(max(
- used - table_border_padding,
- minimum_width_of_all_columns,
- ))
- },
- }
-}
-
-struct Table {
- minimum_width_of_all_columns: Au,
- preferred_width_of_all_columns: Au,
- table_border_padding: Au,
-}
-
-impl ISizeAndMarginsComputer for Table {
- fn compute_border_and_padding(&self, block: &mut BlockFlow, containing_block_inline_size: Au) {
- block
- .fragment
- .compute_border_and_padding(containing_block_inline_size)
- }
-
- fn initial_computed_inline_size(
- &self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- shared_context: &SharedStyleContext,
- ) -> MaybeAuto {
- let containing_block_inline_size =
- self.containing_block_inline_size(block, parent_flow_inline_size, shared_context);
- initial_computed_inline_size(
- block,
- containing_block_inline_size,
- self.minimum_width_of_all_columns,
- self.preferred_width_of_all_columns,
- self.table_border_padding,
- )
- }
-
- fn solve_inline_size_constraints(
- &self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- self.solve_block_inline_size_constraints(block, input)
- }
-}
-
-struct FloatedTable {
- minimum_width_of_all_columns: Au,
- preferred_width_of_all_columns: Au,
- table_border_padding: Au,
-}
-
-impl ISizeAndMarginsComputer for FloatedTable {
- fn compute_border_and_padding(&self, block: &mut BlockFlow, containing_block_inline_size: Au) {
- block
- .fragment
- .compute_border_and_padding(containing_block_inline_size)
- }
-
- fn initial_computed_inline_size(
- &self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- shared_context: &SharedStyleContext,
- ) -> MaybeAuto {
- let containing_block_inline_size =
- self.containing_block_inline_size(block, parent_flow_inline_size, shared_context);
- initial_computed_inline_size(
- block,
- containing_block_inline_size,
- self.minimum_width_of_all_columns,
- self.preferred_width_of_all_columns,
- self.table_border_padding,
- )
- }
-
- fn solve_inline_size_constraints(
- &self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- FloatNonReplaced.solve_inline_size_constraints(block, input)
- }
-}
-
-struct AbsoluteTable {
- minimum_width_of_all_columns: Au,
- preferred_width_of_all_columns: Au,
- table_border_padding: Au,
-}
-
-impl ISizeAndMarginsComputer for AbsoluteTable {
- fn compute_border_and_padding(&self, block: &mut BlockFlow, containing_block_inline_size: Au) {
- block
- .fragment
- .compute_border_and_padding(containing_block_inline_size)
- }
-
- fn initial_computed_inline_size(
- &self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- shared_context: &SharedStyleContext,
- ) -> MaybeAuto {
- let containing_block_inline_size =
- self.containing_block_inline_size(block, parent_flow_inline_size, shared_context);
- initial_computed_inline_size(
- block,
- containing_block_inline_size,
- self.minimum_width_of_all_columns,
- self.preferred_width_of_all_columns,
- self.table_border_padding,
- )
- }
-
- fn containing_block_inline_size(
- &self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- shared_context: &SharedStyleContext,
- ) -> Au {
- AbsoluteNonReplaced.containing_block_inline_size(
- block,
- parent_flow_inline_size,
- shared_context,
- )
- }
-
- fn solve_inline_size_constraints(
- &self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput,
- ) -> ISizeConstraintSolution {
- AbsoluteNonReplaced.solve_inline_size_constraints(block, input)
- }
-
- fn set_inline_position_of_flow_if_necessary(
- &self,
- block: &mut BlockFlow,
- solution: ISizeConstraintSolution,
- ) {
- AbsoluteNonReplaced.set_inline_position_of_flow_if_necessary(block, solution);
- }
-}
diff --git a/components/layout_2020/tests/size_of.rs b/components/layout_2020/tests/size_of.rs
deleted file mode 100644
index e2cec625075..00000000000
--- a/components/layout_2020/tests/size_of.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![cfg(target_pointer_width = "64")]
-
-#[macro_use]
-extern crate size_of_test;
-
-use layout::Fragment;
-use layout::SpecificFragmentInfo;
-
-size_of_test!(test_size_of_fragment, Fragment, 176);
-size_of_test!(
- test_size_of_specific_fragment_info,
- SpecificFragmentInfo,
- 24
-);
diff --git a/components/layout_2020/text.rs b/components/layout_2020/text.rs
deleted file mode 100644
index 199bd7e07f2..00000000000
--- a/components/layout_2020/text.rs
+++ /dev/null
@@ -1,798 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Text layout.
-
-use crate::context::LayoutFontContext;
-use crate::fragment::{Fragment, ScannedTextFlags};
-use crate::fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
-use crate::inline::{InlineFragmentNodeFlags, InlineFragments};
-use crate::linked_list::split_off_head;
-use app_units::Au;
-use gfx::font::{FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions};
-use gfx::text::glyph::ByteIndex;
-use gfx::text::text_run::TextRun;
-use gfx::text::util::{self, CompressionMode};
-use range::Range;
-use servo_atoms::Atom;
-use std::borrow::ToOwned;
-use std::collections::LinkedList;
-use std::mem;
-use std::sync::Arc;
-use style::computed_values::text_rendering::T as TextRendering;
-use style::computed_values::white_space::T as WhiteSpace;
-use style::computed_values::word_break::T as WordBreak;
-use style::logical_geometry::{LogicalSize, WritingMode};
-use style::properties::style_structs::Font as FontStyleStruct;
-use style::properties::ComputedValues;
-use style::values::generics::text::LineHeight;
-use style::values::specified::text::{TextTransform, TextTransformCase};
-use unicode_bidi as bidi;
-use unicode_script::{get_script, Script};
-use xi_unicode::LineBreakLeafIter;
-
-/// Returns the concatenated text of a list of unscanned text fragments.
-fn text(fragments: &LinkedList<Fragment>) -> String {
- // FIXME: Some of this work is later duplicated in split_first_fragment_at_newline_if_necessary
- // and transform_text. This code should be refactored so that the all the scanning for
- // newlines is done in a single pass.
-
- let mut text = String::new();
-
- for fragment in fragments {
- if let SpecificFragmentInfo::UnscannedText(ref info) = fragment.specific {
- if fragment.white_space().preserve_newlines() {
- text.push_str(&info.text);
- } else {
- text.push_str(&info.text.replace("\n", " "));
- }
- }
- }
- text
-}
-
-/// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextFragment`s.
-pub struct TextRunScanner {
- pub clump: LinkedList<Fragment>,
-}
-
-impl TextRunScanner {
- pub fn new() -> TextRunScanner {
- TextRunScanner {
- clump: LinkedList::new(),
- }
- }
-
- pub fn scan_for_runs(
- &mut self,
- font_context: &mut LayoutFontContext,
- mut fragments: LinkedList<Fragment>,
- ) -> InlineFragments {
- debug!(
- "TextRunScanner: scanning {} fragments for text runs...",
- fragments.len()
- );
- debug_assert!(!fragments.is_empty());
-
- // Calculate bidi embedding levels, so we can split bidirectional fragments for reordering.
- let text = text(&fragments);
- let para_level = fragments
- .front()
- .unwrap()
- .style
- .writing_mode
- .to_bidi_level();
- let bidi_info = bidi::BidiInfo::new(&text, Some(para_level));
-
- // Optimization: If all the text is LTR, don't bother splitting on bidi levels.
- let bidi_levels = if bidi_info.has_rtl() {
- Some(&bidi_info.levels[..])
- } else {
- None
- };
-
- // FIXME(pcwalton): We want to be sure not to allocate multiple times, since this is a
- // performance-critical spot, but this may overestimate and allocate too much memory.
- let mut new_fragments = Vec::with_capacity(fragments.len());
- let mut last_whitespace = false;
- let mut paragraph_bytes_processed = 0;
-
- // The first time we process a text run we will set this
- // linebreaker. There is no way for the linebreaker to start
- // with an empty state; you must give it its first input immediately.
- //
- // This linebreaker is shared across text runs, so we can know if
- // there is a break at the beginning of a text run or clump, e.g.
- // in the case of FooBar<span>Baz</span>
- let mut linebreaker = None;
-
- while !fragments.is_empty() {
- // Create a clump.
- split_first_fragment_at_newline_if_necessary(&mut fragments);
- self.clump.append(&mut split_off_head(&mut fragments));
- while !fragments.is_empty() &&
- self.clump
- .back()
- .unwrap()
- .can_merge_with_fragment(fragments.front().unwrap())
- {
- split_first_fragment_at_newline_if_necessary(&mut fragments);
- self.clump.append(&mut split_off_head(&mut fragments));
- }
-
- // Flush that clump to the list of fragments we're building up.
- last_whitespace = self.flush_clump_to_list(
- font_context,
- &mut new_fragments,
- &mut paragraph_bytes_processed,
- bidi_levels,
- last_whitespace,
- &mut linebreaker,
- );
- }
-
- debug!("TextRunScanner: complete.");
- InlineFragments {
- fragments: new_fragments,
- }
- }
-
- /// A "clump" is a range of inline flow leaves that can be merged together into a single
- /// fragment. Adjacent text with the same style can be merged, and nothing else can.
- ///
- /// The flow keeps track of the fragments contained by all non-leaf DOM nodes. This is necessary
- /// for correct painting order. Since we compress several leaf fragments here, the mapping must
- /// be adjusted.
- fn flush_clump_to_list(
- &mut self,
- mut font_context: &mut LayoutFontContext,
- out_fragments: &mut Vec<Fragment>,
- paragraph_bytes_processed: &mut usize,
- bidi_levels: Option<&[bidi::Level]>,
- mut last_whitespace: bool,
- linebreaker: &mut Option<LineBreakLeafIter>,
- ) -> bool {
- debug!(
- "TextRunScanner: flushing {} fragments in range",
- self.clump.len()
- );
-
- debug_assert!(!self.clump.is_empty());
- match self.clump.front().unwrap().specific {
- SpecificFragmentInfo::UnscannedText(_) => {},
- _ => {
- debug_assert!(
- self.clump.len() == 1,
- "WAT: can't coalesce non-text nodes in flush_clump_to_list()!"
- );
- out_fragments.push(self.clump.pop_front().unwrap());
- return false;
- },
- }
-
- // Concatenate all of the transformed strings together, saving the new character indices.
- let mut mappings: Vec<RunMapping> = Vec::new();
- let runs = {
- let font_group;
- let compression;
- let text_transform;
- let letter_spacing;
- let word_spacing;
- let text_rendering;
- let word_break;
- {
- let in_fragment = self.clump.front().unwrap();
- let font_style = in_fragment.style().clone_font();
- let inherited_text_style = in_fragment.style().get_inherited_text();
- font_group = font_context.font_group(font_style);
- compression = match in_fragment.white_space() {
- WhiteSpace::Normal | WhiteSpace::Nowrap => {
- CompressionMode::CompressWhitespaceNewline
- },
- WhiteSpace::Pre | WhiteSpace::PreWrap => CompressionMode::CompressNone,
- WhiteSpace::PreLine => CompressionMode::CompressWhitespace,
- };
- text_transform = inherited_text_style.text_transform;
- letter_spacing = inherited_text_style.letter_spacing;
- word_spacing = inherited_text_style.word_spacing.to_hash_key();
- text_rendering = inherited_text_style.text_rendering;
- word_break = inherited_text_style.word_break;
- }
-
- // First, transform/compress text of all the nodes.
- let (mut run_info_list, mut run_info) = (Vec::new(), RunInfo::new());
- let mut insertion_point = None;
-
- for (fragment_index, in_fragment) in self.clump.iter().enumerate() {
- debug!(" flushing {:?}", in_fragment);
- let mut mapping = RunMapping::new(&run_info_list[..], fragment_index);
- let text;
- let selection;
- match in_fragment.specific {
- SpecificFragmentInfo::UnscannedText(ref text_fragment_info) => {
- text = &text_fragment_info.text;
- selection = text_fragment_info.selection;
- },
- _ => panic!("Expected an unscanned text fragment!"),
- };
- insertion_point = match selection {
- Some(range) if range.is_empty() => {
- // `range` is the range within the current fragment. To get the range
- // within the text run, offset it by the length of the preceding fragments.
- Some(range.begin() + ByteIndex(run_info.text.len() as isize))
- },
- _ => None,
- };
-
- let (mut start_position, mut end_position) = (0, 0);
- for (byte_index, character) in text.char_indices() {
- if !character.is_control() {
- let font = font_group
- .borrow_mut()
- .find_by_codepoint(&mut font_context, character);
-
- let bidi_level = match bidi_levels {
- Some(levels) => levels[*paragraph_bytes_processed],
- None => bidi::Level::ltr(),
- };
-
- // Break the run if the new character has a different explicit script than the
- // previous characters.
- //
- // TODO: Special handling of paired punctuation characters.
- // http://www.unicode.org/reports/tr24/#Common
- let script = get_script(character);
- let compatible_script = is_compatible(script, run_info.script);
- if compatible_script && !is_specific(run_info.script) && is_specific(script)
- {
- run_info.script = script;
- }
-
- let selected = match selection {
- Some(range) => range.contains(ByteIndex(byte_index as isize)),
- None => false,
- };
-
- // Now, if necessary, flush the mapping we were building up.
- let flush_run = !run_info.has_font(&font) ||
- run_info.bidi_level != bidi_level ||
- !compatible_script;
- let new_mapping_needed = flush_run || mapping.selected != selected;
-
- if new_mapping_needed {
- // We ignore empty mappings at the very start of a fragment.
- // The run info values are uninitialized at this point so
- // flushing an empty mapping is pointless.
- if end_position > 0 {
- mapping.flush(
- &mut mappings,
- &mut run_info,
- &**text,
- compression,
- text_transform,
- &mut last_whitespace,
- &mut start_position,
- end_position,
- );
- }
- if run_info.text.len() > 0 {
- if flush_run {
- run_info.flush(&mut run_info_list, &mut insertion_point);
- run_info = RunInfo::new();
- }
- mapping = RunMapping::new(&run_info_list[..], fragment_index);
- }
- run_info.font = font;
- run_info.bidi_level = bidi_level;
- run_info.script = script;
- mapping.selected = selected;
- }
- }
-
- // Consume this character.
- end_position += character.len_utf8();
- *paragraph_bytes_processed += character.len_utf8();
- }
-
- // Flush the last mapping we created for this fragment to the list.
- mapping.flush(
- &mut mappings,
- &mut run_info,
- &**text,
- compression,
- text_transform,
- &mut last_whitespace,
- &mut start_position,
- end_position,
- );
- }
-
- // Push the final run info.
- run_info.flush(&mut run_info_list, &mut insertion_point);
-
- // Per CSS 2.1 § 16.4, "when the resultant space between two characters is not the same
- // as the default space, user agents should not use ligatures." This ensures that, for
- // example, `finally` with a wide `letter-spacing` renders as `f i n a l l y` and not
- // `fi n a l l y`.
- let mut flags = ShapingFlags::empty();
- if letter_spacing.0.px() != 0. {
- flags.insert(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG);
- }
- if text_rendering == TextRendering::Optimizespeed {
- flags.insert(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG);
- flags.insert(ShapingFlags::DISABLE_KERNING_SHAPING_FLAG)
- }
- if word_break == WordBreak::KeepAll {
- flags.insert(ShapingFlags::KEEP_ALL_FLAG);
- }
- let options = ShapingOptions {
- letter_spacing: if letter_spacing.0.px() == 0. {
- None
- } else {
- Some(Au::from(letter_spacing.0))
- },
- word_spacing,
- script: Script::Common,
- flags: flags,
- };
-
- let mut result = Vec::with_capacity(run_info_list.len());
- for run_info in run_info_list {
- let mut options = options;
- options.script = run_info.script;
- if run_info.bidi_level.is_rtl() {
- options.flags.insert(ShapingFlags::RTL_FLAG);
- }
-
- // If no font is found (including fallbacks), there's no way we can render.
- let font = run_info
- .font
- .or_else(|| font_group.borrow_mut().first(&mut font_context))
- .expect("No font found for text run!");
-
- let (run, break_at_zero) = TextRun::new(
- &mut *font.borrow_mut(),
- run_info.text,
- &options,
- run_info.bidi_level,
- linebreaker,
- );
- result.push((
- ScannedTextRun {
- run: Arc::new(run),
- insertion_point: run_info.insertion_point,
- },
- break_at_zero,
- ))
- }
- result
- };
-
- // Make new fragments with the runs and adjusted text indices.
- debug!("TextRunScanner: pushing {} fragment(s)", self.clump.len());
- let mut mappings = mappings.into_iter().peekable();
- let mut prev_fragments_to_meld = Vec::new();
-
- for (logical_offset, old_fragment) in mem::replace(&mut self.clump, LinkedList::new())
- .into_iter()
- .enumerate()
- {
- let mut is_first_mapping_of_this_old_fragment = true;
- loop {
- match mappings.peek() {
- Some(mapping) if mapping.old_fragment_index == logical_offset => {},
- Some(_) | None => {
- if is_first_mapping_of_this_old_fragment {
- // There were no mappings for this unscanned fragment. Transfer its
- // flags to the previous/next sibling elements instead.
- if let Some(ref mut last_fragment) = out_fragments.last_mut() {
- last_fragment.meld_with_next_inline_fragment(&old_fragment);
- }
- prev_fragments_to_meld.push(old_fragment);
- }
- break;
- },
- };
- let mapping = mappings.next().unwrap();
- let (scanned_run, break_at_zero) = runs[mapping.text_run_index].clone();
-
- let mut byte_range = Range::new(
- ByteIndex(mapping.byte_range.begin() as isize),
- ByteIndex(mapping.byte_range.length() as isize),
- );
-
- let mut flags = ScannedTextFlags::empty();
- if !break_at_zero && mapping.byte_range.begin() == 0 {
- // If this is the first segment of the text run,
- // and the text run doesn't break at zero, suppress line breaks
- flags.insert(ScannedTextFlags::SUPPRESS_LINE_BREAK_BEFORE)
- }
- let text_size = old_fragment.border_box.size;
-
- let requires_line_break_afterward_if_wrapping_on_newlines = scanned_run.run.text
- [mapping.byte_range.begin()..mapping.byte_range.end()]
- .ends_with('\n');
-
- if requires_line_break_afterward_if_wrapping_on_newlines {
- byte_range.extend_by(ByteIndex(-1)); // Trim the '\n'
- flags.insert(
- ScannedTextFlags::REQUIRES_LINE_BREAK_AFTERWARD_IF_WRAPPING_ON_NEWLINES,
- );
- }
-
- if mapping.selected {
- flags.insert(ScannedTextFlags::SELECTED);
- }
-
- let insertion_point =
- if mapping.contains_insertion_point(scanned_run.insertion_point) {
- scanned_run.insertion_point
- } else {
- None
- };
-
- let mut new_text_fragment_info = Box::new(ScannedTextFragmentInfo::new(
- scanned_run.run,
- byte_range,
- text_size,
- insertion_point,
- flags,
- ));
-
- let new_metrics = new_text_fragment_info.run.metrics_for_range(&byte_range);
- let writing_mode = old_fragment.style.writing_mode;
- let bounding_box_size = bounding_box_for_run_metrics(&new_metrics, writing_mode);
- new_text_fragment_info.content_size = bounding_box_size;
-
- let mut new_fragment = old_fragment.transform(
- bounding_box_size,
- SpecificFragmentInfo::ScannedText(new_text_fragment_info),
- );
-
- let is_last_mapping_of_this_old_fragment = match mappings.peek() {
- Some(mapping) if mapping.old_fragment_index == logical_offset => false,
- _ => true,
- };
-
- if let Some(ref mut context) = new_fragment.inline_context {
- for node in &mut context.nodes {
- if !is_last_mapping_of_this_old_fragment {
- node.flags
- .remove(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT);
- }
- if !is_first_mapping_of_this_old_fragment {
- node.flags
- .remove(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT);
- }
- }
- }
-
- for prev_fragment in prev_fragments_to_meld.drain(..) {
- new_fragment.meld_with_prev_inline_fragment(&prev_fragment);
- }
-
- is_first_mapping_of_this_old_fragment = false;
- out_fragments.push(new_fragment)
- }
- }
-
- last_whitespace
- }
-}
-
-#[inline]
-fn bounding_box_for_run_metrics(
- metrics: &RunMetrics,
- writing_mode: WritingMode,
-) -> LogicalSize<Au> {
- // TODO: When the text-orientation property is supported, the block and inline directions may
- // be swapped for horizontal glyphs in vertical lines.
- LogicalSize::new(
- writing_mode,
- metrics.bounding_box.size.width,
- metrics.bounding_box.size.height,
- )
-}
-
-/// Returns the metrics of the font represented by the given `FontStyleStruct`.
-///
-/// `#[inline]` because often the caller only needs a few fields from the font metrics.
-///
-/// # Panics
-///
-/// Panics if no font can be found for the given font style.
-#[inline]
-pub fn font_metrics_for_style(
- mut font_context: &mut LayoutFontContext,
- style: crate::ServoArc<FontStyleStruct>,
-) -> FontMetrics {
- let font_group = font_context.font_group(style);
- let font = font_group.borrow_mut().first(&mut font_context);
- let font = font.as_ref().unwrap().borrow();
-
- font.metrics.clone()
-}
-
-/// Returns the line block-size needed by the given computed style and font size.
-pub fn line_height_from_style(style: &ComputedValues, metrics: &FontMetrics) -> Au {
- let font_size = style.get_font().font_size.size();
- match style.get_inherited_text().line_height {
- LineHeight::Normal => Au::from(metrics.line_gap),
- LineHeight::Number(l) => font_size.scale_by(l.0),
- LineHeight::Length(l) => Au::from(l),
- }
-}
-
-fn split_first_fragment_at_newline_if_necessary(fragments: &mut LinkedList<Fragment>) {
- if fragments.is_empty() {
- return;
- }
-
- let new_fragment = {
- let first_fragment = fragments.front_mut().unwrap();
- let string_before;
- let selection_before;
- {
- if !first_fragment.white_space().preserve_newlines() {
- return;
- }
-
- let unscanned_text_fragment_info = match first_fragment.specific {
- SpecificFragmentInfo::UnscannedText(ref mut unscanned_text_fragment_info) => {
- unscanned_text_fragment_info
- },
- _ => return,
- };
-
- let position = match unscanned_text_fragment_info.text.find('\n') {
- Some(position) if position < unscanned_text_fragment_info.text.len() - 1 => {
- position
- },
- Some(_) | None => return,
- };
-
- string_before = unscanned_text_fragment_info.text[..(position + 1)].to_owned();
- unscanned_text_fragment_info.text = unscanned_text_fragment_info.text[(position + 1)..]
- .to_owned()
- .into_boxed_str();
- let offset = ByteIndex(string_before.len() as isize);
- match unscanned_text_fragment_info.selection {
- Some(ref mut selection) if selection.begin() >= offset => {
- // Selection is entirely in the second fragment.
- selection_before = None;
- selection.shift_by(-offset);
- },
- Some(ref mut selection) if selection.end() > offset => {
- // Selection is split across two fragments.
- selection_before = Some(Range::new(selection.begin(), offset));
- *selection = Range::new(ByteIndex(0), selection.end() - offset);
- },
- _ => {
- // Selection is entirely in the first fragment.
- selection_before = unscanned_text_fragment_info.selection;
- unscanned_text_fragment_info.selection = None;
- },
- };
- }
- first_fragment.transform(
- first_fragment.border_box.size,
- SpecificFragmentInfo::UnscannedText(Box::new(UnscannedTextFragmentInfo::new(
- string_before.into_boxed_str(),
- selection_before,
- ))),
- )
- };
-
- fragments.push_front(new_fragment);
-}
-
-/// Information about a text run that we're about to create. This is used in `scan_for_runs`.
-struct RunInfo {
- /// The text that will go in this text run.
- text: String,
- /// The insertion point in this text run, if applicable.
- insertion_point: Option<ByteIndex>,
- /// The font that the text should be rendered with.
- font: Option<FontRef>,
- /// The bidirection embedding level of this text run.
- bidi_level: bidi::Level,
- /// The Unicode script property of this text run.
- script: Script,
-}
-
-impl RunInfo {
- fn new() -> RunInfo {
- RunInfo {
- text: String::new(),
- insertion_point: None,
- font: None,
- bidi_level: bidi::Level::ltr(),
- script: Script::Common,
- }
- }
-
- /// Finish processing this RunInfo and add it to the "done" list.
- ///
- /// * `insertion_point`: The position of the insertion point, in characters relative to the start
- /// of this text run.
- fn flush(mut self, list: &mut Vec<RunInfo>, insertion_point: &mut Option<ByteIndex>) {
- if let Some(idx) = *insertion_point {
- let char_len = ByteIndex(self.text.len() as isize);
- if idx <= char_len {
- // The insertion point is in this text run.
- self.insertion_point = insertion_point.take()
- } else {
- // Continue looking for the insertion point in the next text run.
- *insertion_point = Some(idx - char_len)
- }
- }
- list.push(self);
- }
-
- fn has_font(&self, font: &Option<FontRef>) -> bool {
- fn identifier(font: &Option<FontRef>) -> Option<Atom> {
- font.as_ref().map(|f| f.borrow().identifier())
- }
-
- identifier(&self.font) == identifier(font)
- }
-}
-
-/// A mapping from a portion of an unscanned text fragment to the text run we're going to create
-/// for it.
-#[derive(Clone, Copy, Debug)]
-struct RunMapping {
- /// The range of byte indices within the text fragment.
- byte_range: Range<usize>,
- /// The index of the unscanned text fragment that this mapping corresponds to.
- old_fragment_index: usize,
- /// The index of the text run we're going to create.
- text_run_index: usize,
- /// Is the text in this fragment selected?
- selected: bool,
-}
-
-impl RunMapping {
- /// Given the current set of text runs, creates a run mapping for the next fragment.
- /// `run_info_list` describes the set of runs we've seen already.
- fn new(run_info_list: &[RunInfo], fragment_index: usize) -> RunMapping {
- RunMapping {
- byte_range: Range::new(0, 0),
- old_fragment_index: fragment_index,
- text_run_index: run_info_list.len(),
- selected: false,
- }
- }
-
- /// Flushes this run mapping to the list. `run_info` describes the text run that we're
- /// currently working on. `text` refers to the text of this fragment.
- fn flush(
- mut self,
- mappings: &mut Vec<RunMapping>,
- run_info: &mut RunInfo,
- text: &str,
- compression: CompressionMode,
- text_transform: TextTransform,
- last_whitespace: &mut bool,
- start_position: &mut usize,
- end_position: usize,
- ) {
- let was_empty = *start_position == end_position;
- let old_byte_length = run_info.text.len();
- *last_whitespace = util::transform_text(
- &text[(*start_position)..end_position],
- compression,
- *last_whitespace,
- &mut run_info.text,
- );
-
- // Account for `text-transform`. (Confusingly, this is not handled in "text
- // transformation" above, but we follow Gecko in the naming.)
- let is_first_run = *start_position == 0;
- apply_style_transform_if_necessary(
- &mut run_info.text,
- old_byte_length,
- text_transform,
- *last_whitespace,
- is_first_run,
- );
- *start_position = end_position;
-
- let new_byte_length = run_info.text.len();
- let is_empty = new_byte_length == old_byte_length;
-
- // Don't save mappings that contain only discarded characters.
- // (But keep ones that contained no characters to begin with, since they might have been
- // generated by an empty flow to draw its borders/padding/insertion point.)
- if is_empty && !was_empty {
- return;
- }
-
- self.byte_range = Range::new(old_byte_length, new_byte_length - old_byte_length);
- mappings.push(self)
- }
-
- /// Is the insertion point for this text run within this mapping?
- ///
- /// NOTE: We treat the range as inclusive at both ends, since the insertion point can lie
- /// before the first character *or* after the last character, and should be drawn even if the
- /// text is empty.
- fn contains_insertion_point(&self, insertion_point: Option<ByteIndex>) -> bool {
- match insertion_point.map(ByteIndex::to_usize) {
- None => false,
- Some(idx) => self.byte_range.begin() <= idx && idx <= self.byte_range.end(),
- }
- }
-}
-
-/// Accounts for `text-transform`.
-///
-/// FIXME(#4311, pcwalton): Title-case mapping can change length of the string;
-/// case mapping should be language-specific; `full-width`;
-/// use graphemes instead of characters.
-fn apply_style_transform_if_necessary(
- string: &mut String,
- first_character_position: usize,
- text_transform: TextTransform,
- last_whitespace: bool,
- is_first_run: bool,
-) {
- match text_transform.case_ {
- TextTransformCase::None => {},
- TextTransformCase::Uppercase => {
- let original = string[first_character_position..].to_owned();
- string.truncate(first_character_position);
- for ch in original.chars().flat_map(|ch| ch.to_uppercase()) {
- string.push(ch);
- }
- },
- TextTransformCase::Lowercase => {
- let original = string[first_character_position..].to_owned();
- string.truncate(first_character_position);
- for ch in original.chars().flat_map(|ch| ch.to_lowercase()) {
- string.push(ch);
- }
- },
- TextTransformCase::Capitalize => {
- let original = string[first_character_position..].to_owned();
- string.truncate(first_character_position);
-
- let mut capitalize_next_letter = is_first_run || last_whitespace;
- for character in original.chars() {
- // FIXME(#4311, pcwalton): Should be the CSS/Unicode notion of a *typographic
- // letter unit*, not an *alphabetic* character:
- //
- // http://dev.w3.org/csswg/css-text/#typographic-letter-unit
- if capitalize_next_letter && character.is_alphabetic() {
- string.push(character.to_uppercase().next().unwrap());
- capitalize_next_letter = false;
- continue;
- }
-
- string.push(character);
-
- // FIXME(#4311, pcwalton): Try UAX29 instead of just whitespace.
- if character.is_whitespace() {
- capitalize_next_letter = true
- }
- }
- },
- }
-}
-
-#[derive(Clone)]
-struct ScannedTextRun {
- run: Arc<TextRun>,
- insertion_point: Option<ByteIndex>,
-}
-
-/// Can a character with script `b` continue a text run with script `a`?
-fn is_compatible(a: Script, b: Script) -> bool {
- a == b || !is_specific(a) || !is_specific(b)
-}
-
-/// Returns true if the script is not invalid or inherited.
-fn is_specific(script: Script) -> bool {
- script != Script::Common && script != Script::Inherited
-}
diff --git a/components/layout_2020/traversal.rs b/components/layout_2020/traversal.rs
index e968c184341..5aefdc6e939 100644
--- a/components/layout_2020/traversal.rs
+++ b/components/layout_2020/traversal.rs
@@ -2,21 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-//! Traversals over the DOM and flow trees, running the layout computations.
-
-use crate::construct::FlowConstructor;
use crate::context::LayoutContext;
-use crate::display_list::DisplayListBuildState;
-use crate::flow::{Flow, FlowFlags, GetBaseFlow, ImmutableFlowUtils};
-use crate::wrapper::ThreadSafeLayoutNodeHelpers;
-use crate::wrapper::{GetRawData, LayoutNodeLayoutData};
-use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
-use servo_config::opts;
+use crate::wrapper::GetRawData;
+use script_layout_interface::wrapper_traits::LayoutNode;
use style::context::{SharedStyleContext, StyleContext};
use style::data::ElementData;
use style::dom::{NodeInfo, TElement, TNode};
-use style::selector_parser::RestyleDamage;
-use style::servo::restyle_damage::ServoRestyleDamage;
use style::traversal::PerLevelTraversalData;
use style::traversal::{recalc_style_at, DomTraversal};
@@ -25,13 +16,10 @@ pub struct RecalcStyleAndConstructFlows<'a> {
}
impl<'a> RecalcStyleAndConstructFlows<'a> {
- /// Creates a traversal context, taking ownership of the shared layout context.
pub fn new(context: LayoutContext<'a>) -> Self {
RecalcStyleAndConstructFlows { context: context }
}
- /// Consumes this traversal context, returning ownership of the shared layout
- /// context to the caller.
pub fn destroy(self) -> LayoutContext<'a> {
self.context
}
@@ -53,8 +41,6 @@ where
) where
F: FnMut(E::ConcreteNode),
{
- // FIXME(pcwalton): Stop allocating here. Ideally this should just be
- // done by the HTML parser.
unsafe { node.initialize_data() };
if !node.is_text_node() {
@@ -65,14 +51,14 @@ where
}
fn process_postorder(&self, _style_context: &mut StyleContext<E>, node: E::ConcreteNode) {
- construct_flows_at(&self.context, node);
+ if let Some(el) = node.as_element() {
+ unsafe {
+ el.unset_dirty_descendants();
+ }
+ }
}
fn text_node_needs_traversal(node: E::ConcreteNode, parent_data: &ElementData) -> 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() || !parent_data.damage.is_empty()
}
@@ -80,271 +66,3 @@ where
&self.context.style_context
}
}
-
-/// A top-down traversal.
-pub trait PreorderFlowTraversal {
- /// The operation to perform. Return true to continue or false to stop.
- fn process(&self, flow: &mut dyn Flow);
-
- /// Returns true if this node should be processed and false if neither this node nor its
- /// descendants should be processed.
- fn should_process_subtree(&self, _flow: &mut dyn Flow) -> bool {
- true
- }
-
- /// Returns true if this node must be processed in-order. If this returns false,
- /// we skip the operation for this node, but continue processing the descendants.
- /// This is called *after* parent nodes are visited.
- fn should_process(&self, _flow: &mut dyn Flow) -> bool {
- true
- }
-
- /// Traverses the tree in preorder.
- fn traverse(&self, flow: &mut dyn Flow) {
- if !self.should_process_subtree(flow) {
- return;
- }
- if self.should_process(flow) {
- self.process(flow);
- }
- for kid in flow.mut_base().child_iter_mut() {
- self.traverse(kid);
- }
- }
-
- /// Traverse the Absolute flow tree in preorder.
- ///
- /// Traverse all your direct absolute descendants, who will then traverse
- /// their direct absolute descendants.
- ///
- /// Return true if the traversal is to continue or false to stop.
- fn traverse_absolute_flows(&self, flow: &mut dyn Flow) {
- if self.should_process(flow) {
- self.process(flow);
- }
- for descendant_link in flow.mut_base().abs_descendants.iter() {
- self.traverse_absolute_flows(descendant_link)
- }
- }
-}
-
-/// A bottom-up traversal, with a optional in-order pass.
-pub trait PostorderFlowTraversal {
- /// The operation to perform. Return true to continue or false to stop.
- fn process(&self, flow: &mut dyn Flow);
-
- /// Returns false if this node must be processed in-order. If this returns false, we skip the
- /// operation for this node, but continue processing the ancestors. This is called *after*
- /// child nodes are visited.
- fn should_process(&self, _flow: &mut dyn Flow) -> bool {
- true
- }
-
- /// Traverses the tree in postorder.
- fn traverse(&self, flow: &mut dyn Flow) {
- for kid in flow.mut_base().child_iter_mut() {
- self.traverse(kid);
- }
- if self.should_process(flow) {
- self.process(flow);
- }
- }
-}
-
-/// An in-order (sequential only) traversal.
-pub trait InorderFlowTraversal {
- /// The operation to perform. Returns the level of the tree we're at.
- fn process(&mut self, flow: &mut dyn Flow, level: u32);
-
- /// Returns true if this node should be processed and false if neither this node nor its
- /// descendants should be processed.
- fn should_process_subtree(&mut self, _flow: &mut dyn Flow) -> bool {
- true
- }
-
- /// Traverses the tree in-order.
- fn traverse(&mut self, flow: &mut dyn Flow, level: u32) {
- if !self.should_process_subtree(flow) {
- return;
- }
- self.process(flow, level);
- for kid in flow.mut_base().child_iter_mut() {
- self.traverse(kid, level + 1);
- }
- }
-}
-
-/// A bottom-up, parallelizable traversal.
-pub trait PostorderNodeMutTraversal<ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> {
- /// The operation to perform. Return true to continue or false to stop.
- fn process(&mut self, node: &ConcreteThreadSafeLayoutNode);
-}
-
-/// The flow construction traversal, which builds flows for styled nodes.
-#[inline]
-#[allow(unsafe_code)]
-fn construct_flows_at<N>(context: &LayoutContext, node: N)
-where
- N: LayoutNode,
-{
- debug!("construct_flows_at: {:?}", node);
-
- // Construct flows for this node.
- {
- let tnode = node.to_threadsafe();
-
- // Always reconstruct if incremental layout is turned off.
- let nonincremental_layout = opts::get().nonincremental_layout;
- if nonincremental_layout ||
- tnode.restyle_damage() != RestyleDamage::empty() ||
- node.as_element()
- .map_or(false, |el| el.has_dirty_descendants())
- {
- let mut flow_constructor = FlowConstructor::new(context);
- if nonincremental_layout || !flow_constructor.repair_if_possible(&tnode) {
- flow_constructor.process(&tnode);
- debug!(
- "Constructed flow for {:?}: {:x}",
- tnode,
- tnode.flow_debug_id()
- );
- }
- }
-
- tnode
- .mutate_layout_data()
- .unwrap()
- .flags
- .insert(crate::data::LayoutDataFlags::HAS_BEEN_TRAVERSED);
- }
-
- if let Some(el) = node.as_element() {
- unsafe {
- el.unset_dirty_descendants();
- }
- }
-}
-
-/// The bubble-inline-sizes traversal, the first part of layout computation. This computes
-/// preferred and intrinsic inline-sizes and bubbles them up the tree.
-pub struct BubbleISizes<'a> {
- pub layout_context: &'a LayoutContext<'a>,
-}
-
-impl<'a> PostorderFlowTraversal for BubbleISizes<'a> {
- #[inline]
- fn process(&self, flow: &mut dyn Flow) {
- flow.bubble_inline_sizes();
- flow.mut_base()
- .restyle_damage
- .remove(ServoRestyleDamage::BUBBLE_ISIZES);
- }
-
- #[inline]
- fn should_process(&self, flow: &mut dyn Flow) -> bool {
- flow.base()
- .restyle_damage
- .contains(ServoRestyleDamage::BUBBLE_ISIZES)
- }
-}
-
-/// The assign-inline-sizes traversal. In Gecko this corresponds to `Reflow`.
-#[derive(Clone, Copy)]
-pub struct AssignISizes<'a> {
- pub layout_context: &'a LayoutContext<'a>,
-}
-
-impl<'a> PreorderFlowTraversal for AssignISizes<'a> {
- #[inline]
- fn process(&self, flow: &mut dyn Flow) {
- flow.assign_inline_sizes(self.layout_context);
- }
-
- #[inline]
- fn should_process(&self, flow: &mut dyn Flow) -> bool {
- flow.base()
- .restyle_damage
- .intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW)
- }
-}
-
-/// The assign-block-sizes-and-store-overflow traversal, the last (and most expensive) part of
-/// layout computation. Determines the final block-sizes for all layout objects and computes
-/// positions. In Gecko this corresponds to `Reflow`.
-#[derive(Clone, Copy)]
-pub struct AssignBSizes<'a> {
- pub layout_context: &'a LayoutContext<'a>,
-}
-
-impl<'a> PostorderFlowTraversal for AssignBSizes<'a> {
- #[inline]
- fn process(&self, flow: &mut dyn Flow) {
- // Can't do anything with anything that floats might flow through until we reach their
- // inorder parent.
- //
- // NB: We must return without resetting the restyle bits for these, as we haven't actually
- // reflowed anything!
- if flow.floats_might_flow_through() {
- return;
- }
-
- flow.assign_block_size(self.layout_context);
- }
-
- #[inline]
- fn should_process(&self, flow: &mut dyn Flow) -> bool {
- let base = flow.base();
- base.restyle_damage.intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW) &&
- // The fragmentation countainer is responsible for calling
- // Flow::fragment recursively
- !base.flags.contains(FlowFlags::CAN_BE_FRAGMENTED)
- }
-}
-
-pub struct ComputeStackingRelativePositions<'a> {
- pub layout_context: &'a LayoutContext<'a>,
-}
-
-impl<'a> PreorderFlowTraversal for ComputeStackingRelativePositions<'a> {
- #[inline]
- fn should_process_subtree(&self, flow: &mut dyn Flow) -> bool {
- flow.base()
- .restyle_damage
- .contains(ServoRestyleDamage::REPOSITION)
- }
-
- #[inline]
- fn process(&self, flow: &mut dyn Flow) {
- flow.compute_stacking_relative_position(self.layout_context);
- flow.mut_base()
- .restyle_damage
- .remove(ServoRestyleDamage::REPOSITION)
- }
-}
-
-pub struct BuildDisplayList<'a> {
- pub state: DisplayListBuildState<'a>,
-}
-
-impl<'a> BuildDisplayList<'a> {
- #[inline]
- pub fn traverse(&mut self, flow: &mut dyn Flow) {
- let parent_stacking_context_id = self.state.current_stacking_context_id;
- self.state.current_stacking_context_id = flow.base().stacking_context_id;
-
- let parent_clipping_and_scrolling = self.state.current_clipping_and_scrolling;
- self.state.current_clipping_and_scrolling = flow.clipping_and_scrolling();
-
- flow.build_display_list(&mut self.state);
- flow.mut_base()
- .restyle_damage
- .remove(ServoRestyleDamage::REPAINT);
-
- for kid in flow.mut_base().child_iter_mut() {
- self.traverse(kid);
- }
-
- self.state.current_stacking_context_id = parent_stacking_context_id;
- self.state.current_clipping_and_scrolling = parent_clipping_and_scrolling;
- }
-}
diff --git a/components/layout_2020/wrapper.rs b/components/layout_2020/wrapper.rs
index 063d7f3d25d..02a0f8ceeee 100644
--- a/components/layout_2020/wrapper.rs
+++ b/components/layout_2020/wrapper.rs
@@ -2,65 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-//! A safe wrapper for DOM nodes that prevents layout from mutating the DOM, from letting DOM nodes
-//! escape, and from generally doing anything that it isn't supposed to. This is accomplished via
-//! a simple whitelist of allowed operations, along with some lifetime magic to prevent nodes from
-//! escaping.
-//!
-//! As a security wrapper is only as good as its whitelist, be careful when adding operations to
-//! this list. The cardinal rules are:
-//!
-//! 1. Layout is not allowed to mutate the DOM.
-//!
-//! 2. Layout is not allowed to see anything with `LayoutDom` in the name, because it could hang
-//! onto these objects and cause use-after-free.
-//!
-//! When implementing wrapper functions, be careful that you do not touch the borrow flags, or you
-//! will race and cause spurious thread failure. (Note that I do not believe these races are
-//! exploitable, but they'll result in brokenness nonetheless.)
-//!
-//! Rules of the road for this file:
-//!
-//! * Do not call any methods on DOM nodes without checking to see whether they use borrow flags.
-//!
-//! o Instead of `get_attr()`, use `.get_attr_val_for_layout()`.
-//!
-//! o Instead of `html_element_in_html_document()`, use
-//! `html_element_in_html_document_for_layout()`.
-
#![allow(unsafe_code)]
-use crate::data::{LayoutData, LayoutDataFlags, StyleAndLayoutData};
-use atomic_refcell::{AtomicRef, AtomicRefMut};
+use crate::data::StyleAndLayoutData;
use script_layout_interface::wrapper_traits::GetLayoutData;
-use script_layout_interface::wrapper_traits::{ThreadSafeLayoutElement, ThreadSafeLayoutNode};
-use style::dom::{NodeInfo, TNode};
-use style::selector_parser::RestyleDamage;
-use style::values::computed::counters::ContentItem;
-use style::values::generics::counters::Content;
-
-pub trait LayoutNodeLayoutData {
- /// Similar to borrow_data*, but returns the full PersistentLayoutData rather
- /// than only the style::data::ElementData.
- fn borrow_layout_data(&self) -> Option<AtomicRef<LayoutData>>;
- fn mutate_layout_data(&self) -> Option<AtomicRefMut<LayoutData>>;
- fn flow_debug_id(self) -> usize;
-}
-
-impl<T: GetLayoutData> LayoutNodeLayoutData for T {
- fn borrow_layout_data(&self) -> Option<AtomicRef<LayoutData>> {
- self.get_raw_data().map(|d| d.layout_data.borrow())
- }
-
- fn mutate_layout_data(&self) -> Option<AtomicRefMut<LayoutData>> {
- self.get_raw_data().map(|d| d.layout_data.borrow_mut())
- }
-
- fn flow_debug_id(self) -> usize {
- self.borrow_layout_data()
- .map_or(0, |d| d.flow_construction_result.debug_id())
- }
-}
pub trait GetRawData {
fn get_raw_data(&self) -> Option<&StyleAndLayoutData>;
@@ -74,101 +19,3 @@ impl<T: GetLayoutData> GetRawData for T {
})
}
}
-
-pub trait ThreadSafeLayoutNodeHelpers {
- /// Returns the layout data flags for this node.
- fn flags(self) -> LayoutDataFlags;
-
- /// Adds the given flags to this node.
- fn insert_flags(self, new_flags: LayoutDataFlags);
-
- /// Removes the given flags from this node.
- fn remove_flags(self, flags: LayoutDataFlags);
-
- /// If this is a text node, generated content, or a form element, copies out
- /// its content. Otherwise, panics.
- ///
- /// FIXME(pcwalton): This might have too much copying and/or allocation. Profile this.
- fn text_content(&self) -> TextContent;
-
- /// The RestyleDamage from any restyling, or RestyleDamage::rebuild_and_reflow() if this
- /// is the first time layout is visiting this node. We implement this here, rather than
- /// with the rest of the wrapper layer, because we need layout code to determine whether
- /// layout has visited the node.
- fn restyle_damage(self) -> RestyleDamage;
-}
-
-impl<T: ThreadSafeLayoutNode> ThreadSafeLayoutNodeHelpers for T {
- fn flags(self) -> LayoutDataFlags {
- self.borrow_layout_data().as_ref().unwrap().flags
- }
-
- fn insert_flags(self, new_flags: LayoutDataFlags) {
- self.mutate_layout_data().unwrap().flags.insert(new_flags);
- }
-
- fn remove_flags(self, flags: LayoutDataFlags) {
- self.mutate_layout_data().unwrap().flags.remove(flags);
- }
-
- fn text_content(&self) -> TextContent {
- if self.get_pseudo_element_type().is_replaced_content() {
- let style = self.as_element().unwrap().resolved_style();
-
- return TextContent::GeneratedContent(match style.as_ref().get_counters().content {
- Content::Items(ref value) => value.to_vec(),
- _ => vec![],
- });
- }
-
- TextContent::Text(self.node_text_content().into_boxed_str())
- }
-
- fn restyle_damage(self) -> RestyleDamage {
- // We need the underlying node to potentially access the parent in the
- // case of text nodes. This is safe as long as we don't let the parent
- // escape and never access its descendants.
- let mut node = unsafe { self.unsafe_get() };
-
- // If this is a text node, use the parent element, since that's what
- // controls our style.
- if node.is_text_node() {
- node = node.parent_node().unwrap();
- debug_assert!(node.is_element());
- }
-
- let damage = {
- let data = node.get_raw_data().unwrap();
-
- if !data
- .layout_data
- .borrow()
- .flags
- .contains(crate::data::LayoutDataFlags::HAS_BEEN_TRAVERSED)
- {
- // We're reflowing a node that was styled for the first time and
- // has never been visited by layout. Return rebuild_and_reflow,
- // because that's what the code expects.
- RestyleDamage::rebuild_and_reflow()
- } else {
- data.style_data.element_data.borrow().damage
- }
- };
-
- damage
- }
-}
-
-pub enum TextContent {
- Text(Box<str>),
- GeneratedContent(Vec<ContentItem>),
-}
-
-impl TextContent {
- pub fn is_empty(&self) -> bool {
- match *self {
- TextContent::Text(_) => false,
- TextContent::GeneratedContent(ref content) => content.is_empty(),
- }
- }
-}
diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs
index 4f5018e0a98..2e3f5956e43 100644
--- a/components/layout_thread_2020/lib.rs
+++ b/components/layout_thread_2020/lib.rs
@@ -13,8 +13,6 @@ extern crate crossbeam_channel;
#[macro_use]
extern crate html5ever;
#[macro_use]
-extern crate layout;
-#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
@@ -30,7 +28,7 @@ use crossbeam_channel::{unbounded, Receiver, Sender};
use embedder_traits::resources::{self, Resource};
use euclid::{Point2D, Rect, Size2D, TypedScale, TypedSize2D};
use fnv::FnvHashMap;
-use fxhash::{FxHashMap, FxHashSet};
+use fxhash::FxHashMap;
use gfx::font;
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context;
@@ -38,19 +36,11 @@ use gfx_traits::{node_id_from_scroll_id, Epoch};
use histogram::Histogram;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
-use layout::animation;
-use layout::construct::ConstructionResult;
-use layout::context::malloc_size_of_persistent_local_context;
use layout::context::LayoutContext;
-use layout::context::RegisteredPainter;
-use layout::context::RegisteredPainters;
-use layout::display_list::items::{OpaqueNode, WebRenderImageInfo};
-use layout::display_list::{IndexableText, ToLayout, WebRenderDisplayListConverter};
-use layout::flow::{Flow, GetBaseFlow, ImmutableFlowUtils, MutableOwnedFlowUtils};
+use layout::display_list::items::OpaqueNode;
+use layout::display_list::{IndexableText, WebRenderDisplayListConverter};
+use layout::flow::{Flow, GetBaseFlow};
use layout::flow_ref::FlowRef;
-use layout::incremental::{RelayoutMode, SpecialRestyleDamage};
-use layout::layout_debug;
-use layout::parallel;
use layout::query::{
process_content_box_request, process_content_boxes_request, LayoutRPCImpl, LayoutThreadData,
};
@@ -59,11 +49,7 @@ use layout::query::{process_node_scroll_area_request, process_node_scroll_id_req
use layout::query::{
process_offset_parent_query, process_resolved_style_request, process_style_query,
};
-use layout::sequential;
-use layout::traversal::{
- ComputeStackingRelativePositions, PreorderFlowTraversal, RecalcStyleAndConstructFlows,
-};
-use layout::wrapper::LayoutNodeLayoutData;
+use layout::traversal::RecalcStyleAndConstructFlows;
use layout_traits::LayoutThreadFactory;
use libc::c_void;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
@@ -71,30 +57,29 @@ use metrics::{PaintTimeMetrics, ProfilerMetadataFactory, ProgressiveWebMetric};
use msg::constellation_msg::{
BackgroundHangMonitor, BackgroundHangMonitorRegister, HangAnnotation,
};
-use msg::constellation_msg::{BrowsingContextId, MonitoredComponentId, TopLevelBrowsingContextId};
+use msg::constellation_msg::{MonitoredComponentId, TopLevelBrowsingContextId};
use msg::constellation_msg::{LayoutHangAnnotation, MonitoredComponentType, PipelineId};
-use net_traits::image_cache::{ImageCache, UsePlaceholder};
+use net_traits::image_cache::ImageCache;
use parking_lot::RwLock;
use profile_traits::mem::{self as profile_mem, Report, ReportKind, ReportsChan};
use profile_traits::time::{self as profile_time, profile, TimerMetadata};
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
-use script_layout_interface::message::{LayoutThreadInit, Msg, NodesFromPointQueryType, Reflow};
+use script_layout_interface::message::{LayoutThreadInit, Msg, NodesFromPointQueryType};
use script_layout_interface::message::{QueryMsg, ReflowComplete, ReflowGoal, ScriptReflow};
use script_layout_interface::rpc::TextIndexResponse;
use script_layout_interface::rpc::{LayoutRPC, OffsetParentResponse, StyleResponse};
use script_layout_interface::wrapper_traits::LayoutNode;
use script_traits::Painter;
use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
-use script_traits::{DrawAPaintImageResult, IFrameSizeMsg, PaintWorkletError, WindowSizeType};
+use script_traits::{DrawAPaintImageResult, PaintWorkletError};
use script_traits::{ScrollState, UntrustedNodeAddress};
use selectors::Element;
use servo_arc::Arc as ServoArc;
use servo_atoms::Atom;
use servo_config::opts;
use servo_config::pref;
-use servo_geometry::{DeviceIndependentPixel, MaxRect};
+use servo_geometry::DeviceIndependentPixel;
use servo_url::ServoUrl;
-use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
@@ -111,7 +96,6 @@ use style::driver;
use style::error_reporting::RustLogReporter;
use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
use style::invalidation::element::restyle_hints::RestyleHint;
-use style::logical_geometry::LogicalPoint;
use style::media_queries::{Device, MediaList, MediaType};
use style::properties::PropertyId;
use style::selector_parser::SnapshotMap;
@@ -161,9 +145,6 @@ pub struct LayoutThread {
/// A means of communication with the background hang monitor.
background_hang_monitor: Box<dyn BackgroundHangMonitor>,
- /// The channel on which messages can be sent to the constellation.
- constellation_chan: IpcSender<ConstellationMsg>,
-
/// The channel on which messages can be sent to the script thread.
script_chan: IpcSender<ConstellationControlMsg>,
@@ -173,9 +154,6 @@ pub struct LayoutThread {
/// The channel on which messages can be sent to the memory profiler.
mem_profiler_chan: profile_mem::ProfilerChan,
- /// Reference to the script thread image cache.
- image_cache: Arc<dyn ImageCache>,
-
/// Public interface to the font cache thread.
font_cache_thread: FontCacheThread,
@@ -194,7 +172,7 @@ pub struct LayoutThread {
new_animations_sender: Sender<Animation>,
/// Receives newly-discovered animations.
- new_animations_receiver: Receiver<Animation>,
+ _new_animations_receiver: Receiver<Animation>,
/// The number of Web fonts that have been requested but not yet loaded.
outstanding_web_fonts: Arc<AtomicUsize>,
@@ -224,8 +202,6 @@ pub struct LayoutThread {
/// All the other elements of this struct are read-only.
rw_data: Arc<Mutex<LayoutThreadData>>,
- webrender_image_cache: Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo>>>,
-
/// The executors for paint worklets.
registered_painters: RegisteredPaintersImpl,
@@ -245,9 +221,6 @@ pub struct LayoutThread {
/// The time a layout query has waited before serviced by layout thread.
layout_query_waiting_time: Histogram,
- /// The sizes of all iframes encountered during the last layout operation.
- last_iframe_sizes: RefCell<HashMap<BrowsingContextId, TypedSize2D<f32, CSSPixel>>>,
-
/// Flag that indicates if LayoutThread is busy handling a request.
busy: Arc<AtomicBool>,
@@ -302,7 +275,7 @@ impl LayoutThreadFactory for LayoutThread {
background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
constellation_chan: IpcSender<ConstellationMsg>,
script_chan: IpcSender<ConstellationControlMsg>,
- image_cache: Arc<dyn ImageCache>,
+ _image_cache: Arc<dyn ImageCache>,
font_cache_thread: FontCacheThread,
time_profiler_chan: profile_time::ProfilerChan,
mem_profiler_chan: profile_mem::ProfilerChan,
@@ -352,7 +325,6 @@ impl LayoutThreadFactory for LayoutThread {
background_hang_monitor,
constellation_chan,
script_chan,
- image_cache.clone(),
font_cache_thread,
time_profiler_chan,
mem_profiler_chan.clone(),
@@ -526,7 +498,6 @@ impl LayoutThread {
background_hang_monitor: Box<dyn BackgroundHangMonitor>,
constellation_chan: IpcSender<ConstellationMsg>,
script_chan: IpcSender<ConstellationControlMsg>,
- image_cache: Arc<dyn ImageCache>,
font_cache_thread: FontCacheThread,
time_profiler_chan: profile_time::ProfilerChan,
mem_profiler_chan: profile_mem::ProfilerChan,
@@ -574,11 +545,9 @@ impl LayoutThread {
pipeline_port: pipeline_receiver,
script_chan: script_chan.clone(),
background_hang_monitor,
- constellation_chan: constellation_chan.clone(),
time_profiler_chan: time_profiler_chan,
mem_profiler_chan: mem_profiler_chan,
registered_painters: RegisteredPaintersImpl(Default::default()),
- image_cache: image_cache.clone(),
font_cache_thread: font_cache_thread,
first_reflow: Cell::new(true),
font_cache_receiver: font_cache_receiver,
@@ -586,7 +555,7 @@ impl LayoutThread {
parallel_flag: true,
generation: Cell::new(0),
new_animations_sender: new_animations_sender,
- new_animations_receiver: new_animations_receiver,
+ _new_animations_receiver: new_animations_receiver,
outstanding_web_fonts: Arc::new(AtomicUsize::new(0)),
root_flow: RefCell::new(None),
document_shared_lock: None,
@@ -614,7 +583,6 @@ impl LayoutThread {
nodes_from_point_response: vec![],
element_inner_text_response: String::new(),
})),
- webrender_image_cache: Arc::new(RwLock::new(FnvHashMap::default())),
timer: if pref!(layout.animations.test.enabled) {
Timer::test_mode()
} else {
@@ -622,7 +590,6 @@ impl LayoutThread {
},
paint_time_metrics: paint_time_metrics,
layout_query_waiting_time: Histogram::new(),
- last_iframe_sizes: Default::default(),
busy,
load_webfonts_synchronously,
initial_window_size,
@@ -655,7 +622,6 @@ impl LayoutThread {
fn build_layout_context<'a>(
&'a self,
guards: StylesheetGuards<'a>,
- script_initiated_layout: bool,
snapshot_map: &'a SnapshotMap,
) -> LayoutContext<'a> {
let thread_local_style_context_creation_data =
@@ -676,20 +642,6 @@ impl LayoutThread {
traversal_flags: TraversalFlags::empty(),
snapshot_map: snapshot_map,
},
- image_cache: self.image_cache.clone(),
- font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
- webrender_image_cache: self.webrender_image_cache.clone(),
- pending_images: if script_initiated_layout {
- Some(Mutex::new(vec![]))
- } else {
- None
- },
- newly_transitioning_nodes: if script_initiated_layout {
- Some(Mutex::new(vec![]))
- } else {
- None
- },
- registered_painters: &self.registered_painters,
}
}
@@ -862,23 +814,7 @@ impl LayoutThread {
Msg::SetFinalUrl(final_url) => {
self.url = final_url;
},
- Msg::RegisterPaint(name, mut properties, painter) => {
- debug!("Registering the painter");
- let properties = properties
- .drain(..)
- .filter_map(|name| {
- let id = PropertyId::parse_enabled_for_all_content(&*name).ok()?;
- Some((name.clone(), id))
- })
- .filter(|&(_, ref id)| !id.is_shorthand())
- .collect();
- let registered_painter = RegisteredPainterImpl {
- name: name.clone(),
- properties,
- painter,
- };
- self.registered_painters.0.insert(name, registered_painter);
- },
+ Msg::RegisterPaint(_name, _properties, _painter) => {},
Msg::PrepareToExit(response_chan) => {
self.prepare_to_exit(response_chan);
return false;
@@ -926,13 +862,6 @@ impl LayoutThread {
size: self.stylist.size_of(&mut ops),
});
- // The LayoutThread has data in Persistent TLS...
- reports.push(Report {
- path: path![formatted_url, "layout-thread", "local-context"],
- kind: ReportKind::ExplicitJemallocHeapSize,
- size: malloc_size_of_persistent_local_context(&mut ops),
- });
-
reports_chan.send(reports);
}
@@ -1045,171 +974,35 @@ impl LayoutThread {
self.stylist.set_quirks_mode(quirks_mode);
}
- fn try_get_layout_root<N: LayoutNode>(&self, node: N) -> Option<FlowRef> {
- let result = node.mutate_layout_data()?.flow_construction_result.get();
-
- let mut flow = match result {
- ConstructionResult::Flow(mut flow, abs_descendants) => {
- // Note: Assuming that the root has display 'static' (as per
- // CSS Section 9.3.1). Otherwise, if it were absolutely
- // positioned, it would return a reference to itself in
- // `abs_descendants` and would lead to a circular reference.
- // Set Root as CB for any remaining absolute descendants.
- flow.set_absolute_descendants(abs_descendants);
- flow
- },
- _ => return None,
- };
-
- FlowRef::deref_mut(&mut flow).mark_as_root();
-
- Some(flow)
- }
-
- /// Performs layout constraint solving.
- ///
- /// This corresponds to `Reflow()` in Gecko and `layout()` in WebKit/Blink and should be
- /// benchmarked against those two. It is marked `#[inline(never)]` to aid profiling.
- #[inline(never)]
- fn solve_constraints(layout_root: &mut dyn Flow, layout_context: &LayoutContext) {
- let _scope = layout_debug_scope!("solve_constraints");
- sequential::reflow(layout_root, layout_context, RelayoutMode::Incremental);
- }
-
- /// Performs layout constraint solving in parallel.
- ///
- /// This corresponds to `Reflow()` in Gecko and `layout()` in WebKit/Blink and should be
- /// benchmarked against those two. It is marked `#[inline(never)]` to aid profiling.
- #[inline(never)]
- fn solve_constraints_parallel(
- traversal: &rayon::ThreadPool,
- layout_root: &mut dyn Flow,
- profiler_metadata: Option<TimerMetadata>,
- time_profiler_chan: profile_time::ProfilerChan,
- layout_context: &LayoutContext,
- ) {
- let _scope = layout_debug_scope!("solve_constraints_parallel");
-
- // NOTE: this currently computes borders, so any pruning should separate that
- // operation out.
- parallel::reflow(
- layout_root,
- profiler_metadata,
- time_profiler_chan,
- layout_context,
- traversal,
- );
+ fn try_get_layout_root<N: LayoutNode>(&self, _node: N) -> Option<FlowRef> {
+ None
}
/// Computes the stacking-relative positions of all flows and, if the painting is dirty and the
/// reflow type need it, builds the display list.
fn compute_abs_pos_and_build_display_list(
&self,
- data: &Reflow,
reflow_goal: &ReflowGoal,
document: Option<&ServoLayoutDocument>,
layout_root: &mut dyn Flow,
- layout_context: &mut LayoutContext,
rw_data: &mut LayoutThreadData,
) {
- let writing_mode = layout_root.base().writing_mode;
let (metadata, sender) = (self.profiler_metadata(), self.time_profiler_chan.clone());
profile(
profile_time::ProfilerCategory::LayoutDispListBuild,
metadata.clone(),
sender.clone(),
|| {
- layout_root.mut_base().stacking_relative_position =
- LogicalPoint::zero(writing_mode)
- .to_physical(writing_mode, self.viewport_size)
- .to_vector();
-
- layout_root.mut_base().clip = data.page_clip_rect;
-
- let traversal = ComputeStackingRelativePositions {
- layout_context: layout_context,
- };
- traversal.traverse(layout_root);
-
if layout_root
.base()
.restyle_damage
.contains(ServoRestyleDamage::REPAINT) ||
rw_data.display_list.is_none()
{
- if reflow_goal.needs_display_list() {
- let background_color = get_root_flow_background_color(layout_root);
- let mut build_state = sequential::build_display_list_for_subtree(
- layout_root,
- layout_context,
- background_color,
- data.page_clip_rect.size,
- );
-
- debug!("Done building display list.");
-
- let root_size = {
- let root_flow = layout_root.base();
- if self.stylist.viewport_constraints().is_some() {
- root_flow.position.size.to_physical(root_flow.writing_mode)
- } else {
- root_flow.overflow.scroll.size
- }
- };
-
- let origin = Rect::new(Point2D::new(Au(0), Au(0)), root_size).to_layout();
- build_state.root_stacking_context.bounds = origin;
- build_state.root_stacking_context.overflow = origin;
-
- if !build_state.iframe_sizes.is_empty() {
- // build_state.iframe_sizes is only used here, so its okay to replace
- // it with an empty vector
- let iframe_sizes =
- std::mem::replace(&mut build_state.iframe_sizes, vec![]);
- // Collect the last frame's iframe sizes to compute any differences.
- // Every frame starts with a fresh collection so that any removed
- // iframes do not linger.
- let last_iframe_sizes = std::mem::replace(
- &mut *self.last_iframe_sizes.borrow_mut(),
- HashMap::default(),
- );
- let mut size_messages = vec![];
- for new_size in iframe_sizes {
- // Only notify the constellation about existing iframes
- // that have a new size, or iframes that did not previously
- // exist.
- if let Some(old_size) = last_iframe_sizes.get(&new_size.id) {
- if *old_size != new_size.size {
- size_messages.push(IFrameSizeMsg {
- data: new_size,
- type_: WindowSizeType::Resize,
- });
- }
- } else {
- size_messages.push(IFrameSizeMsg {
- data: new_size,
- type_: WindowSizeType::Initial,
- });
- }
- self.last_iframe_sizes
- .borrow_mut()
- .insert(new_size.id, new_size.size);
- }
-
- if !size_messages.is_empty() {
- let msg = ConstellationMsg::IFrameSizes(size_messages);
- if let Err(e) = self.constellation_chan.send(msg) {
- warn!("Layout resize to constellation failed ({}).", e);
- }
- }
- }
-
- rw_data.indexable_text = std::mem::replace(
- &mut build_state.indexable_text,
- IndexableText::default(),
- );
- rw_data.display_list = Some(build_state.to_display_list());
- }
+ layout_root
+ .mut_base()
+ .restyle_damage
+ .remove(ServoRestyleDamage::REPAINT);
}
if !reflow_goal.needs_display() {
@@ -1228,13 +1021,6 @@ impl LayoutThread {
let display_list = rw_data.display_list.as_mut().unwrap();
- if self.dump_display_list {
- display_list.print();
- }
- if self.dump_display_list_json {
- println!("{}", serde_json::to_string_pretty(&display_list).unwrap());
- }
-
debug!("Layout done!");
// TODO: Avoid the temporary conversion and build webrender sc/dl directly!
@@ -1500,7 +1286,7 @@ impl LayoutThread {
self.stylist.flush(&guards, Some(element), Some(&map));
// Create a layout context for use throughout the following passes.
- let mut layout_context = self.build_layout_context(guards.clone(), true, &map);
+ let mut layout_context = self.build_layout_context(guards.clone(), &map);
let (thread_pool, num_threads) = if self.parallel_flag {
(
@@ -1579,12 +1365,9 @@ impl LayoutThread {
if let Some(mut root_flow) = self.root_flow.borrow().clone() {
self.perform_post_style_recalc_layout_passes(
&mut root_flow,
- &data.reflow_info,
&data.reflow_goal,
Some(&document),
&mut rw_data,
- &mut layout_context,
- FxHashSet::default(),
);
}
@@ -1593,7 +1376,6 @@ impl LayoutThread {
&data.reflow_goal,
&mut *rw_data,
&mut layout_context,
- data.result.borrow_mut().as_mut().unwrap(),
);
}
@@ -1602,32 +1384,14 @@ impl LayoutThread {
reflow_goal: &ReflowGoal,
rw_data: &mut LayoutThreadData,
context: &mut LayoutContext,
- reflow_result: &mut ReflowComplete,
) {
- let pending_images = match context.pending_images {
- Some(ref pending) => std::mem::replace(&mut *pending.lock().unwrap(), vec![]),
- None => vec![],
- };
- reflow_result.pending_images = pending_images;
-
- let newly_transitioning_nodes = match context.newly_transitioning_nodes {
- Some(ref nodes) => std::mem::replace(&mut *nodes.lock().unwrap(), vec![]),
- None => vec![],
- };
- reflow_result.newly_transitioning_nodes = newly_transitioning_nodes;
-
- let mut root_flow = match self.root_flow.borrow().clone() {
- Some(root_flow) => root_flow,
- None => return,
- };
- let root_flow = FlowRef::deref_mut(&mut root_flow);
match *reflow_goal {
ReflowGoal::LayoutQuery(ref querymsg, _) => match querymsg {
&QueryMsg::ContentBoxQuery(node) => {
- rw_data.content_box_response = process_content_box_request(node, root_flow);
+ rw_data.content_box_response = process_content_box_request(node);
},
&QueryMsg::ContentBoxesQuery(node) => {
- rw_data.content_boxes_response = process_content_boxes_request(node, root_flow);
+ rw_data.content_boxes_response = process_content_boxes_request(node);
},
&QueryMsg::TextIndexQuery(node, point_in_node) => {
let point_in_node = Point2D::new(
@@ -1638,11 +1402,11 @@ impl LayoutThread {
TextIndexResponse(rw_data.indexable_text.text_index(node, point_in_node));
},
&QueryMsg::NodeGeometryQuery(node) => {
- rw_data.client_rect_response = process_node_geometry_request(node, root_flow);
+ rw_data.client_rect_response = process_node_geometry_request(node);
},
&QueryMsg::NodeScrollGeometryQuery(node) => {
rw_data.scroll_area_response =
- process_node_scroll_area_request(node, root_flow);
+ process_node_scroll_area_request(node);
},
&QueryMsg::NodeScrollIdQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&node) };
@@ -1652,10 +1416,10 @@ impl LayoutThread {
&QueryMsg::ResolvedStyleQuery(node, ref pseudo, ref property) => {
let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.resolved_style_response =
- process_resolved_style_request(context, node, pseudo, property, root_flow);
+ process_resolved_style_request(context, node, pseudo, property);
},
&QueryMsg::OffsetParentQuery(node) => {
- rw_data.offset_parent_response = process_offset_parent_query(node, root_flow);
+ rw_data.offset_parent_response = process_offset_parent_query(node);
},
&QueryMsg::StyleQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&node) };
@@ -1737,203 +1501,62 @@ impl LayoutThread {
}
if let Some(mut root_flow) = self.root_flow.borrow().clone() {
- let reflow_info = Reflow {
- page_clip_rect: Rect::max_rect(),
- };
-
// Unwrap here should not panic since self.root_flow is only ever set to Some(_)
// in handle_reflow() where self.document_shared_lock is as well.
let author_shared_lock = self.document_shared_lock.clone().unwrap();
let author_guard = author_shared_lock.read();
let ua_or_user_guard = UA_STYLESHEETS.shared_lock.read();
- let guards = StylesheetGuards {
+ let _guards = StylesheetGuards {
author: &author_guard,
ua_or_user: &ua_or_user_guard,
};
- let snapshots = SnapshotMap::new();
- let mut layout_context = self.build_layout_context(guards, false, &snapshots);
- let invalid_nodes = {
- // Perform an abbreviated style recalc that operates without access to the DOM.
- let animations = self.running_animations.read();
- profile(
- profile_time::ProfilerCategory::LayoutStyleRecalc,
- self.profiler_metadata(),
- self.time_profiler_chan.clone(),
- || {
- animation::recalc_style_for_animations::<ServoLayoutElement>(
- &layout_context,
- FlowRef::deref_mut(&mut root_flow),
- &animations,
- )
- },
- )
- };
self.perform_post_style_recalc_layout_passes(
&mut root_flow,
- &reflow_info,
&ReflowGoal::TickAnimations,
None,
&mut *rw_data,
- &mut layout_context,
- invalid_nodes,
);
- assert!(layout_context.pending_images.is_none());
- assert!(layout_context.newly_transitioning_nodes.is_none());
}
}
fn perform_post_style_recalc_layout_passes(
&self,
root_flow: &mut FlowRef,
- data: &Reflow,
reflow_goal: &ReflowGoal,
document: Option<&ServoLayoutDocument>,
rw_data: &mut LayoutThreadData,
- context: &mut LayoutContext,
- invalid_nodes: FxHashSet<OpaqueNode>,
) {
- {
- let mut newly_transitioning_nodes = context
- .newly_transitioning_nodes
- .as_ref()
- .map(|nodes| nodes.lock().unwrap());
- let newly_transitioning_nodes =
- newly_transitioning_nodes.as_mut().map(|nodes| &mut **nodes);
- // Kick off animations if any were triggered, expire completed ones.
- animation::update_animation_state::<ServoLayoutElement>(
- &self.constellation_chan,
- &self.script_chan,
- &mut *self.running_animations.write(),
- &mut *self.expired_animations.write(),
- invalid_nodes,
- newly_transitioning_nodes,
- &self.new_animations_receiver,
- self.id,
- &self.timer,
- );
- }
-
profile(
profile_time::ProfilerCategory::LayoutRestyleDamagePropagation,
self.profiler_metadata(),
self.time_profiler_chan.clone(),
- || {
- // Call `compute_layout_damage` even in non-incremental mode, because it sets flags
- // that are needed in both incremental and non-incremental traversals.
- let damage = FlowRef::deref_mut(root_flow).compute_layout_damage();
-
- if self.nonincremental_layout ||
- damage.contains(SpecialRestyleDamage::REFLOW_ENTIRE_DOCUMENT)
- {
- FlowRef::deref_mut(root_flow).reflow_entire_document()
- }
- },
- );
-
- if self.trace_layout {
- layout_debug::begin_trace(root_flow.clone());
- }
-
- // Resolve generated content.
- profile(
- profile_time::ProfilerCategory::LayoutGeneratedContent,
- self.profiler_metadata(),
- self.time_profiler_chan.clone(),
- || sequential::resolve_generated_content(FlowRef::deref_mut(root_flow), &context),
- );
-
- // Guess float placement.
- profile(
- profile_time::ProfilerCategory::LayoutFloatPlacementSpeculation,
- self.profiler_metadata(),
- self.time_profiler_chan.clone(),
- || sequential::guess_float_placement(FlowRef::deref_mut(root_flow)),
- );
-
- // Perform the primary layout passes over the flow tree to compute the locations of all
- // the boxes.
- if root_flow
- .base()
- .restyle_damage
- .intersects(ServoRestyleDamage::REFLOW | ServoRestyleDamage::REFLOW_OUT_OF_FLOW)
- {
- profile(
- profile_time::ProfilerCategory::LayoutMain,
- self.profiler_metadata(),
- self.time_profiler_chan.clone(),
- || {
- let profiler_metadata = self.profiler_metadata();
-
- let thread_pool = if self.parallel_flag {
- STYLE_THREAD_POOL.style_thread_pool.as_ref()
- } else {
- None
- };
-
- if let Some(pool) = thread_pool {
- // Parallel mode.
- LayoutThread::solve_constraints_parallel(
- pool,
- FlowRef::deref_mut(root_flow),
- profiler_metadata,
- self.time_profiler_chan.clone(),
- &*context,
- );
- } else {
- //Sequential mode
- LayoutThread::solve_constraints(FlowRef::deref_mut(root_flow), &context)
- }
- },
- );
- }
-
- profile(
- profile_time::ProfilerCategory::LayoutStoreOverflow,
- self.profiler_metadata(),
- self.time_profiler_chan.clone(),
- || {
- sequential::store_overflow(context, FlowRef::deref_mut(root_flow) as &mut dyn Flow);
- },
+ || {},
);
self.perform_post_main_layout_passes(
- data,
root_flow,
reflow_goal,
document,
rw_data,
- context,
);
}
fn perform_post_main_layout_passes(
&self,
- data: &Reflow,
mut root_flow: &mut FlowRef,
reflow_goal: &ReflowGoal,
document: Option<&ServoLayoutDocument>,
rw_data: &mut LayoutThreadData,
- layout_context: &mut LayoutContext,
) {
// Build the display list if necessary, and send it to the painter.
self.compute_abs_pos_and_build_display_list(
- data,
reflow_goal,
document,
FlowRef::deref_mut(&mut root_flow),
- &mut *layout_context,
rw_data,
);
- if self.trace_layout {
- layout_debug::end_trace(self.generation.get());
- }
-
- if self.dump_flow_tree {
- root_flow.print("Post layout flow tree".to_owned());
- }
-
self.generation.set(self.generation.get() + 1);
}
@@ -1945,10 +1568,6 @@ impl LayoutThread {
ServoRestyleDamage::REFLOW |
ServoRestyleDamage::REPOSITION,
);
-
- for child in flow.mut_base().child_iter_mut() {
- LayoutThread::reflow_all_nodes(child);
- }
}
/// Returns profiling information which is passed to the time profiler.
@@ -1975,51 +1594,6 @@ impl ProfilerMetadataFactory for LayoutThread {
}
}
-// The default computed value for background-color is transparent (see
-// http://dev.w3.org/csswg/css-backgrounds/#background-color). However, we
-// need to propagate the background color from the root HTML/Body
-// element (http://dev.w3.org/csswg/css-backgrounds/#special-backgrounds) if
-// it is non-transparent. The phrase in the spec "If the canvas background
-// is not opaque, what shows through is UA-dependent." is handled by rust-layers
-// clearing the frame buffer to white. This ensures that setting a background
-// color on an iframe element, while the iframe content itself has a default
-// transparent background color is handled correctly.
-fn get_root_flow_background_color(flow: &mut dyn Flow) -> webrender_api::ColorF {
- let transparent = webrender_api::ColorF {
- r: 0.0,
- g: 0.0,
- b: 0.0,
- a: 0.0,
- };
- if !flow.is_block_like() {
- return transparent;
- }
-
- let block_flow = flow.as_mut_block();
- let kid = match block_flow.base.children.iter_mut().next() {
- None => return transparent,
- Some(kid) => kid,
- };
- if !kid.is_block_like() {
- return transparent;
- }
-
- let kid_block_flow = kid.as_block();
- let color = kid_block_flow.fragment.style.resolve_color(
- kid_block_flow
- .fragment
- .style
- .get_background()
- .background_color,
- );
- webrender_api::ColorF::new(
- color.red_f32(),
- color.green_f32(),
- color.blue_f32(),
- color.alpha_f32(),
- )
-}
-
fn get_ua_stylesheets() -> Result<UserAgentStylesheets, &'static str> {
fn parse_ua_stylesheet(
shared_lock: &SharedRwLock,
@@ -2144,8 +1718,6 @@ impl Painter for RegisteredPainterImpl {
}
}
-impl RegisteredPainter for RegisteredPainterImpl {}
-
struct RegisteredPaintersImpl(FnvHashMap<Atom, RegisteredPainterImpl>);
impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
@@ -2155,11 +1727,3 @@ impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
.map(|painter| painter as &dyn RegisteredSpeculativePainter)
}
}
-
-impl RegisteredPainters for RegisteredPaintersImpl {
- fn get(&self, name: &Atom) -> Option<&dyn RegisteredPainter> {
- self.0
- .get(&name)
- .map(|painter| painter as &dyn RegisteredPainter)
- }
-}