mirror of
https://github.com/servo/servo.git
synced 2025-06-06 00:25:37 +00:00
Merge 94a662193e
into b422a65a00
This commit is contained in:
commit
3b0c0d8cce
19 changed files with 1068 additions and 295 deletions
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -6607,7 +6607,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "selectors"
|
||||
version = "0.28.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620"
|
||||
source = "git+https://github.com/coding-joedow/stylo?branch=main#9a87b429580c4b8504b6259b8f9c674d1cb10fe6"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"cssparser",
|
||||
|
@ -6902,7 +6902,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "servo_arc"
|
||||
version = "0.4.1"
|
||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620"
|
||||
source = "git+https://github.com/coding-joedow/stylo?branch=main#9a87b429580c4b8504b6259b8f9c674d1cb10fe6"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
|
@ -7363,7 +7363,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "stylo"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620"
|
||||
source = "git+https://github.com/coding-joedow/stylo?branch=main#9a87b429580c4b8504b6259b8f9c674d1cb10fe6"
|
||||
dependencies = [
|
||||
"app_units",
|
||||
"arrayvec",
|
||||
|
@ -7421,7 +7421,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "stylo_atoms"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620"
|
||||
source = "git+https://github.com/coding-joedow/stylo?branch=main#9a87b429580c4b8504b6259b8f9c674d1cb10fe6"
|
||||
dependencies = [
|
||||
"string_cache",
|
||||
"string_cache_codegen",
|
||||
|
@ -7430,12 +7430,12 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "stylo_config"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620"
|
||||
source = "git+https://github.com/coding-joedow/stylo?branch=main#9a87b429580c4b8504b6259b8f9c674d1cb10fe6"
|
||||
|
||||
[[package]]
|
||||
name = "stylo_derive"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620"
|
||||
source = "git+https://github.com/coding-joedow/stylo?branch=main#9a87b429580c4b8504b6259b8f9c674d1cb10fe6"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
|
@ -7447,7 +7447,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "stylo_dom"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620"
|
||||
source = "git+https://github.com/coding-joedow/stylo?branch=main#9a87b429580c4b8504b6259b8f9c674d1cb10fe6"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"stylo_malloc_size_of",
|
||||
|
@ -7456,7 +7456,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "stylo_malloc_size_of"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620"
|
||||
source = "git+https://github.com/coding-joedow/stylo?branch=main#9a87b429580c4b8504b6259b8f9c674d1cb10fe6"
|
||||
dependencies = [
|
||||
"app_units",
|
||||
"cssparser",
|
||||
|
@ -7473,12 +7473,12 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "stylo_static_prefs"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620"
|
||||
source = "git+https://github.com/coding-joedow/stylo?branch=main#9a87b429580c4b8504b6259b8f9c674d1cb10fe6"
|
||||
|
||||
[[package]]
|
||||
name = "stylo_traits"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620"
|
||||
source = "git+https://github.com/coding-joedow/stylo?branch=main#9a87b429580c4b8504b6259b8f9c674d1cb10fe6"
|
||||
dependencies = [
|
||||
"app_units",
|
||||
"bitflags 2.9.1",
|
||||
|
@ -7887,7 +7887,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
[[package]]
|
||||
name = "to_shmem"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620"
|
||||
source = "git+https://github.com/coding-joedow/stylo?branch=main#9a87b429580c4b8504b6259b8f9c674d1cb10fe6"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"servo_arc",
|
||||
|
@ -7900,7 +7900,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "to_shmem_derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620"
|
||||
source = "git+https://github.com/coding-joedow/stylo?branch=main#9a87b429580c4b8504b6259b8f9c674d1cb10fe6"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
|
|
16
Cargo.toml
16
Cargo.toml
|
@ -119,7 +119,7 @@ rustls-pemfile = "2.0"
|
|||
rustls-pki-types = "1.12"
|
||||
script_layout_interface = { path = "components/shared/script_layout" }
|
||||
script_traits = { path = "components/shared/script" }
|
||||
selectors = { git = "https://github.com/servo/stylo", branch = "2025-05-01" }
|
||||
selectors = { git = "https://github.com/coding-joedow/stylo", branch = "main" }
|
||||
serde = "1.0.219"
|
||||
serde_bytes = "0.11"
|
||||
serde_json = "1.0"
|
||||
|
@ -127,7 +127,7 @@ servo-media = { git = "https://github.com/servo/media" }
|
|||
servo-media-dummy = { git = "https://github.com/servo/media" }
|
||||
servo-media-gstreamer = { git = "https://github.com/servo/media" }
|
||||
servo-tracing = { path = "components/servo_tracing" }
|
||||
servo_arc = { git = "https://github.com/servo/stylo", branch = "2025-05-01" }
|
||||
servo_arc = { git = "https://github.com/coding-joedow/stylo", branch = "main" }
|
||||
smallbitvec = "2.6.0"
|
||||
smallvec = "1.15"
|
||||
snapshot = { path = "./components/shared/snapshot" }
|
||||
|
@ -136,12 +136,12 @@ string_cache = "0.8"
|
|||
string_cache_codegen = "0.5"
|
||||
strum = "0.26"
|
||||
strum_macros = "0.26"
|
||||
stylo = { git = "https://github.com/servo/stylo", branch = "2025-05-01" }
|
||||
stylo_atoms = { git = "https://github.com/servo/stylo", branch = "2025-05-01" }
|
||||
stylo_config = { git = "https://github.com/servo/stylo", branch = "2025-05-01" }
|
||||
stylo_dom = { git = "https://github.com/servo/stylo", branch = "2025-05-01" }
|
||||
stylo_malloc_size_of = { git = "https://github.com/servo/stylo", branch = "2025-05-01" }
|
||||
stylo_traits = { git = "https://github.com/servo/stylo", branch = "2025-05-01" }
|
||||
stylo = { git = "https://github.com/coding-joedow/stylo", branch = "main" }
|
||||
stylo_atoms = { git = "https://github.com/coding-joedow/stylo", branch = "main" }
|
||||
stylo_config = { git = "https://github.com/coding-joedow/stylo", branch = "main" }
|
||||
stylo_dom = { git = "https://github.com/coding-joedow/stylo", branch = "main" }
|
||||
stylo_malloc_size_of = { git = "https://github.com/coding-joedow/stylo", branch = "main" }
|
||||
stylo_traits = { git = "https://github.com/coding-joedow/stylo", branch = "main" }
|
||||
surfman = { git = "https://github.com/servo/surfman", rev = "f7688b4585f9e0b5d4bf8ee8e4a91e82349610b1", features = ["chains"] }
|
||||
syn = { version = "2", default-features = false, features = ["clone-impls", "derive", "parsing"] }
|
||||
synstructure = "0.13"
|
||||
|
|
|
@ -21,6 +21,10 @@ impl<T> ArcRefCell<T> {
|
|||
value: Arc::new(AtomicRefCell::new(value)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
|
||||
Arc::ptr_eq(&this.value, &other.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for ArcRefCell<T> {
|
||||
|
|
|
@ -20,7 +20,7 @@ use script_layout_interface::{
|
|||
use servo_arc::Arc as ServoArc;
|
||||
use style::context::SharedStyleContext;
|
||||
use style::properties::ComputedValues;
|
||||
use style::selector_parser::PseudoElement;
|
||||
use style::selector_parser::{PseudoElement, RestyleDamage};
|
||||
|
||||
use crate::cell::ArcRefCell;
|
||||
use crate::flexbox::FlexLevelBox;
|
||||
|
@ -160,7 +160,6 @@ pub struct BoxSlot<'dom> {
|
|||
/// A mutable reference to a `LayoutBox` stored in a DOM element.
|
||||
impl BoxSlot<'_> {
|
||||
pub(crate) fn new(slot: ArcRefCell<Option<LayoutBox>>) -> Self {
|
||||
*slot.borrow_mut() = None;
|
||||
let slot = Some(slot);
|
||||
Self {
|
||||
slot,
|
||||
|
@ -216,6 +215,7 @@ pub(crate) trait NodeExt<'dom> {
|
|||
fn invalidate_cached_fragment(&self);
|
||||
|
||||
fn repair_style(&self, context: &SharedStyleContext);
|
||||
fn clear_restyle_damage(&self);
|
||||
}
|
||||
|
||||
impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
|
||||
|
@ -404,4 +404,10 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_restyle_damage(&self) {
|
||||
if let Some(data) = self.style_data() {
|
||||
data.element_data.borrow_mut().damage = RestyleDamage::empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ use selectors::Element as SelectorsElement;
|
|||
use servo_arc::Arc as ServoArc;
|
||||
use style::dom::{NodeInfo, TElement, TNode, TShadowRoot};
|
||||
use style::properties::ComputedValues;
|
||||
use style::selector_parser::PseudoElement;
|
||||
use style::selector_parser::{PseudoElement, RestyleDamage};
|
||||
use style::values::generics::counters::{Content, ContentItem};
|
||||
use style::values::specified::Quotes;
|
||||
|
||||
|
@ -94,6 +94,24 @@ impl<'dom> NodeAndStyleInfo<'dom> {
|
|||
pub(crate) fn get_selection_range(&self) -> Option<Range<ByteIndex>> {
|
||||
self.node.to_threadsafe().selection()
|
||||
}
|
||||
|
||||
pub(crate) fn get_restyle_damage(&self) -> RestyleDamage {
|
||||
self.node.style_data().unwrap().element_data.borrow().damage
|
||||
}
|
||||
|
||||
pub(crate) fn is_anonymous(&self) -> bool {
|
||||
if matches!(
|
||||
self.pseudo_element_type,
|
||||
Some(PseudoElement::ServoAnonymousBox) |
|
||||
Some(PseudoElement::ServoAnonymousTable) |
|
||||
Some(PseudoElement::ServoAnonymousTableCell) |
|
||||
Some(PseudoElement::ServoAnonymousTableRow)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'dom> From<&NodeAndStyleInfo<'dom>> for BaseFragmentInfo {
|
||||
|
@ -108,13 +126,7 @@ impl<'dom> From<&NodeAndStyleInfo<'dom>> for BaseFragmentInfo {
|
|||
// TODO(mrobinson): It seems that anonymous boxes should take part in hit testing in some
|
||||
// cases, but currently this means that the order of hit test results isn't as expected for
|
||||
// some WPT tests. This needs more investigation.
|
||||
if matches!(
|
||||
pseudo,
|
||||
Some(PseudoElement::ServoAnonymousBox) |
|
||||
Some(PseudoElement::ServoAnonymousTable) |
|
||||
Some(PseudoElement::ServoAnonymousTableCell) |
|
||||
Some(PseudoElement::ServoAnonymousTableRow)
|
||||
) {
|
||||
if info.is_anonymous() {
|
||||
return Self::anonymous();
|
||||
}
|
||||
|
||||
|
@ -198,6 +210,12 @@ pub(super) trait TraversalHandler<'dom> {
|
|||
|
||||
/// Notify the handler that we have finished a `display: contents` element.
|
||||
fn leave_display_contents(&mut self) {}
|
||||
|
||||
/// Returns whether the pseudo element box should be cleared before traversed
|
||||
/// by this handler.
|
||||
fn need_clear_pseudo_element_box(&self, _node: &ServoLayoutNode<'dom>) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn traverse_children_of<'dom>(
|
||||
|
@ -246,7 +264,9 @@ fn traverse_element<'dom>(
|
|||
) {
|
||||
// Clear any existing pseudo-element box slot, because markers are not handled like
|
||||
// `::before`` and `::after`. They are processed during box tree creation.
|
||||
element.unset_pseudo_element_box(PseudoElement::Marker);
|
||||
if handler.need_clear_pseudo_element_box(&element) {
|
||||
element.unset_pseudo_element_box(PseudoElement::Marker);
|
||||
}
|
||||
|
||||
let replaced = ReplacedContents::for_element(element, context);
|
||||
let style = element.style(context.shared_context());
|
||||
|
@ -288,6 +308,14 @@ fn traverse_element<'dom>(
|
|||
handler.handle_element(&info, display, contents, box_slot);
|
||||
},
|
||||
}
|
||||
|
||||
// Currently, we do not support storing the restyle damage caused by their
|
||||
// styles changes for pseudo-element and list-marker seperately, which is
|
||||
// stored at the originating element's style data. Clearing restyle damage
|
||||
// for the originating element at here for keeping consistent with current
|
||||
// restyle damage detecting strategy: when the originating element's box can
|
||||
// be kept unchanged, the pseudo-element and list-marker will be kept unchanged.
|
||||
element.clear_restyle_damage();
|
||||
}
|
||||
|
||||
fn traverse_eager_pseudo_element<'dom>(
|
||||
|
@ -299,7 +327,9 @@ fn traverse_eager_pseudo_element<'dom>(
|
|||
assert!(pseudo_element_type.is_eager());
|
||||
|
||||
// First clear any old contents from the node.
|
||||
node.unset_pseudo_element_box(pseudo_element_type);
|
||||
if handler.need_clear_pseudo_element_box(&node) {
|
||||
node.unset_pseudo_element_box(pseudo_element_type);
|
||||
}
|
||||
|
||||
let Some(element) = node.to_threadsafe().as_element() else {
|
||||
return;
|
||||
|
|
|
@ -142,6 +142,12 @@ impl FlexContainer {
|
|||
self.config = FlexContainerConfig::new(new_style);
|
||||
self.style = new_style.clone();
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_subtree_caches(&self) {
|
||||
for flex_level_box in &self.children {
|
||||
flex_level_box.borrow().invalidate_subtree_caches();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, MallocSizeOf)]
|
||||
|
@ -182,6 +188,17 @@ impl FlexLevelBox {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_subtree_caches(&self) {
|
||||
match self {
|
||||
FlexLevelBox::FlexItem(flex_item_box) => flex_item_box
|
||||
.independent_formatting_context
|
||||
.invalidate_subtree_caches(),
|
||||
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
|
||||
positioned_box.borrow().context.invalidate_subtree_caches()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fragments(&self) -> Vec<Fragment> {
|
||||
match self {
|
||||
FlexLevelBox::FlexItem(flex_item_box) => flex_item_box
|
||||
|
|
|
@ -4,12 +4,16 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::convert::TryFrom;
|
||||
use std::mem;
|
||||
|
||||
use atomic_refcell::AtomicRef;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
use script::layout_dom::ServoLayoutNode;
|
||||
use script_layout_interface::wrapper_traits::LayoutNode;
|
||||
use servo_arc::Arc;
|
||||
use style::properties::ComputedValues;
|
||||
use style::properties::longhands::list_style_position::computed_value::T as ListStylePosition;
|
||||
use style::selector_parser::PseudoElement;
|
||||
use style::selector_parser::{PseudoElement, RestyleDamage};
|
||||
use style::str::char_is_whitespace;
|
||||
|
||||
use super::OutsideMarker;
|
||||
|
@ -56,9 +60,36 @@ impl BlockFormattingContext {
|
|||
contains_floats,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn repair(
|
||||
&mut self,
|
||||
context: &LayoutContext,
|
||||
info: &NodeAndStyleInfo<'_>,
|
||||
contents: NonReplacedContents,
|
||||
propagated_data: PropagatedBoxTreeData,
|
||||
is_list_item: bool,
|
||||
) {
|
||||
self.contents
|
||||
.repair(context, info, contents, propagated_data, is_list_item);
|
||||
self.contains_floats = self.contents.contains_floats();
|
||||
}
|
||||
}
|
||||
|
||||
struct BlockLevelJob<'dom> {
|
||||
enum BlockLevelJob<'dom> {
|
||||
Copy(ArcRefCell<BlockLevelBox>),
|
||||
Repair(BlockLevelRepairJob<'dom>),
|
||||
Create(BlockLevelCreateJob<'dom>),
|
||||
}
|
||||
|
||||
struct BlockLevelRepairJob<'dom> {
|
||||
info: NodeAndStyleInfo<'dom>,
|
||||
repaired_box: ArcRefCell<BlockLevelBox>,
|
||||
propagated_data: PropagatedBoxTreeData,
|
||||
display_inside: DisplayInside,
|
||||
contents: Contents,
|
||||
}
|
||||
|
||||
struct BlockLevelCreateJob<'dom> {
|
||||
info: NodeAndStyleInfo<'dom>,
|
||||
box_slot: BoxSlot<'dom>,
|
||||
propagated_data: PropagatedBoxTreeData,
|
||||
|
@ -104,6 +135,20 @@ enum IntermediateBlockContainer {
|
|||
},
|
||||
}
|
||||
|
||||
/// Some childern node of a given DOM node do not have `REBUILD_BOX` restyle damage.
|
||||
/// Thus, their boxes do not need to be rebuilt and can be retrieved from the
|
||||
/// previously built children boxes of the given DOM node's repaired block container.
|
||||
///
|
||||
/// This helper struct used to try to find the matched boxes in previously built
|
||||
/// children boxes for a given children node. For block level node, this helper
|
||||
/// is not very useful. It is more useful to find the inline formatting context
|
||||
/// that the inline level node participates in, and the anonymous box that wraps
|
||||
/// the inline formatting context.
|
||||
struct PreviouslyBuiltChildernMatcher {
|
||||
block_level_boxes: Vec<ArcRefCell<BlockLevelBox>>,
|
||||
block_level_boxes_cursor: usize,
|
||||
}
|
||||
|
||||
/// A builder for a block container.
|
||||
///
|
||||
/// This builder starts from the first child of a given DOM node
|
||||
|
@ -115,7 +160,9 @@ pub(crate) struct BlockContainerBuilder<'dom, 'style> {
|
|||
/// content designator, and the block container style.
|
||||
info: &'style NodeAndStyleInfo<'dom>,
|
||||
|
||||
/// The list of block-level boxes to be built for the final block container.
|
||||
/// The list of block-level boxes to be repaired or copied from previous
|
||||
/// block container building result, or newly built, for the final block
|
||||
/// container.
|
||||
///
|
||||
/// Contains all the block-level jobs we found traversing the tree
|
||||
/// so far, if this is empty at the end of the traversal and the ongoing
|
||||
|
@ -156,6 +203,10 @@ pub(crate) struct BlockContainerBuilder<'dom, 'style> {
|
|||
/// ancestors that have been processed. This `Vec` allows passing those into new
|
||||
/// [`InlineFormattingContext`]s that we create.
|
||||
display_contents_shared_styles: Vec<SharedInlineStyles>,
|
||||
|
||||
/// The [`PreviouslyBuiltChildrenMatcher`] if we need to repair the current block
|
||||
/// container, otherwise None.
|
||||
previously_built_children_matcher: Option<PreviouslyBuiltChildernMatcher>,
|
||||
}
|
||||
|
||||
impl BlockContainer {
|
||||
|
@ -166,26 +217,74 @@ impl BlockContainer {
|
|||
propagated_data: PropagatedBoxTreeData,
|
||||
is_list_item: bool,
|
||||
) -> BlockContainer {
|
||||
let mut builder = BlockContainerBuilder::new(context, info, propagated_data);
|
||||
let builder = BlockContainerBuilder::new(context, info, propagated_data);
|
||||
builder.build(contents, is_list_item)
|
||||
}
|
||||
|
||||
if is_list_item {
|
||||
if let Some((marker_info, marker_contents)) = crate::lists::make_marker(context, info) {
|
||||
match marker_info.style.clone_list_style_position() {
|
||||
ListStylePosition::Inside => {
|
||||
builder.handle_list_item_marker_inside(&marker_info, info, marker_contents)
|
||||
},
|
||||
ListStylePosition::Outside => builder.handle_list_item_marker_outside(
|
||||
&marker_info,
|
||||
info,
|
||||
marker_contents,
|
||||
info.style.clone(),
|
||||
),
|
||||
}
|
||||
}
|
||||
pub fn repair(
|
||||
&mut self,
|
||||
context: &LayoutContext,
|
||||
info: &NodeAndStyleInfo<'_>,
|
||||
contents: NonReplacedContents,
|
||||
propagated_data: PropagatedBoxTreeData,
|
||||
is_list_item: bool,
|
||||
) {
|
||||
let builder = BlockContainerBuilder::new_for_repair(context, info, propagated_data, self);
|
||||
let _ = mem::replace(self, builder.build(contents, is_list_item));
|
||||
}
|
||||
}
|
||||
|
||||
impl PreviouslyBuiltChildernMatcher {
|
||||
fn new(block_container: &mut BlockContainer) -> Self {
|
||||
let previously_built_block_level_boxes = match block_container {
|
||||
BlockContainer::BlockLevelBoxes(boxes) => mem::take(boxes),
|
||||
BlockContainer::InlineFormattingContext(_) => vec![],
|
||||
};
|
||||
|
||||
Self {
|
||||
block_level_boxes: previously_built_block_level_boxes,
|
||||
block_level_boxes_cursor: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn match_and_advance(
|
||||
&mut self,
|
||||
info: &NodeAndStyleInfo<'_>,
|
||||
) -> Option<ArcRefCell<BlockLevelBox>> {
|
||||
if info.is_anonymous() {
|
||||
return self.find_first_anonymous_box_and_advance();
|
||||
}
|
||||
|
||||
contents.traverse(context, info, &mut builder);
|
||||
builder.finish()
|
||||
let data = info.node.layout_data_mut();
|
||||
let layout_box = data.for_pseudo(info.pseudo_element_type);
|
||||
|
||||
let previously_bound_box = match &*AtomicRef::filter_map(layout_box, Option::as_ref)? {
|
||||
LayoutBox::BlockLevel(block_level_box) => block_level_box.clone(),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
// Skip the mismatch boxes, which were produced by the removed node or
|
||||
// the node has `REBUILD_BOX` restyle damage, to find the box produced by
|
||||
// current given node.
|
||||
loop {
|
||||
let curr = self.next()?;
|
||||
if ArcRefCell::ptr_eq(&curr, &previously_bound_box) {
|
||||
return Some(curr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_first_anonymous_box_and_advance(&mut self) -> Option<ArcRefCell<BlockLevelBox>> {
|
||||
unreachable!("Unexpected situations, and unimplemented");
|
||||
}
|
||||
|
||||
fn next(&mut self) -> Option<ArcRefCell<BlockLevelBox>> {
|
||||
if self.block_level_boxes_cursor < self.block_level_boxes.len() {
|
||||
self.block_level_boxes_cursor += 1;
|
||||
return Some(self.block_level_boxes[self.block_level_boxes_cursor - 1].clone());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,9 +304,48 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
|
|||
anonymous_table_content: Vec::new(),
|
||||
inline_formatting_context_builder: None,
|
||||
display_contents_shared_styles: Vec::new(),
|
||||
previously_built_children_matcher: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_for_repair(
|
||||
context: &'style LayoutContext,
|
||||
info: &'style NodeAndStyleInfo<'dom>,
|
||||
propagated_data: PropagatedBoxTreeData,
|
||||
repaired_block_container: &mut BlockContainer,
|
||||
) -> Self {
|
||||
let mut builder = Self::new(context, info, propagated_data);
|
||||
builder.previously_built_children_matcher = Some(PreviouslyBuiltChildernMatcher::new(
|
||||
repaired_block_container,
|
||||
));
|
||||
builder
|
||||
}
|
||||
|
||||
fn build(mut self, contents: NonReplacedContents, is_list_item: bool) -> BlockContainer {
|
||||
if is_list_item {
|
||||
if let Some((marker_info, marker_contents)) =
|
||||
crate::lists::make_marker(self.context, self.info)
|
||||
{
|
||||
match marker_info.style.clone_list_style_position() {
|
||||
ListStylePosition::Inside => self.handle_list_item_marker_inside(
|
||||
&marker_info,
|
||||
self.info,
|
||||
marker_contents,
|
||||
),
|
||||
ListStylePosition::Outside => self.handle_list_item_marker_outside(
|
||||
&marker_info,
|
||||
self.info,
|
||||
marker_contents,
|
||||
self.info.style.clone(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contents.traverse(self.context, self.info, &mut self);
|
||||
self.finish()
|
||||
}
|
||||
|
||||
fn currently_processing_inline_box(&self) -> bool {
|
||||
self.inline_formatting_context_builder
|
||||
.as_ref()
|
||||
|
@ -300,12 +438,13 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
|
|||
self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
|
||||
}
|
||||
|
||||
self.block_level_boxes.push(BlockLevelJob {
|
||||
info: table_info,
|
||||
box_slot: BoxSlot::dummy(),
|
||||
kind: BlockLevelCreator::AnonymousTable { table_block },
|
||||
propagated_data: self.propagated_data,
|
||||
});
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Create(BlockLevelCreateJob {
|
||||
info: table_info,
|
||||
box_slot: BoxSlot::dummy(),
|
||||
kind: BlockLevelCreator::AnonymousTable { table_block },
|
||||
propagated_data: self.propagated_data,
|
||||
}));
|
||||
}
|
||||
|
||||
// If the last element in the anonymous table content is whitespace, that
|
||||
|
@ -400,9 +539,34 @@ impl<'dom> TraversalHandler<'dom> for BlockContainerBuilder<'dom, '_> {
|
|||
builder.leave_display_contents();
|
||||
}
|
||||
}
|
||||
|
||||
fn need_clear_pseudo_element_box(&self, node: &ServoLayoutNode<'dom>) -> bool {
|
||||
let damage = node.style_data().unwrap().element_data.borrow().damage;
|
||||
if damage.contains(RestyleDamage::REBUILD_BOX) {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'dom> BlockContainerBuilder<'dom, '_> {
|
||||
fn try_reuse_block_level_box(
|
||||
&mut self,
|
||||
info: &NodeAndStyleInfo<'dom>,
|
||||
) -> Option<ArcRefCell<BlockLevelBox>> {
|
||||
let previously_built_children_matcher = self.previously_built_children_matcher.as_mut()?;
|
||||
|
||||
if info
|
||||
.get_restyle_damage()
|
||||
.contains(RestyleDamage::REBUILD_BOX)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
previously_built_children_matcher.match_and_advance(info)
|
||||
}
|
||||
|
||||
fn handle_list_item_marker_inside(
|
||||
&mut self,
|
||||
marker_info: &NodeAndStyleInfo<'dom>,
|
||||
|
@ -435,6 +599,57 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
|
|||
container_info: &NodeAndStyleInfo<'dom>,
|
||||
contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
|
||||
list_item_style: Arc<ComputedValues>,
|
||||
) {
|
||||
// TODO: We do not currently support saving box slots for ::marker pseudo-elements
|
||||
// that are part nested in ::before and ::after pseudo elements. For now, just
|
||||
// always create new box for it.
|
||||
if container_info.pseudo_element_type.is_some() {
|
||||
self.create_list_item_marker_outside(
|
||||
marker_info,
|
||||
container_info,
|
||||
contents,
|
||||
list_item_style,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(reused_block_level_box) = self.try_reuse_block_level_box(marker_info) else {
|
||||
self.create_list_item_marker_outside(
|
||||
marker_info,
|
||||
container_info,
|
||||
contents,
|
||||
list_item_style,
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
if marker_info
|
||||
.get_restyle_damage()
|
||||
.contains(RestyleDamage::REPAIR_BOX)
|
||||
{
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Repair(BlockLevelRepairJob {
|
||||
info: marker_info.clone(),
|
||||
repaired_box: reused_block_level_box,
|
||||
propagated_data: self.propagated_data,
|
||||
contents: NonReplacedContents::OfPseudoElement(contents).into(),
|
||||
display_inside: DisplayInside::Flow {
|
||||
is_list_item: false,
|
||||
},
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Copy(reused_block_level_box));
|
||||
}
|
||||
|
||||
fn create_list_item_marker_outside(
|
||||
&mut self,
|
||||
marker_info: &NodeAndStyleInfo<'dom>,
|
||||
container_info: &NodeAndStyleInfo<'dom>,
|
||||
contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
|
||||
list_item_style: Arc<ComputedValues>,
|
||||
) {
|
||||
// TODO: We do not currently support saving box slots for ::marker pseudo-elements
|
||||
// that are part nested in ::before and ::after pseudo elements. For now, just
|
||||
|
@ -446,15 +661,16 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
|
|||
.pseudo_element_box_slot(PseudoElement::Marker),
|
||||
};
|
||||
|
||||
self.block_level_boxes.push(BlockLevelJob {
|
||||
info: marker_info.clone(),
|
||||
box_slot,
|
||||
kind: BlockLevelCreator::OutsideMarker {
|
||||
contents,
|
||||
list_item_style,
|
||||
},
|
||||
propagated_data: self.propagated_data,
|
||||
});
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Create(BlockLevelCreateJob {
|
||||
info: marker_info.clone(),
|
||||
box_slot,
|
||||
kind: BlockLevelCreator::OutsideMarker {
|
||||
contents,
|
||||
list_item_style,
|
||||
},
|
||||
propagated_data: self.propagated_data,
|
||||
}));
|
||||
}
|
||||
|
||||
fn handle_inline_level_element(
|
||||
|
@ -464,12 +680,12 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
|
|||
contents: Contents,
|
||||
box_slot: BoxSlot<'dom>,
|
||||
) {
|
||||
let propagated_data = self.propagated_data;
|
||||
let (DisplayInside::Flow { is_list_item }, false) =
|
||||
(display_inside, contents.is_replaced())
|
||||
else {
|
||||
// If this inline element is an atomic, handle it and return.
|
||||
let context = self.context;
|
||||
let propagated_data = self.propagated_data;
|
||||
let atomic = self.ensure_inline_formatting_context_builder().push_atomic(
|
||||
IndependentFormattingContext::construct(
|
||||
context,
|
||||
|
@ -546,6 +762,39 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
|
|||
self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
|
||||
}
|
||||
|
||||
if let Some(reused_block_level_box) = self.try_reuse_block_level_box(info) {
|
||||
if info
|
||||
.get_restyle_damage()
|
||||
.contains(RestyleDamage::REPAIR_BOX)
|
||||
{
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Repair(BlockLevelRepairJob {
|
||||
info: info.clone(),
|
||||
repaired_box: reused_block_level_box,
|
||||
propagated_data: self.propagated_data,
|
||||
contents,
|
||||
display_inside,
|
||||
}));
|
||||
} else {
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Copy(reused_block_level_box));
|
||||
}
|
||||
} else {
|
||||
self.create_block_level_box(info, display_inside, contents, box_slot);
|
||||
}
|
||||
|
||||
// Any block also counts as the first line for the purposes of text indent. Even if
|
||||
// they don't actually indent.
|
||||
self.have_already_seen_first_line_for_text_indent = true;
|
||||
}
|
||||
|
||||
fn create_block_level_box(
|
||||
&mut self,
|
||||
info: &NodeAndStyleInfo<'dom>,
|
||||
display_inside: DisplayInside,
|
||||
contents: Contents,
|
||||
box_slot: BoxSlot<'dom>,
|
||||
) {
|
||||
let propagated_data = self.propagated_data;
|
||||
let kind = match contents {
|
||||
Contents::NonReplaced(contents) => match display_inside {
|
||||
|
@ -577,16 +826,13 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
|
|||
}
|
||||
},
|
||||
};
|
||||
self.block_level_boxes.push(BlockLevelJob {
|
||||
info: info.clone(),
|
||||
box_slot,
|
||||
kind,
|
||||
propagated_data,
|
||||
});
|
||||
|
||||
// Any block also counts as the first line for the purposes of text indent. Even if
|
||||
// they don't actually indent.
|
||||
self.have_already_seen_first_line_for_text_indent = true;
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Create(BlockLevelCreateJob {
|
||||
info: info.clone(),
|
||||
box_slot,
|
||||
kind,
|
||||
propagated_data,
|
||||
}));
|
||||
}
|
||||
|
||||
fn handle_absolutely_positioned_element(
|
||||
|
@ -610,16 +856,37 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(reused_block_level_box) = self.try_reuse_block_level_box(info) {
|
||||
if info
|
||||
.get_restyle_damage()
|
||||
.contains(RestyleDamage::REPAIR_BOX)
|
||||
{
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Repair(BlockLevelRepairJob {
|
||||
info: info.clone(),
|
||||
repaired_box: reused_block_level_box,
|
||||
propagated_data: self.propagated_data,
|
||||
contents,
|
||||
display_inside,
|
||||
}));
|
||||
} else {
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Copy(reused_block_level_box));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let kind = BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
|
||||
contents,
|
||||
display_inside,
|
||||
};
|
||||
self.block_level_boxes.push(BlockLevelJob {
|
||||
info: info.clone(),
|
||||
box_slot,
|
||||
kind,
|
||||
propagated_data: self.propagated_data,
|
||||
});
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Create(BlockLevelCreateJob {
|
||||
info: info.clone(),
|
||||
box_slot,
|
||||
kind,
|
||||
propagated_data: self.propagated_data,
|
||||
}));
|
||||
}
|
||||
|
||||
fn handle_float_element(
|
||||
|
@ -643,16 +910,37 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(reused_block_level_box) = self.try_reuse_block_level_box(info) {
|
||||
if info
|
||||
.get_restyle_damage()
|
||||
.contains(RestyleDamage::REPAIR_BOX)
|
||||
{
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Repair(BlockLevelRepairJob {
|
||||
info: info.clone(),
|
||||
repaired_box: reused_block_level_box,
|
||||
propagated_data: self.propagated_data,
|
||||
contents,
|
||||
display_inside,
|
||||
}));
|
||||
} else {
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Copy(reused_block_level_box));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let kind = BlockLevelCreator::OutOfFlowFloatBox {
|
||||
contents,
|
||||
display_inside,
|
||||
};
|
||||
self.block_level_boxes.push(BlockLevelJob {
|
||||
info: info.clone(),
|
||||
box_slot,
|
||||
kind,
|
||||
propagated_data: self.propagated_data,
|
||||
});
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Create(BlockLevelCreateJob {
|
||||
info: info.clone(),
|
||||
box_slot,
|
||||
kind,
|
||||
propagated_data: self.propagated_data,
|
||||
}));
|
||||
}
|
||||
|
||||
fn push_block_level_job_for_inline_formatting_context(
|
||||
|
@ -669,23 +957,118 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
|
|||
})
|
||||
.clone();
|
||||
|
||||
self.block_level_boxes.push(BlockLevelJob {
|
||||
info,
|
||||
// FIXME(nox): We should be storing this somewhere.
|
||||
box_slot: BoxSlot::dummy(),
|
||||
kind: BlockLevelCreator::SameFormattingContextBlock(
|
||||
IntermediateBlockContainer::InlineFormattingContext(
|
||||
BlockContainer::InlineFormattingContext(inline_formatting_context),
|
||||
self.block_level_boxes
|
||||
.push(BlockLevelJob::Create(BlockLevelCreateJob {
|
||||
info,
|
||||
// FIXME(nox): We should be storing this somewhere.
|
||||
box_slot: BoxSlot::dummy(),
|
||||
kind: BlockLevelCreator::SameFormattingContextBlock(
|
||||
IntermediateBlockContainer::InlineFormattingContext(
|
||||
BlockContainer::InlineFormattingContext(inline_formatting_context),
|
||||
),
|
||||
),
|
||||
),
|
||||
propagated_data: self.propagated_data,
|
||||
});
|
||||
propagated_data: self.propagated_data,
|
||||
}));
|
||||
|
||||
self.have_already_seen_first_line_for_text_indent = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockLevelJob<'_> {
|
||||
impl BlockLevelBox {
|
||||
pub(crate) fn repair(
|
||||
&mut self,
|
||||
context: &LayoutContext,
|
||||
info: &NodeAndStyleInfo<'_>,
|
||||
repaired_contents: Contents,
|
||||
display_inside: DisplayInside,
|
||||
propagated_data: PropagatedBoxTreeData,
|
||||
) {
|
||||
match self {
|
||||
BlockLevelBox::SameFormattingContextBlock {
|
||||
base,
|
||||
contents,
|
||||
contains_floats,
|
||||
} => match display_inside {
|
||||
DisplayInside::Flow { is_list_item } | DisplayInside::FlowRoot { is_list_item } => {
|
||||
contents.repair(
|
||||
context,
|
||||
info,
|
||||
repaired_contents
|
||||
.try_into()
|
||||
.expect("Expect NonReplacedContents, but got ReplacedContents!"),
|
||||
propagated_data,
|
||||
is_list_item,
|
||||
);
|
||||
// Currently, the incremental layout is not fully ready. When a box is preserved
|
||||
// during incremental box tree update, its cached layout result is also preserved.
|
||||
// So, we have to invalidate all caches here to ensure that the layout is recomputed
|
||||
// correctly.
|
||||
base.invalidate_all_caches();
|
||||
base.repair_style(&info.style);
|
||||
*contains_floats = contents.contains_floats();
|
||||
},
|
||||
_ => unreachable!("Expect flow display inside"),
|
||||
},
|
||||
BlockLevelBox::Independent(independent_formatting_context) => {
|
||||
independent_formatting_context.repair(
|
||||
context,
|
||||
info,
|
||||
repaired_contents,
|
||||
display_inside,
|
||||
propagated_data,
|
||||
)
|
||||
},
|
||||
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
|
||||
positioned_box.borrow_mut().context.repair(
|
||||
context,
|
||||
info,
|
||||
repaired_contents,
|
||||
display_inside,
|
||||
PropagatedBoxTreeData::default(),
|
||||
)
|
||||
},
|
||||
BlockLevelBox::OutOfFlowFloatBox(float_box) => float_box.contents.repair(
|
||||
context,
|
||||
info,
|
||||
repaired_contents,
|
||||
display_inside,
|
||||
propagated_data,
|
||||
),
|
||||
BlockLevelBox::OutsideMarker(marker) => {
|
||||
marker.block_container.repair(
|
||||
context,
|
||||
info,
|
||||
repaired_contents
|
||||
.try_into()
|
||||
.expect("Expect NonReplacedContents, but got ReplacedContents!"),
|
||||
propagated_data,
|
||||
false,
|
||||
);
|
||||
marker.repair_style(context.shared_context(), &info.node, &info.style);
|
||||
// Currently, the incremental layout is not fully ready. When a box is preserved
|
||||
// during incremental box tree update, its cached layout result is also preserved.
|
||||
// So, we have to invalidate all caches here to ensure that the layout is recomputed
|
||||
// correctly.
|
||||
marker.invalidate_all_caches();
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockLevelRepairJob<'_> {
|
||||
fn finish(self, context: &LayoutContext) -> ArcRefCell<BlockLevelBox> {
|
||||
self.repaired_box.borrow_mut().repair(
|
||||
context,
|
||||
&self.info,
|
||||
self.contents,
|
||||
self.display_inside,
|
||||
self.propagated_data,
|
||||
);
|
||||
self.repaired_box
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockLevelCreateJob<'_> {
|
||||
fn finish(self, context: &LayoutContext) -> ArcRefCell<BlockLevelBox> {
|
||||
let info = &self.info;
|
||||
let block_level_box = match self.kind {
|
||||
|
@ -758,6 +1141,19 @@ impl BlockLevelJob<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl BlockLevelJob<'_> {
|
||||
fn finish(self, context: &LayoutContext) -> ArcRefCell<BlockLevelBox> {
|
||||
match self {
|
||||
BlockLevelJob::Copy(copied_box) => {
|
||||
copied_box.borrow().invalidate_subtree_caches();
|
||||
copied_box
|
||||
},
|
||||
BlockLevelJob::Repair(repair_job) => repair_job.finish(context),
|
||||
BlockLevelJob::Create(create_job) => create_job.finish(context),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntermediateBlockContainer {
|
||||
fn finish(self, context: &LayoutContext, info: &NodeAndStyleInfo<'_>) -> BlockContainer {
|
||||
match self {
|
||||
|
|
|
@ -278,6 +278,26 @@ impl InlineItem {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_subtree_caches(&self) {
|
||||
match self {
|
||||
InlineItem::StartInlineBox(inline_box) => {
|
||||
inline_box.borrow().base.invalidate_all_caches()
|
||||
},
|
||||
InlineItem::EndInlineBox | InlineItem::TextRun(..) => {},
|
||||
InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
|
||||
positioned_box.borrow().context.invalidate_subtree_caches();
|
||||
},
|
||||
InlineItem::OutOfFlowFloatBox(float_box) => {
|
||||
float_box.borrow().contents.invalidate_subtree_caches()
|
||||
},
|
||||
InlineItem::Atomic(independent_formatting_context, ..) => {
|
||||
independent_formatting_context
|
||||
.borrow()
|
||||
.invalidate_subtree_caches();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fragments(&self) -> Vec<Fragment> {
|
||||
match self {
|
||||
InlineItem::StartInlineBox(inline_box) => inline_box.borrow().base.fragments(),
|
||||
|
@ -1713,6 +1733,12 @@ impl InlineFormattingContext {
|
|||
*self.shared_inline_styles.selected.borrow_mut() = node.to_threadsafe().selected_style();
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_subtree_caches(&self) {
|
||||
for inline_item in &self.inline_items {
|
||||
inline_item.borrow().invalidate_subtree_caches();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn layout(
|
||||
&self,
|
||||
layout_context: &LayoutContext,
|
||||
|
|
|
@ -87,6 +87,19 @@ impl BlockContainer {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_subtree_caches(&self) {
|
||||
match self {
|
||||
BlockContainer::BlockLevelBoxes(block_level_boxes) => {
|
||||
for block_level_box in block_level_boxes {
|
||||
block_level_box.borrow().invalidate_subtree_caches();
|
||||
}
|
||||
},
|
||||
BlockContainer::InlineFormattingContext(inline_formating_context) => {
|
||||
inline_formating_context.invalidate_subtree_caches();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, MallocSizeOf)]
|
||||
|
@ -138,6 +151,25 @@ impl BlockLevelBox {
|
|||
self.with_base(LayoutBoxBase::invalidate_cached_fragment);
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_subtree_caches(&self) {
|
||||
match self {
|
||||
BlockLevelBox::Independent(independent_formatting_context) => {
|
||||
independent_formatting_context.invalidate_subtree_caches()
|
||||
},
|
||||
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
|
||||
positioned_box.borrow().context.invalidate_subtree_caches()
|
||||
},
|
||||
BlockLevelBox::OutOfFlowFloatBox(float_box) => {
|
||||
float_box.contents.invalidate_subtree_caches()
|
||||
},
|
||||
BlockLevelBox::OutsideMarker(outside_marker) => outside_marker.invalidate_all_caches(),
|
||||
BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
|
||||
base.invalidate_all_caches();
|
||||
contents.invalidate_subtree_caches();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fragments(&self) -> Vec<Fragment> {
|
||||
self.with_base(LayoutBoxBase::fragments)
|
||||
}
|
||||
|
@ -427,6 +459,10 @@ impl OutsideMarker {
|
|||
self.list_item_style = node.style(context);
|
||||
self.base.repair_style(new_style);
|
||||
}
|
||||
|
||||
fn invalidate_all_caches(&self) {
|
||||
self.base.invalidate_all_caches();
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockFormattingContext {
|
||||
|
|
|
@ -12,10 +12,10 @@ use script::layout_dom::ServoLayoutNode;
|
|||
use script_layout_interface::wrapper_traits::{
|
||||
LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
|
||||
};
|
||||
use script_layout_interface::{LayoutElementType, LayoutNodeType};
|
||||
use servo_arc::Arc;
|
||||
use style::dom::{NodeInfo, TNode};
|
||||
use style::properties::ComputedValues;
|
||||
use style::selector_parser::RestyleDamage;
|
||||
use style::values::computed::Overflow;
|
||||
use style_traits::CSSPixel;
|
||||
|
||||
|
@ -32,8 +32,8 @@ use crate::fragment_tree::FragmentTree;
|
|||
use crate::geom::{LogicalVec2, PhysicalSize};
|
||||
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
|
||||
use crate::replaced::ReplacedContents;
|
||||
use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside};
|
||||
use crate::taffy::{TaffyItemBox, TaffyItemBoxInner};
|
||||
use crate::style_ext::{Display, DisplayInside};
|
||||
use crate::taffy::TaffyItemBoxInner;
|
||||
use crate::{DefiniteContainingBlock, PropagatedBoxTreeData};
|
||||
|
||||
#[derive(MallocSizeOf)]
|
||||
|
@ -109,185 +109,29 @@ impl BoxTree {
|
|||
/// arbitrary node that is not necessarily the document's root element.
|
||||
///
|
||||
/// If the node is not a valid candidate for incremental update, the method
|
||||
/// loops over its parent. The only valid candidates for now are absolutely
|
||||
/// positioned boxes which don't change their outside display mode (i.e. it
|
||||
/// will not attempt to update from an absolutely positioned inline element
|
||||
/// which became an absolutely positioned block element). The value `true`
|
||||
/// is returned if an incremental update could be done, and `false`
|
||||
/// otherwise.
|
||||
/// loops over its parent. Currently, the only valid candidates for now
|
||||
/// are absolutely positioned boxes and their originating nodes must have
|
||||
/// restyle damage that is `REPAIR_BOX` rather than `REBUILD_BOX`. In the
|
||||
/// future, this may be extended to other types of boxes if the new restyle
|
||||
/// damage types for incremental layout is ready.
|
||||
///
|
||||
/// There are various pain points that need to be taken care of to extend
|
||||
/// the set of valid candidates:
|
||||
/// * it is not obvious how to incrementally check whether a block
|
||||
/// formatting context still contains floats or not;
|
||||
/// * the propagation of text decorations towards node descendants is
|
||||
/// hard to do incrementally with our current representation of boxes
|
||||
/// * how intrinsic content sizes are computed eagerly makes it hard
|
||||
/// to update those sizes for ancestors of the node from which we
|
||||
/// made an incremental update.
|
||||
pub fn update(context: &LayoutContext, mut dirty_node: ServoLayoutNode<'_>) -> bool {
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
enum UpdatePoint {
|
||||
AbsolutelyPositionedBlockLevelBox(ArcRefCell<BlockLevelBox>),
|
||||
AbsolutelyPositionedInlineLevelBox(ArcRefCell<InlineItem>, usize),
|
||||
AbsolutelyPositionedFlexLevelBox(ArcRefCell<FlexLevelBox>),
|
||||
AbsolutelyPositionedTaffyLevelBox(ArcRefCell<TaffyItemBox>),
|
||||
}
|
||||
|
||||
fn update_point(
|
||||
node: ServoLayoutNode<'_>,
|
||||
) -> Option<(Arc<ComputedValues>, DisplayInside, UpdatePoint)> {
|
||||
if !node.is_element() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if node.type_id() == LayoutNodeType::Element(LayoutElementType::HTMLBodyElement) {
|
||||
// This can require changes to the canvas background.
|
||||
return None;
|
||||
}
|
||||
|
||||
// Don't update unstyled nodes or nodes that have pseudo-elements.
|
||||
let element_data = node.style_data()?.element_data.borrow();
|
||||
if !element_data.styles.pseudos.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let layout_data = NodeExt::layout_data(&node)?;
|
||||
if layout_data.pseudo_before_box.borrow().is_some() {
|
||||
return None;
|
||||
}
|
||||
if layout_data.pseudo_after_box.borrow().is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let primary_style = element_data.styles.primary();
|
||||
let box_style = primary_style.get_box();
|
||||
|
||||
if !box_style.position.is_absolutely_positioned() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let display_inside = match Display::from(box_style.display) {
|
||||
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => {
|
||||
inside
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let update_point =
|
||||
match &*AtomicRef::filter_map(layout_data.self_box.borrow(), Option::as_ref)? {
|
||||
LayoutBox::DisplayContents(..) => return None,
|
||||
LayoutBox::BlockLevel(block_level_box) => match &*block_level_box.borrow() {
|
||||
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_)
|
||||
if box_style.position.is_absolutely_positioned() =>
|
||||
{
|
||||
UpdatePoint::AbsolutelyPositionedBlockLevelBox(block_level_box.clone())
|
||||
},
|
||||
_ => return None,
|
||||
},
|
||||
LayoutBox::InlineLevel(inline_level_items) => {
|
||||
let inline_level_box = inline_level_items.first()?;
|
||||
let InlineItem::OutOfFlowAbsolutelyPositionedBox(_, text_offset_index) =
|
||||
&*inline_level_box.borrow()
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
UpdatePoint::AbsolutelyPositionedInlineLevelBox(
|
||||
inline_level_box.clone(),
|
||||
*text_offset_index,
|
||||
)
|
||||
},
|
||||
LayoutBox::FlexLevel(flex_level_box) => match &*flex_level_box.borrow() {
|
||||
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_)
|
||||
if box_style.position.is_absolutely_positioned() =>
|
||||
{
|
||||
UpdatePoint::AbsolutelyPositionedFlexLevelBox(flex_level_box.clone())
|
||||
},
|
||||
_ => return None,
|
||||
},
|
||||
LayoutBox::TableLevelBox(..) => return None,
|
||||
LayoutBox::TaffyItemBox(taffy_level_box) => match &taffy_level_box
|
||||
.borrow()
|
||||
.taffy_level_box
|
||||
{
|
||||
TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(_)
|
||||
if box_style.position.is_absolutely_positioned() =>
|
||||
{
|
||||
UpdatePoint::AbsolutelyPositionedTaffyLevelBox(taffy_level_box.clone())
|
||||
},
|
||||
_ => return None,
|
||||
},
|
||||
};
|
||||
Some((primary_style.clone(), display_inside, update_point))
|
||||
}
|
||||
|
||||
loop {
|
||||
let Some((primary_style, display_inside, update_point)) = update_point(dirty_node)
|
||||
else {
|
||||
dirty_node = match dirty_node.parent_node() {
|
||||
Some(parent) => parent,
|
||||
None => return false,
|
||||
};
|
||||
continue;
|
||||
};
|
||||
|
||||
let contents = ReplacedContents::for_element(dirty_node, context)
|
||||
.map_or_else(|| NonReplacedContents::OfElement.into(), Contents::Replaced);
|
||||
let info = NodeAndStyleInfo::new(dirty_node, Arc::clone(&primary_style));
|
||||
let out_of_flow_absolutely_positioned_box = ArcRefCell::new(
|
||||
AbsolutelyPositionedBox::construct(context, &info, display_inside, contents),
|
||||
);
|
||||
match update_point {
|
||||
UpdatePoint::AbsolutelyPositionedBlockLevelBox(block_level_box) => {
|
||||
*block_level_box.borrow_mut() = BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
|
||||
out_of_flow_absolutely_positioned_box,
|
||||
);
|
||||
},
|
||||
UpdatePoint::AbsolutelyPositionedInlineLevelBox(
|
||||
inline_level_box,
|
||||
text_offset_index,
|
||||
) => {
|
||||
*inline_level_box.borrow_mut() = InlineItem::OutOfFlowAbsolutelyPositionedBox(
|
||||
out_of_flow_absolutely_positioned_box,
|
||||
text_offset_index,
|
||||
);
|
||||
},
|
||||
UpdatePoint::AbsolutelyPositionedFlexLevelBox(flex_level_box) => {
|
||||
*flex_level_box.borrow_mut() = FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(
|
||||
out_of_flow_absolutely_positioned_box,
|
||||
);
|
||||
},
|
||||
UpdatePoint::AbsolutelyPositionedTaffyLevelBox(taffy_level_box) => {
|
||||
taffy_level_box.borrow_mut().taffy_level_box =
|
||||
TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(
|
||||
out_of_flow_absolutely_positioned_box,
|
||||
);
|
||||
},
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// We are going to rebuild the box tree from the update point downward, but this update
|
||||
// point is an absolute, which means that it needs to be laid out again in the containing
|
||||
// block for absolutes, which is established by one of its ancestors. In addition,
|
||||
// absolutes, when laid out, can produce more absolutes (either fixed or absolutely
|
||||
// positioned) elements, so there may be yet more layout that has to happen in this
|
||||
// ancestor.
|
||||
//
|
||||
// We do not know which ancestor is the one that established the containing block for this
|
||||
// update point, so just invalidate the fragment cache of all ancestors, meaning that even
|
||||
// though the box tree is preserved, the fragment tree from the root to the update point and
|
||||
// all of its descendants will need to be rebuilt. This isn't as bad as it seems, because
|
||||
// siblings and siblings of ancestors of this path through the tree will still have cached
|
||||
// fragments.
|
||||
//
|
||||
// TODO: Do better. This is still a very crude way to do incremental layout.
|
||||
while let Some(parent_node) = dirty_node.parent_node() {
|
||||
parent_node.invalidate_cached_fragment();
|
||||
dirty_node = parent_node;
|
||||
}
|
||||
|
||||
true
|
||||
/// There are various reasons why only absolutely positioned boxes can be
|
||||
/// selected as the update point:
|
||||
/// * it needs a bit of trick to incrementally update the `contains_floats`
|
||||
/// for a block formatting context and `SameFormattingContext`, which make
|
||||
/// the incremental update less eligible.
|
||||
/// * the propagation of box tree data towards node descendants is hard to do
|
||||
/// incrementally with our current representation of boxes. To support
|
||||
/// incremental update, we have to store it at the `LayoutBoxBase`.
|
||||
/// * We have to clear all layout caches for ancestors of the update point
|
||||
/// when the incremental layout is not fully ready. However, it is really hard
|
||||
/// to do that incrementally because a box does not hold a reference to its
|
||||
/// parent box with current representation of boxes. The anonymous ancestor
|
||||
/// boxes can not be visited. Thus, select the absolutely positioned boxes
|
||||
/// as the update point, this is not a problem.
|
||||
pub fn update(context: &LayoutContext, dirty_node: ServoLayoutNode<'_>) -> bool {
|
||||
let mut helper = BoxTreeUpdateHelper::new(context, dirty_node);
|
||||
helper.try_update_box_tree()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,6 +139,8 @@ fn construct_for_root_element(
|
|||
context: &LayoutContext,
|
||||
root_element: ServoLayoutNode<'_>,
|
||||
) -> Vec<ArcRefCell<BlockLevelBox>> {
|
||||
root_element.clear_restyle_damage();
|
||||
|
||||
let info = NodeAndStyleInfo::new(root_element, root_element.style(context.shared_context()));
|
||||
let box_style = info.style.get_box();
|
||||
|
||||
|
@ -400,3 +246,201 @@ impl BoxTree {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct BoxTreeUpdateHelper<'style, 'dom> {
|
||||
context: &'style LayoutContext<'style>,
|
||||
dirty_node: ServoLayoutNode<'dom>,
|
||||
primary_style: Option<Arc<ComputedValues>>,
|
||||
display_inside: Option<DisplayInside>,
|
||||
}
|
||||
|
||||
enum UpdatePoint {
|
||||
DoNotNeedUpdateTree,
|
||||
RootElementPrimaryBox(ArcRefCell<BlockLevelBox>),
|
||||
AbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
|
||||
}
|
||||
|
||||
impl<'style, 'dom> BoxTreeUpdateHelper<'style, 'dom> {
|
||||
fn new(context: &'style LayoutContext<'style>, dirty_node: ServoLayoutNode<'dom>) -> Self {
|
||||
Self {
|
||||
context,
|
||||
dirty_node,
|
||||
primary_style: None,
|
||||
display_inside: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_update_box_tree(&mut self) -> bool {
|
||||
loop {
|
||||
let Some(update_point) = self.update_point() else {
|
||||
self.dirty_node = match self.dirty_node.parent_node() {
|
||||
Some(parent) => parent,
|
||||
None => return false,
|
||||
};
|
||||
continue;
|
||||
};
|
||||
|
||||
match update_point {
|
||||
UpdatePoint::DoNotNeedUpdateTree => {},
|
||||
_ => {
|
||||
self.update_box_tree(update_point);
|
||||
self.invalidate_layout_cache();
|
||||
self.clear_ancestor_restyle_damage();
|
||||
},
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn update_point(&mut self) -> Option<UpdatePoint> {
|
||||
if !self.dirty_node.is_element() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let element_data = self.dirty_node.style_data()?.element_data.borrow();
|
||||
|
||||
if !element_data.damage.contains(RestyleDamage::REPAIR_BOX) {
|
||||
return Some(UpdatePoint::DoNotNeedUpdateTree);
|
||||
}
|
||||
|
||||
if element_data.damage.contains(RestyleDamage::REBUILD_BOX) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let primary_style = element_data.styles.primary();
|
||||
let box_style = primary_style.get_box();
|
||||
|
||||
let display_inside = match Display::from(box_style.display) {
|
||||
Display::GeneratingBox(generating_box) => generating_box.display_inside(),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let layout_data = NodeExt::layout_data(&self.dirty_node)?;
|
||||
let layout_box = &*AtomicRef::filter_map(layout_data.self_box.borrow(), Option::as_ref)?;
|
||||
if self.dirty_node.to_threadsafe().as_element()?.is_root() {
|
||||
match layout_box {
|
||||
LayoutBox::BlockLevel(block_level_box) => {
|
||||
self.primary_style = Some(primary_style.clone());
|
||||
self.display_inside = Some(display_inside);
|
||||
return Some(UpdatePoint::RootElementPrimaryBox(block_level_box.clone()));
|
||||
},
|
||||
// Unreachable because the style crate adjusts the computed values:
|
||||
// https://drafts.csswg.org/css-display-3/#transformations
|
||||
// “'display' of 'contents' computes to 'block' on the root element”
|
||||
_ => unreachable!("Root element should only has display: block"),
|
||||
}
|
||||
}
|
||||
|
||||
if !box_style.position.is_absolutely_positioned() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let update_point = match layout_box {
|
||||
LayoutBox::DisplayContents(..) => return None,
|
||||
LayoutBox::BlockLevel(block_level_box) => match &*block_level_box.borrow() {
|
||||
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
|
||||
UpdatePoint::AbsolutelyPositionedBox(positioned_box.clone())
|
||||
},
|
||||
_ => return None,
|
||||
},
|
||||
LayoutBox::InlineLevel(inline_level_items) => {
|
||||
let inline_level_item = inline_level_items.first()?;
|
||||
match &*inline_level_item.borrow() {
|
||||
InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
|
||||
UpdatePoint::AbsolutelyPositionedBox(positioned_box.clone())
|
||||
},
|
||||
_ => return None,
|
||||
}
|
||||
},
|
||||
LayoutBox::FlexLevel(flex_level_box) => match &*flex_level_box.borrow() {
|
||||
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
|
||||
UpdatePoint::AbsolutelyPositionedBox(positioned_box.clone())
|
||||
},
|
||||
_ => return None,
|
||||
},
|
||||
LayoutBox::TableLevelBox(..) => return None,
|
||||
LayoutBox::TaffyItemBox(taffy_item_box) => {
|
||||
match &taffy_item_box.borrow().taffy_level_box {
|
||||
TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
|
||||
UpdatePoint::AbsolutelyPositionedBox(positioned_box.clone())
|
||||
},
|
||||
_ => return None,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
self.primary_style = Some(primary_style.clone());
|
||||
self.display_inside = Some(display_inside);
|
||||
|
||||
Some(update_point)
|
||||
}
|
||||
|
||||
/// We are going to repair the box tree from the update point downward, but this update
|
||||
/// point is an absolute, which means that it needs to be laid out again in the containing
|
||||
/// block for absolutes, which is established by on if its ancestors. In addition,
|
||||
/// absolutes, when laid out, can produce more absolutes (either fixed or absolutely
|
||||
/// positioned) elements, so there may be yet more layout that has to happen in this
|
||||
/// ancestor.
|
||||
///
|
||||
/// We do not know which ancestor is the one that established the containing block for this
|
||||
/// update point, so just invalidate the fragment cache of all ancestors, meaning that even
|
||||
/// though the box tree is preserved, the fragment tree from the root to the update point and
|
||||
/// all of its descendants will need to be rebuilt. This isn't as bad as it seems, because
|
||||
/// slibings and slibings of ancestors of this path through the tree will still have cached
|
||||
/// fragments.
|
||||
///
|
||||
/// TODO: Do better. This is still a very crude way to do incremental layout.
|
||||
fn invalidate_layout_cache(&self) {
|
||||
let mut invalidation_start_point = self.dirty_node;
|
||||
while let Some(parent_node) = invalidation_start_point.parent_node() {
|
||||
parent_node.invalidate_cached_fragment();
|
||||
invalidation_start_point = parent_node;
|
||||
}
|
||||
}
|
||||
|
||||
/// We have already propagate up some restyle damage from descendants to
|
||||
/// the ancestors of update point, but we are just going to traverse the
|
||||
/// subtree from the update point rather than the root element during
|
||||
/// incremental update. Thus, clear all ancestor's restyle damage now.
|
||||
fn clear_ancestor_restyle_damage(&self) {
|
||||
let mut inclusive_ancestor = Some(self.dirty_node);
|
||||
while let Some(node) = inclusive_ancestor {
|
||||
node.clear_restyle_damage();
|
||||
inclusive_ancestor = node.parent_node();
|
||||
}
|
||||
}
|
||||
|
||||
fn update_box_tree(&self, update_point: UpdatePoint) {
|
||||
let context = self.context;
|
||||
let display_inside = self.display_inside.unwrap();
|
||||
|
||||
let info = NodeAndStyleInfo::new(self.dirty_node, self.primary_style.clone().unwrap());
|
||||
let contents = ReplacedContents::for_element(self.dirty_node, context)
|
||||
.map_or_else(|| NonReplacedContents::OfElement.into(), Contents::Replaced);
|
||||
|
||||
match update_point {
|
||||
UpdatePoint::DoNotNeedUpdateTree => unreachable!("Should have been filtered out"),
|
||||
UpdatePoint::RootElementPrimaryBox(block_level_box) => {
|
||||
block_level_box.borrow_mut().repair(
|
||||
context,
|
||||
&info,
|
||||
contents,
|
||||
display_inside,
|
||||
PropagatedBoxTreeData::default(),
|
||||
);
|
||||
},
|
||||
UpdatePoint::AbsolutelyPositionedBox(positioned_box) => {
|
||||
positioned_box.borrow_mut().context.repair(
|
||||
context,
|
||||
&info,
|
||||
contents,
|
||||
display_inside,
|
||||
PropagatedBoxTreeData::default(),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* 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 std::mem;
|
||||
|
||||
use app_units::Au;
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use script::layout_dom::{ServoLayoutElement, ServoLayoutNode};
|
||||
|
@ -11,7 +13,7 @@ use style::properties::ComputedValues;
|
|||
use style::selector_parser::PseudoElement;
|
||||
|
||||
use crate::context::LayoutContext;
|
||||
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
|
||||
use crate::dom_traversal::{Contents, NodeAndStyleInfo, NonReplacedContents};
|
||||
use crate::flexbox::FlexContainer;
|
||||
use crate::flow::BlockFormattingContext;
|
||||
use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags};
|
||||
|
@ -143,6 +145,39 @@ impl IndependentFormattingContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn repair(
|
||||
&mut self,
|
||||
context: &LayoutContext,
|
||||
node_and_style_info: &NodeAndStyleInfo,
|
||||
contents: Contents,
|
||||
display_inside: DisplayInside,
|
||||
propagated_data: PropagatedBoxTreeData,
|
||||
) {
|
||||
match &mut self.contents {
|
||||
IndependentFormattingContextContents::NonReplaced(
|
||||
independent_non_replaced_contents,
|
||||
) => {
|
||||
independent_non_replaced_contents.repair(
|
||||
context,
|
||||
node_and_style_info,
|
||||
contents
|
||||
.try_into()
|
||||
.expect("Expect NonReplacedContents, but got ReplacedContents!"),
|
||||
display_inside,
|
||||
propagated_data,
|
||||
);
|
||||
},
|
||||
IndependentFormattingContextContents::Replaced(..) => {},
|
||||
}
|
||||
|
||||
// Currently, the incremental layout is not fully ready. When a box is preserved
|
||||
// during incremental box tree update, its cached layout result is also preserved.
|
||||
// So, we have to invalidate all caches here to ensure that the layout is recomputed
|
||||
// correctly.
|
||||
self.base.invalidate_all_caches();
|
||||
self.base.repair_style(&node_and_style_info.style);
|
||||
}
|
||||
|
||||
pub fn is_replaced(&self) -> bool {
|
||||
matches!(
|
||||
self.contents,
|
||||
|
@ -234,6 +269,16 @@ impl IndependentFormattingContext {
|
|||
IndependentFormattingContextContents::Replaced(..) => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_subtree_caches(&self) {
|
||||
self.base.invalidate_all_caches();
|
||||
match &self.contents {
|
||||
IndependentFormattingContextContents::NonReplaced(content) => {
|
||||
content.invalidate_subtree_caches();
|
||||
},
|
||||
IndependentFormattingContextContents::Replaced(..) => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndependentNonReplacedContents {
|
||||
|
@ -376,6 +421,86 @@ impl IndependentNonReplacedContents {
|
|||
IndependentNonReplacedContents::Table(table) => table.repair_style(context, new_style),
|
||||
}
|
||||
}
|
||||
|
||||
fn repair(
|
||||
&mut self,
|
||||
context: &LayoutContext,
|
||||
node_and_style_info: &NodeAndStyleInfo,
|
||||
non_replaced_contents: NonReplacedContents,
|
||||
display_inside: DisplayInside,
|
||||
propagated_data: PropagatedBoxTreeData,
|
||||
) {
|
||||
match self {
|
||||
IndependentNonReplacedContents::Flow(block_formatting_context) => {
|
||||
match display_inside {
|
||||
DisplayInside::Flow { is_list_item } |
|
||||
DisplayInside::FlowRoot { is_list_item } => {
|
||||
block_formatting_context.repair(
|
||||
context,
|
||||
node_and_style_info,
|
||||
non_replaced_contents,
|
||||
propagated_data,
|
||||
is_list_item,
|
||||
);
|
||||
},
|
||||
_ => unreachable!("Expect flow display inside mode"),
|
||||
}
|
||||
},
|
||||
IndependentNonReplacedContents::Flex(old_flex_container) => {
|
||||
let new_flex_container = FlexContainer::construct(
|
||||
context,
|
||||
node_and_style_info,
|
||||
non_replaced_contents,
|
||||
propagated_data,
|
||||
);
|
||||
let _ = mem::replace(old_flex_container, new_flex_container);
|
||||
},
|
||||
IndependentNonReplacedContents::Grid(old_taffy_container) => {
|
||||
let new_taffy_container = TaffyContainer::construct(
|
||||
context,
|
||||
node_and_style_info,
|
||||
non_replaced_contents,
|
||||
propagated_data,
|
||||
);
|
||||
let _ = mem::replace(old_taffy_container, new_taffy_container);
|
||||
},
|
||||
IndependentNonReplacedContents::Table(old_table) => {
|
||||
let table_grid_style = context
|
||||
.shared_context()
|
||||
.stylist
|
||||
.style_for_anonymous::<ServoLayoutElement>(
|
||||
&context.shared_context().guards,
|
||||
&PseudoElement::ServoTableGrid,
|
||||
&node_and_style_info.style,
|
||||
);
|
||||
let new_table = Table::construct(
|
||||
context,
|
||||
node_and_style_info,
|
||||
table_grid_style,
|
||||
non_replaced_contents,
|
||||
propagated_data,
|
||||
);
|
||||
let _ = mem::replace(old_table, new_table);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn invalidate_subtree_caches(&self) {
|
||||
match self {
|
||||
IndependentNonReplacedContents::Flow(block_formatting_context) => {
|
||||
block_formatting_context
|
||||
.contents
|
||||
.invalidate_subtree_caches();
|
||||
},
|
||||
IndependentNonReplacedContents::Flex(flex_container) => {
|
||||
flex_container.invalidate_subtree_caches()
|
||||
},
|
||||
IndependentNonReplacedContents::Grid(taffy_container) => {
|
||||
taffy_container.invalidate_subtree_caches()
|
||||
},
|
||||
IndependentNonReplacedContents::Table(table) => table.invalidate_subtree_caches(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputeInlineContentSizes for IndependentNonReplacedContents {
|
||||
|
|
|
@ -74,6 +74,12 @@ impl LayoutBoxBase {
|
|||
let _ = self.cached_layout_result.borrow_mut().take();
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_all_caches(&self) {
|
||||
let _ = self.cached_inline_content_size.borrow_mut().take();
|
||||
self.invalidate_cached_fragment();
|
||||
self.clear_fragments();
|
||||
}
|
||||
|
||||
pub(crate) fn fragments(&self) -> Vec<Fragment> {
|
||||
self.fragments.borrow().clone()
|
||||
}
|
||||
|
|
|
@ -778,7 +778,7 @@ impl LayoutThread {
|
|||
compute_damage_and_repair_style(layout_context.shared_context(), root_node);
|
||||
if viewport_changed {
|
||||
damage = RestyleDamage::REBUILD_BOX;
|
||||
} else if !damage.contains(RestyleDamage::REBUILD_BOX) {
|
||||
} else if !damage.will_change_box_subtree() {
|
||||
layout_context.style_context.stylist.rule_tree().maybe_gc();
|
||||
return damage;
|
||||
}
|
||||
|
|
|
@ -207,6 +207,35 @@ impl Table {
|
|||
new_style,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_subtree_caches(&self) {
|
||||
for caption in &self.captions {
|
||||
caption.borrow().context.invalidate_subtree_caches();
|
||||
}
|
||||
for column_group in &self.column_groups {
|
||||
column_group.borrow().base.invalidate_all_caches();
|
||||
}
|
||||
for row_group in &self.row_groups {
|
||||
row_group.borrow().base.invalidate_all_caches();
|
||||
}
|
||||
for column in &self.columns {
|
||||
column.borrow().base.invalidate_all_caches();
|
||||
}
|
||||
for row in &self.rows {
|
||||
row.borrow().base.invalidate_all_caches();
|
||||
}
|
||||
for slot_cell_row in &self.slots {
|
||||
for slot in slot_cell_row {
|
||||
match slot {
|
||||
TableSlot::Cell(cell) => {
|
||||
cell.borrow().base.invalidate_all_caches();
|
||||
cell.borrow().contents.contents.invalidate_subtree_caches();
|
||||
},
|
||||
TableSlot::Spanned(..) | TableSlot::Empty => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type TableSlotCoordinates = Point2D<usize, UnknownUnit>;
|
||||
|
|
|
@ -73,6 +73,12 @@ impl TaffyContainer {
|
|||
pub(crate) fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||
self.style = new_style.clone();
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_subtree_caches(&self) {
|
||||
for taffy_level_box in &self.children {
|
||||
taffy_level_box.borrow_mut().invalidate_subtree_caches();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MallocSizeOf)]
|
||||
|
@ -138,6 +144,21 @@ impl TaffyItemBox {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_subtree_caches(&mut self) {
|
||||
self.taffy_layout = Default::default();
|
||||
self.positioning_context = PositioningContext::default();
|
||||
self.child_fragments.clear();
|
||||
|
||||
match self.taffy_level_box {
|
||||
TaffyItemBoxInner::InFlowBox(ref independent_formatting_context) => {
|
||||
independent_formatting_context.invalidate_subtree_caches();
|
||||
},
|
||||
TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(ref positioned_box) => {
|
||||
positioned_box.borrow().context.invalidate_subtree_caches();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fragments(&self) -> Vec<Fragment> {
|
||||
match self.taffy_level_box {
|
||||
TaffyItemBoxInner::InFlowBox(ref independent_formatting_context) => {
|
||||
|
|
|
@ -99,6 +99,17 @@ pub(crate) fn compute_damage_and_repair_style(
|
|||
compute_damage_and_repair_style_inner(context, node, RestyleDamage::empty())
|
||||
}
|
||||
|
||||
fn need_repair_style_before_box_tree_update(damage: RestyleDamage) -> bool {
|
||||
// Repair the style at the node's layout objects only when itself has
|
||||
// restyle damage but it's boxes will be kept unchanged, otherwise,
|
||||
// the style will be repaired during box tree update.
|
||||
if damage.is_empty() || damage.will_change_box_subtree() {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn compute_damage_and_repair_style_inner(
|
||||
context: &SharedStyleContext,
|
||||
node: ServoLayoutNode<'_>,
|
||||
|
@ -106,7 +117,6 @@ pub(crate) fn compute_damage_and_repair_style_inner(
|
|||
) -> RestyleDamage {
|
||||
let original_damage;
|
||||
let damage;
|
||||
|
||||
{
|
||||
let mut element_data = node
|
||||
.style_data()
|
||||
|
@ -114,28 +124,42 @@ pub(crate) fn compute_damage_and_repair_style_inner(
|
|||
.element_data
|
||||
.borrow_mut();
|
||||
|
||||
original_damage = std::mem::take(&mut element_data.damage);
|
||||
original_damage = element_data.damage;
|
||||
// The damage that can cause the box subtree to change will be cleaned
|
||||
// after incremental box tree update.
|
||||
if !element_data.damage.will_change_box_subtree() {
|
||||
let _ = std::mem::take(&mut element_data.damage);
|
||||
};
|
||||
|
||||
damage = original_damage | parent_restyle_damage;
|
||||
|
||||
if let Some(ref style) = element_data.styles.primary {
|
||||
if style.get_box().display == Display::None {
|
||||
return damage;
|
||||
return damage.propagate_up_damage();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut propagated_damage = damage.propagate_up_damage();
|
||||
let propagated_down_damage = damage.propagate_down_damage();
|
||||
|
||||
let mut propagated_damage = damage;
|
||||
for child in iter_child_nodes(node) {
|
||||
if child.is_element() {
|
||||
propagated_damage |= compute_damage_and_repair_style_inner(context, child, damage);
|
||||
propagated_damage |=
|
||||
compute_damage_and_repair_style_inner(context, child, propagated_down_damage);
|
||||
}
|
||||
}
|
||||
|
||||
if !propagated_damage.contains(RestyleDamage::REBUILD_BOX) &&
|
||||
!original_damage.contains(RestyleDamage::REBUILD_BOX)
|
||||
if need_repair_style_before_box_tree_update(propagated_damage) &&
|
||||
need_repair_style_before_box_tree_update(original_damage)
|
||||
{
|
||||
node.repair_style(context);
|
||||
}
|
||||
|
||||
if propagated_damage.contains(RestyleDamage::REPAIR_BOX) {
|
||||
let mut element_data = node.style_data().unwrap().element_data.borrow_mut();
|
||||
element_data.damage |= RestyleDamage::REPAIR_BOX;
|
||||
}
|
||||
|
||||
propagated_damage
|
||||
}
|
||||
|
|
|
@ -940,7 +940,7 @@ impl Document {
|
|||
|
||||
// FIXME(emilio): This is very inefficient, ideally the flag above would
|
||||
// be enough and incremental layout could figure out from there.
|
||||
node.dirty(NodeDamage::OtherNodeDamage);
|
||||
node.dirty(NodeDamage::NodeContentOrHeritageDamaged);
|
||||
}
|
||||
|
||||
/// Remove any existing association between the provided id and any elements in this document.
|
||||
|
|
|
@ -360,8 +360,9 @@ impl Element {
|
|||
// NodeStyleDamaged, but I'm preserving existing behavior.
|
||||
restyle.hint.insert(RestyleHint::RESTYLE_SELF);
|
||||
|
||||
if damage == NodeDamage::OtherNodeDamage {
|
||||
doc.note_node_with_dirty_descendants(self.upcast());
|
||||
if damage == NodeDamage::NodeContentOrHeritageDamaged {
|
||||
restyle.damage = RestyleDamage::repair();
|
||||
} else if damage == NodeDamage::OtherNodeDamage {
|
||||
restyle.damage = RestyleDamage::reconstruct();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3933,9 +3933,17 @@ impl VirtualMethods for Node {
|
|||
|
||||
/// A summary of the changes that happened to a node.
|
||||
#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
|
||||
// FIXME(joedow): The enum-variant-name-threshold's default is 3, so add the
|
||||
// `NodeContentOrHeritageDamaged` variant leads to the lint being triggered, reference:
|
||||
// https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
|
||||
//
|
||||
// Allow this lint temperarily, and its fix will be done in a follow-up PR.
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub(crate) enum NodeDamage {
|
||||
/// The node's `style` attribute changed.
|
||||
NodeStyleDamaged,
|
||||
/// The node's content or heritage changed: children removed or added, text content changed.
|
||||
NodeContentOrHeritageDamaged,
|
||||
/// Other parts of a node changed; attributes, text content, etc.
|
||||
OtherNodeDamage,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue