mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-24 02:35:41 +00:00
eea8cb0cb9
(Do not merge) Merge some test crates in their corresponding components Source-Repo: https://github.com/servo/servo Source-Revision: a4808a3649e6cb6e579e60b82d9979755643f711 --HG-- rename : servo/tests/unit/servo_config/opts.rs => servo/components/config/tests/opts.rs rename : servo/tests/unit/servo_config/prefs.rs => servo/components/config/tests/prefs.rs rename : servo/tests/unit/gfx/text_util.rs => servo/components/gfx/tests/text_util.rs rename : servo/tests/unit/layout/size_of.rs => servo/components/layout/tests/size_of.rs rename : servo/tests/unit/msg/size_of.rs => servo/components/msg/tests/size_of.rs rename : servo/tests/unit/net/chrome_loader.rs => servo/components/net/tests/chrome_loader.rs rename : servo/tests/unit/net/cookie.rs => servo/components/net/tests/cookie.rs rename : servo/tests/unit/net/cookie_http_state.rs => servo/components/net/tests/cookie_http_state.rs rename : servo/tests/unit/net/cookie_http_state_utils.py => servo/components/net/tests/cookie_http_state_utils.py rename : servo/tests/unit/net/data_loader.rs => servo/components/net/tests/data_loader.rs rename : servo/tests/unit/net/fetch.rs => servo/components/net/tests/fetch.rs rename : servo/tests/unit/net/file_loader.rs => servo/components/net/tests/file_loader.rs rename : servo/tests/unit/net/filemanager_thread.rs => servo/components/net/tests/filemanager_thread.rs rename : servo/tests/unit/net/hsts.rs => servo/components/net/tests/hsts.rs rename : servo/tests/unit/net/http_loader.rs => servo/components/net/tests/http_loader.rs rename : servo/tests/unit/net/lib.rs => servo/components/net/tests/main.rs rename : servo/tests/unit/net/mime_classifier.rs => servo/components/net/tests/mime_classifier.rs rename : servo/tests/unit/net/parsable_mime/application/font-woff/test.wof => servo/components/net/tests/parsable_mime/application/font-woff/test.wof rename : servo/tests/unit/net/parsable_mime/application/ogg/small.ogg => servo/components/net/tests/parsable_mime/application/ogg/small.ogg rename : servo/tests/unit/net/parsable_mime/application/pdf/test.pdf => servo/components/net/tests/parsable_mime/application/pdf/test.pdf rename : servo/tests/unit/net/parsable_mime/application/postscript/test.ps => servo/components/net/tests/parsable_mime/application/postscript/test.ps rename : servo/tests/unit/net/parsable_mime/application/vnd.ms-fontobject/vnd.ms-fontobject => servo/components/net/tests/parsable_mime/application/vnd.ms-fontobject/vnd.ms-fontobject rename : servo/tests/unit/net/parsable_mime/application/x-gzip/test.gz => servo/components/net/tests/parsable_mime/application/x-gzip/test.gz rename : servo/tests/unit/net/parsable_mime/application/x-rar-compressed/test.rar => servo/components/net/tests/parsable_mime/application/x-rar-compressed/test.rar rename : servo/tests/unit/net/parsable_mime/application/zip/test.zip => servo/components/net/tests/parsable_mime/application/zip/test.zip rename : servo/tests/unit/net/parsable_mime/audio/aiff/test.aif => servo/components/net/tests/parsable_mime/audio/aiff/test.aif rename : servo/tests/unit/net/parsable_mime/audio/basic/test.au => servo/components/net/tests/parsable_mime/audio/basic/test.au rename : servo/tests/unit/net/parsable_mime/audio/midi/test.mid => servo/components/net/tests/parsable_mime/audio/midi/test.mid rename : servo/tests/unit/net/parsable_mime/audio/mpeg/test.mp3 => servo/components/net/tests/parsable_mime/audio/mpeg/test.mp3 rename : servo/tests/unit/net/parsable_mime/audio/wave/test.wav => servo/components/net/tests/parsable_mime/audio/wave/test.wav rename : servo/tests/unit/net/parsable_mime/image/bmp/test.bmp => servo/components/net/tests/parsable_mime/image/bmp/test.bmp rename : servo/tests/unit/net/parsable_mime/image/gif/test87a => servo/components/net/tests/parsable_mime/image/gif/test87a rename : servo/tests/unit/net/parsable_mime/image/gif/test89a.gif => servo/components/net/tests/parsable_mime/image/gif/test89a.gif rename : servo/tests/unit/net/parsable_mime/image/jpeg/test.jpg => servo/components/net/tests/parsable_mime/image/jpeg/test.jpg rename : servo/tests/unit/net/parsable_mime/image/png/test.png => servo/components/net/tests/parsable_mime/image/png/test.png rename : servo/tests/unit/net/parsable_mime/image/webp/test.webp => servo/components/net/tests/parsable_mime/image/webp/test.webp rename : servo/tests/unit/net/parsable_mime/image/x-icon/test.ico => servo/components/net/tests/parsable_mime/image/x-icon/test.ico rename : servo/tests/unit/net/parsable_mime/image/x-icon/test_cursor.ico => servo/components/net/tests/parsable_mime/image/x-icon/test_cursor.ico rename : servo/tests/unit/net/parsable_mime/text/html/text_html_a_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_a_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_a_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_a_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_a_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_a_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_a_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_a_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_b_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_b_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_b_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_b_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_b_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_b_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_b_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_b_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_body_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_body_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_body_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_body_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_body_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_body_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_body_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_body_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_br_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_br_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_br_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_br_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_br_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_br_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_br_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_br_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_comment_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_comment_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_comment_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_comment_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_comment_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_comment_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_comment_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_comment_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_div_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_div_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_div_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_div_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_div_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_div_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_div_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_div_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_doctype_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_doctype_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_doctype_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_doctype_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_doctype_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_doctype_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_doctype_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_doctype_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_font_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_font_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_font_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_font_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_font_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_font_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_font_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_font_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_h1_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_h1_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_h1_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_h1_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_h1_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_h1_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_h1_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_h1_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_head_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_head_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_head_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_head_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_head_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_head_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_head_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_head_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_iframe_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_iframe_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_iframe_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_iframe_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_iframe_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_iframe_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_iframe_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_iframe_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_p_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_p_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_p_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_p_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_p_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_p_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_p_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_p_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_page_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_page_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_page_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_page_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_page_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_page_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_page_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_page_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_script_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_script_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_script_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_script_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_script_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_script_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_script_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_script_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_style_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_style_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_style_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_style_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_style_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_style_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_style_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_style_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_table_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_table_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_table_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_table_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_table_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_table_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_table_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_table_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_title_20.html => servo/components/net/tests/parsable_mime/text/html/text_html_title_20.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_title_20_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_title_20_u.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_title_3e.html => servo/components/net/tests/parsable_mime/text/html/text_html_title_3e.html rename : servo/tests/unit/net/parsable_mime/text/html/text_html_title_3e_u.html => servo/components/net/tests/parsable_mime/text/html/text_html_title_3e_u.html rename : servo/tests/unit/net/parsable_mime/text/plain/utf16bebom.txt => servo/components/net/tests/parsable_mime/text/plain/utf16bebom.txt rename : servo/tests/unit/net/parsable_mime/text/plain/utf16lebom.txt => servo/components/net/tests/parsable_mime/text/plain/utf16lebom.txt rename : servo/tests/unit/net/parsable_mime/text/plain/utf8bom.txt => servo/components/net/tests/parsable_mime/text/plain/utf8bom.txt rename : servo/tests/unit/net/parsable_mime/text/xml/feed.atom => servo/components/net/tests/parsable_mime/text/xml/feed.atom rename : servo/tests/unit/net/parsable_mime/text/xml/feed.rss => servo/components/net/tests/parsable_mime/text/xml/feed.rss rename : servo/tests/unit/net/parsable_mime/text/xml/rdf_rss.xml => servo/components/net/tests/parsable_mime/text/xml/rdf_rss.xml rename : servo/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_1.xml => servo/components/net/tests/parsable_mime/text/xml/rdf_rss_ko_1.xml rename : servo/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_2.xml => servo/components/net/tests/parsable_mime/text/xml/rdf_rss_ko_2.xml rename : servo/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_3.xml => servo/components/net/tests/parsable_mime/text/xml/rdf_rss_ko_3.xml rename : servo/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_4.xml => servo/components/net/tests/parsable_mime/text/xml/rdf_rss_ko_4.xml rename : servo/tests/unit/net/parsable_mime/text/xml/test.xml => servo/components/net/tests/parsable_mime/text/xml/test.xml rename : servo/tests/unit/net/parsable_mime/unknown/binary_file => servo/components/net/tests/parsable_mime/unknown/binary_file rename : servo/tests/unit/net/parsable_mime/unknown/open_type => servo/components/net/tests/parsable_mime/unknown/open_type rename : servo/tests/unit/net/parsable_mime/unknown/true_type.ttf => servo/components/net/tests/parsable_mime/unknown/true_type.ttf rename : servo/tests/unit/net/parsable_mime/unknown/true_type_collection.ttc => servo/components/net/tests/parsable_mime/unknown/true_type_collection.ttc rename : servo/tests/unit/net/parsable_mime/video/avi/test.avi => servo/components/net/tests/parsable_mime/video/avi/test.avi rename : servo/tests/unit/net/parsable_mime/video/mp4/test.mp4 => servo/components/net/tests/parsable_mime/video/mp4/test.mp4 rename : servo/tests/unit/net/parsable_mime/video/webm/test.webm => servo/components/net/tests/parsable_mime/video/webm/test.webm rename : servo/tests/unit/net/resource_thread.rs => servo/components/net/tests/resource_thread.rs rename : servo/tests/unit/net/subresource_integrity.rs => servo/components/net/tests/subresource_integrity.rs rename : servo/tests/unit/net/test.jpeg => servo/components/net/tests/test.jpeg rename : servo/tests/unit/net_traits/image.rs => servo/components/net_traits/tests/image.rs rename : servo/tests/unit/net_traits/pub_domains.rs => servo/components/net_traits/tests/pub_domains.rs rename : servo/tests/unit/net_traits/lib.rs => servo/components/net_traits/tests/whitespace.rs rename : servo/tests/unit/servo_remutex/lib.rs => servo/components/remutex/tests/smoke.rs extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : ccaf67122c28522d622dcde39da197b3f2daf5b3
1451 lines
58 KiB
Rust
1451 lines
58 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||
|
||
//! 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 app_units::Au;
|
||
use block::{BlockFlow, FormattingContextType};
|
||
use context::LayoutContext;
|
||
use display_list::{DisplayListBuildState, StackingContextCollectionState};
|
||
use euclid::{Transform3D, Point2D, Vector2D, Rect, Size2D};
|
||
use flex::FlexFlow;
|
||
use floats::{Floats, SpeculatedFloatPlacement};
|
||
use flow_list::{FlowList, FlowListIterator, MutFlowListIterator};
|
||
use flow_ref::{FlowRef, WeakFlowRef};
|
||
use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow};
|
||
use gfx::display_list::ClippingAndScrolling;
|
||
use gfx_traits::StackingContextId;
|
||
use gfx_traits::print_tree::PrintTree;
|
||
use inline::InlineFlow;
|
||
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
|
||
use multicol::MulticolFlow;
|
||
use parallel::FlowParallelInfo;
|
||
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::iter::Zip;
|
||
use std::slice::IterMut;
|
||
use std::sync::Arc;
|
||
use std::sync::atomic::Ordering;
|
||
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::LengthOrPercentageOrAuto;
|
||
use table::TableFlow;
|
||
use table_caption::TableCaptionFlow;
|
||
use table_cell::TableCellFlow;
|
||
use table_colgroup::TableColGroupFlow;
|
||
use table_row::TableRowFlow;
|
||
use table_rowgroup::TableRowGroupFlow;
|
||
use table_wrapper::TableWrapperFlow;
|
||
|
||
/// 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;
|
||
}
|
||
|
||
impl<T: HasBaseFlow + ?Sized> GetBaseFlow for T {
|
||
#[inline(always)]
|
||
#[allow(unsafe_code)]
|
||
fn base(&self) -> &BaseFlow {
|
||
let ptr: *const Self = self;
|
||
let ptr = ptr as *const BaseFlow;
|
||
unsafe { &*ptr }
|
||
}
|
||
|
||
#[inline(always)]
|
||
#[allow(unsafe_code)]
|
||
fn mut_base(&mut self) -> &mut BaseFlow {
|
||
let ptr: *mut Self = self;
|
||
let ptr = ptr as *mut BaseFlow;
|
||
unsafe { &mut *ptr }
|
||
}
|
||
}
|
||
|
||
/// 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 a flex flow, returns the underlying object, borrowed mutably. Fails otherwise.
|
||
fn as_mut_flex(&mut self) -> &mut FlexFlow {
|
||
panic!("called as_mut_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, borrowed mutably. Fails
|
||
/// otherwise.
|
||
fn as_mut_table_wrapper(&mut self) -> &mut TableWrapperFlow {
|
||
panic!("called as_mut_table_wrapper() on a non-tablewrapper 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 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_caption(&mut self) -> &mut TableCaptionFlow {
|
||
panic!("called as_mut_table_caption() on a non-tablecaption 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 multicol flow, returns the underlying object, borrowed mutably. Fails
|
||
/// otherwise.
|
||
fn as_mut_multicol(&mut self) -> &mut MulticolFlow {
|
||
panic!("called as_mut_multicol() on a non-multicol 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<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(Transform3D::identity())
|
||
.to_2d();
|
||
let transformed_overflow = Overflow {
|
||
paint: f32_rect_to_au_rect(transform_2d.transform_rect(
|
||
&au_rect_to_f32_rect(overflow.paint))),
|
||
scroll: f32_rect_to_au_rect(transform_2d.transform_rect(
|
||
&au_rect_to_f32_rect(overflow.scroll))),
|
||
};
|
||
|
||
// TODO: We are taking the union of the overflow and transformed overflow here, which
|
||
// happened implicitly in the previous version of this code. This will probably be
|
||
// unnecessary once we are taking into account 3D transformations above.
|
||
overflow.union(&transformed_overflow);
|
||
|
||
overflow.translate(&position.origin.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 FragmentBorderBoxIterator,
|
||
level: i32,
|
||
stacking_context_position: &Point2D<Au>);
|
||
|
||
/// Mutably iterates through fragments in this flow.
|
||
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment));
|
||
|
||
fn compute_collapsible_block_start_margin(&mut self,
|
||
_layout_context: &mut LayoutContext,
|
||
_margin_collapse_info: &mut MarginCollapseInfo) {
|
||
// The default implementation is a no-op.
|
||
}
|
||
|
||
/// 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: &::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 proper table child.
|
||
fn is_proper_table_child(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 true if this flow is one of table-related flows.
|
||
fn is_table_kind(self) -> bool;
|
||
|
||
/// Returns true if this flow has no children.
|
||
fn is_leaf(self) -> bool;
|
||
|
||
/// Returns the number of children that this flow possesses.
|
||
fn child_count(self) -> usize;
|
||
|
||
/// Return true if this flow is a Block Container.
|
||
fn is_block_container(self) -> bool;
|
||
|
||
/// 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: &::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 Flow;
|
||
fn next(&mut self) -> Option<&'a mut Flow> {
|
||
self.iter.next().map(|info| FlowRef::deref_mut(&mut info.flow))
|
||
}
|
||
|
||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||
self.iter.size_hint()
|
||
}
|
||
}
|
||
|
||
pub type AbsoluteDescendantOffsetIter<'a> = Zip<AbsoluteDescendantIter<'a>, IterMut<'a, Au>>;
|
||
|
||
/// 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,
|
||
}
|
||
|
||
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 == LengthOrPercentageOrAuto::Auto &&
|
||
logical_position.inline_end == LengthOrPercentageOrAuto::Auto {
|
||
flags.insert(FlowFlags::INLINE_POSITION_IS_STATIC);
|
||
}
|
||
if logical_position.block_start == LengthOrPercentageOrAuto::Auto &&
|
||
logical_position.block_end == LengthOrPercentageOrAuto::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.0.is_empty() ||
|
||
!style.get_counters().counter_increment.0.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 == LengthOrPercentageOrAuto::Auto &&
|
||
logical_position.inline_end == LengthOrPercentageOrAuto::Auto);
|
||
self.flags.set(FlowFlags::BLOCK_POSITION_IS_STATIC,
|
||
logical_position.block_start == LengthOrPercentageOrAuto::Auto &&
|
||
logical_position.block_end == LengthOrPercentageOrAuto::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 flow_id(&self) -> usize {
|
||
return self as *const BaseFlow 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)
|
||
}
|
||
}
|
||
|
||
impl<'a> ImmutableFlowUtils for &'a 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 proper table child.
|
||
/// 'Proper table child' is defined as table-row flow, table-rowgroup flow,
|
||
/// table-column-group flow, or table-caption flow.
|
||
fn is_proper_table_child(self) -> bool {
|
||
match self.class() {
|
||
FlowClass::TableRow | FlowClass::TableRowGroup |
|
||
FlowClass::TableColGroup | FlowClass::TableCaption => true,
|
||
_ => false,
|
||
}
|
||
}
|
||
|
||
/// 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 true if this flow is one of table-related flows.
|
||
fn is_table_kind(self) -> bool {
|
||
match self.class() {
|
||
FlowClass::TableWrapper | FlowClass::Table |
|
||
FlowClass::TableColGroup | FlowClass::TableRowGroup |
|
||
FlowClass::TableRow | FlowClass::TableCaption | FlowClass::TableCell => true,
|
||
_ => false,
|
||
}
|
||
}
|
||
|
||
/// Returns true if this flow has no children.
|
||
fn is_leaf(self) -> bool {
|
||
self.base().children.is_empty()
|
||
}
|
||
|
||
/// Returns the number of children that this flow possesses.
|
||
fn child_count(self) -> usize {
|
||
self.base().children.len()
|
||
}
|
||
|
||
/// Return true if this flow is a Block Container.
|
||
///
|
||
/// Except for table fragments and replaced elements, block-level fragments (`BlockFlow`) are
|
||
/// also block container fragments.
|
||
/// Non-replaced inline blocks and non-replaced table cells are also block
|
||
/// containers.
|
||
fn is_block_container(self) -> bool {
|
||
match self.class() {
|
||
// TODO: Change this when inline-blocks are supported.
|
||
FlowClass::Block | FlowClass::TableCaption | FlowClass::TableCell => {
|
||
// FIXME: Actually check the type of the node
|
||
self.child_count() != 0
|
||
}
|
||
_ => false,
|
||
}
|
||
}
|
||
|
||
/// 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 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: &::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: &Flow) -> OpaqueFlow {
|
||
let object_ptr: *const Flow = flow;
|
||
let data_ptr = object_ptr as *const ();
|
||
OpaqueFlow(data_ptr as usize)
|
||
}
|
||
}
|