mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
layout: Add a first pass at incremental box tree construction (#37751)
This change: - Adds a new type of LayoutDamage that signifies that a box needs its children recollected, because one or more of them need to be rebuilt. - During restyle damage propagation, propagate this new damage upward in the tree. Then box tree construction should be able to preserve any still-valid box tree nodes from box slots. - During BlockLevelBox job finalization, if a box slot is valid and there is not LayoutDamage to the element, use the old box slot, ensuring that its fragment cache is invalidated. Testing: This should not change observable behavior and thus is covered by existing WPT tests. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Oriol Brufau <obrufau@igalia.com> Co-authored-by: coding-joedow <ibluegalaxy_taoj@163.com>
This commit is contained in:
parent
9aa06b2c17
commit
6dafeb7a59
9 changed files with 156 additions and 78 deletions
|
@ -12,7 +12,7 @@ use layout_api::wrapper_traits::{
|
||||||
LayoutDataTrait, LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
|
LayoutDataTrait, LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
|
||||||
};
|
};
|
||||||
use layout_api::{
|
use layout_api::{
|
||||||
GenericLayoutDataTrait, LayoutElementType, LayoutNodeType as ScriptLayoutNodeType,
|
GenericLayoutDataTrait, LayoutDamage, LayoutElementType, LayoutNodeType as ScriptLayoutNodeType,
|
||||||
};
|
};
|
||||||
use malloc_size_of_derive::MallocSizeOf;
|
use malloc_size_of_derive::MallocSizeOf;
|
||||||
use net_traits::image_cache::Image;
|
use net_traits::image_cache::Image;
|
||||||
|
@ -20,7 +20,7 @@ use script::layout_dom::ServoLayoutNode;
|
||||||
use servo_arc::Arc as ServoArc;
|
use servo_arc::Arc as ServoArc;
|
||||||
use style::context::SharedStyleContext;
|
use style::context::SharedStyleContext;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::selector_parser::PseudoElement;
|
use style::selector_parser::{PseudoElement, RestyleDamage};
|
||||||
|
|
||||||
use crate::cell::ArcRefCell;
|
use crate::cell::ArcRefCell;
|
||||||
use crate::flexbox::FlexLevelBox;
|
use crate::flexbox::FlexLevelBox;
|
||||||
|
@ -160,7 +160,6 @@ pub struct BoxSlot<'dom> {
|
||||||
/// A mutable reference to a `LayoutBox` stored in a DOM element.
|
/// A mutable reference to a `LayoutBox` stored in a DOM element.
|
||||||
impl BoxSlot<'_> {
|
impl BoxSlot<'_> {
|
||||||
pub(crate) fn new(slot: ArcRefCell<Option<LayoutBox>>) -> Self {
|
pub(crate) fn new(slot: ArcRefCell<Option<LayoutBox>>) -> Self {
|
||||||
*slot.borrow_mut() = None;
|
|
||||||
let slot = Some(slot);
|
let slot = Some(slot);
|
||||||
Self {
|
Self {
|
||||||
slot,
|
slot,
|
||||||
|
@ -216,6 +215,7 @@ pub(crate) trait NodeExt<'dom> {
|
||||||
fn invalidate_cached_fragment(&self);
|
fn invalidate_cached_fragment(&self);
|
||||||
|
|
||||||
fn repair_style(&self, context: &SharedStyleContext);
|
fn repair_style(&self, context: &SharedStyleContext);
|
||||||
|
fn take_restyle_damage(&self) -> LayoutDamage;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
|
impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
|
||||||
|
@ -404,4 +404,12 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_restyle_damage(&self) -> LayoutDamage {
|
||||||
|
let damage = self
|
||||||
|
.style_data()
|
||||||
|
.map(|style_data| std::mem::take(&mut style_data.element_data.borrow_mut().damage))
|
||||||
|
.unwrap_or_else(RestyleDamage::reconstruct);
|
||||||
|
LayoutDamage::from_bits_retain(damage.bits())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::iter::FusedIterator;
|
||||||
use fonts::ByteIndex;
|
use fonts::ByteIndex;
|
||||||
use html5ever::{LocalName, local_name};
|
use html5ever::{LocalName, local_name};
|
||||||
use layout_api::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
|
use layout_api::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
|
||||||
use layout_api::{LayoutElementType, LayoutNodeType};
|
use layout_api::{LayoutDamage, LayoutElementType, LayoutNodeType};
|
||||||
use range::Range;
|
use range::Range;
|
||||||
use script::layout_dom::ServoLayoutNode;
|
use script::layout_dom::ServoLayoutNode;
|
||||||
use selectors::Element as SelectorsElement;
|
use selectors::Element as SelectorsElement;
|
||||||
|
@ -34,26 +34,20 @@ pub(crate) struct NodeAndStyleInfo<'dom> {
|
||||||
pub node: ServoLayoutNode<'dom>,
|
pub node: ServoLayoutNode<'dom>,
|
||||||
pub pseudo_element_type: Option<PseudoElement>,
|
pub pseudo_element_type: Option<PseudoElement>,
|
||||||
pub style: ServoArc<ComputedValues>,
|
pub style: ServoArc<ComputedValues>,
|
||||||
|
pub damage: LayoutDamage,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'dom> NodeAndStyleInfo<'dom> {
|
impl<'dom> NodeAndStyleInfo<'dom> {
|
||||||
fn new_with_pseudo(
|
pub(crate) fn new(
|
||||||
node: ServoLayoutNode<'dom>,
|
node: ServoLayoutNode<'dom>,
|
||||||
pseudo_element_type: PseudoElement,
|
|
||||||
style: ServoArc<ComputedValues>,
|
style: ServoArc<ComputedValues>,
|
||||||
|
damage: LayoutDamage,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
|
||||||
node,
|
|
||||||
pseudo_element_type: Some(pseudo_element_type),
|
|
||||||
style,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn new(node: ServoLayoutNode<'dom>, style: ServoArc<ComputedValues>) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
node,
|
node,
|
||||||
pseudo_element_type: None,
|
pseudo_element_type: None,
|
||||||
style,
|
style,
|
||||||
|
damage,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +70,7 @@ impl<'dom> NodeAndStyleInfo<'dom> {
|
||||||
node: self.node,
|
node: self.node,
|
||||||
pseudo_element_type: Some(pseudo_element_type),
|
pseudo_element_type: Some(pseudo_element_type),
|
||||||
style,
|
style,
|
||||||
|
damage: self.damage,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,16 +188,14 @@ pub(super) trait TraversalHandler<'dom> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traverse_children_of<'dom>(
|
fn traverse_children_of<'dom>(
|
||||||
parent_element: ServoLayoutNode<'dom>,
|
parent_element_info: &NodeAndStyleInfo<'dom>,
|
||||||
context: &LayoutContext,
|
context: &LayoutContext,
|
||||||
handler: &mut impl TraversalHandler<'dom>,
|
handler: &mut impl TraversalHandler<'dom>,
|
||||||
) {
|
) {
|
||||||
traverse_eager_pseudo_element(PseudoElement::Before, parent_element, context, handler);
|
traverse_eager_pseudo_element(PseudoElement::Before, parent_element_info, context, handler);
|
||||||
|
|
||||||
if parent_element.is_text_input() {
|
if parent_element_info.node.is_text_input() {
|
||||||
let info =
|
let node_text_content = parent_element_info.node.to_threadsafe().node_text_content();
|
||||||
NodeAndStyleInfo::new(parent_element, parent_element.style(&context.style_context));
|
|
||||||
let node_text_content = parent_element.to_threadsafe().node_text_content();
|
|
||||||
if node_text_content.is_empty() {
|
if node_text_content.is_empty() {
|
||||||
// The addition of zero-width space here forces the text input to have an inline formatting
|
// The addition of zero-width space here forces the text input to have an inline formatting
|
||||||
// context that might otherwise be trimmed if there's no text. This is important to ensure
|
// context that might otherwise be trimmed if there's no text. This is important to ensure
|
||||||
|
@ -211,14 +204,18 @@ fn traverse_children_of<'dom>(
|
||||||
//
|
//
|
||||||
// This is also used to ensure that the caret will still be rendered when the input is empty.
|
// This is also used to ensure that the caret will still be rendered when the input is empty.
|
||||||
// TODO: Is there a less hacky way to do this?
|
// TODO: Is there a less hacky way to do this?
|
||||||
handler.handle_text(&info, "\u{200B}".into());
|
handler.handle_text(parent_element_info, "\u{200B}".into());
|
||||||
} else {
|
} else {
|
||||||
handler.handle_text(&info, node_text_content);
|
handler.handle_text(parent_element_info, node_text_content);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for child in iter_child_nodes(parent_element) {
|
for child in iter_child_nodes(parent_element_info.node) {
|
||||||
if child.is_text_node() {
|
if child.is_text_node() {
|
||||||
let info = NodeAndStyleInfo::new(child, child.style(&context.style_context));
|
let info = NodeAndStyleInfo::new(
|
||||||
|
child,
|
||||||
|
child.style(&context.style_context),
|
||||||
|
child.take_restyle_damage(),
|
||||||
|
);
|
||||||
handler.handle_text(&info, child.to_threadsafe().node_text_content());
|
handler.handle_text(&info, child.to_threadsafe().node_text_content());
|
||||||
} else if child.is_element() {
|
} else if child.is_element() {
|
||||||
traverse_element(child, context, handler);
|
traverse_element(child, context, handler);
|
||||||
|
@ -226,7 +223,7 @@ fn traverse_children_of<'dom>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
traverse_eager_pseudo_element(PseudoElement::After, parent_element, context, handler);
|
traverse_eager_pseudo_element(PseudoElement::After, parent_element_info, context, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traverse_element<'dom>(
|
fn traverse_element<'dom>(
|
||||||
|
@ -240,7 +237,10 @@ fn traverse_element<'dom>(
|
||||||
|
|
||||||
let replaced = ReplacedContents::for_element(element, context);
|
let replaced = ReplacedContents::for_element(element, context);
|
||||||
let style = element.style(&context.style_context);
|
let style = element.style(&context.style_context);
|
||||||
match Display::from(style.get_box().display) {
|
let damage = element.take_restyle_damage();
|
||||||
|
let info = NodeAndStyleInfo::new(element, style, damage);
|
||||||
|
|
||||||
|
match Display::from(info.style.get_box().display) {
|
||||||
Display::None => element.unset_all_boxes(),
|
Display::None => element.unset_all_boxes(),
|
||||||
Display::Contents => {
|
Display::Contents => {
|
||||||
if replaced.is_some() {
|
if replaced.is_some() {
|
||||||
|
@ -248,14 +248,13 @@ fn traverse_element<'dom>(
|
||||||
// <https://drafts.csswg.org/css-display-3/#valdef-display-contents>
|
// <https://drafts.csswg.org/css-display-3/#valdef-display-contents>
|
||||||
element.unset_all_boxes()
|
element.unset_all_boxes()
|
||||||
} else {
|
} else {
|
||||||
let shared_inline_styles: SharedInlineStyles =
|
let shared_inline_styles: SharedInlineStyles = (&info).into();
|
||||||
(&NodeAndStyleInfo::new(element, style)).into();
|
|
||||||
element
|
element
|
||||||
.element_box_slot()
|
.element_box_slot()
|
||||||
.set(LayoutBox::DisplayContents(shared_inline_styles.clone()));
|
.set(LayoutBox::DisplayContents(shared_inline_styles.clone()));
|
||||||
|
|
||||||
handler.enter_display_contents(shared_inline_styles);
|
handler.enter_display_contents(shared_inline_styles);
|
||||||
traverse_children_of(element, context, handler);
|
traverse_children_of(&info, context, handler);
|
||||||
handler.leave_display_contents();
|
handler.leave_display_contents();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -274,7 +273,6 @@ fn traverse_element<'dom>(
|
||||||
};
|
};
|
||||||
let display = display.used_value_for_contents(&contents);
|
let display = display.used_value_for_contents(&contents);
|
||||||
let box_slot = element.element_box_slot();
|
let box_slot = element.element_box_slot();
|
||||||
let info = NodeAndStyleInfo::new(element, style);
|
|
||||||
handler.handle_element(&info, display, contents, box_slot);
|
handler.handle_element(&info, display, contents, box_slot);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -282,45 +280,45 @@ fn traverse_element<'dom>(
|
||||||
|
|
||||||
fn traverse_eager_pseudo_element<'dom>(
|
fn traverse_eager_pseudo_element<'dom>(
|
||||||
pseudo_element_type: PseudoElement,
|
pseudo_element_type: PseudoElement,
|
||||||
node: ServoLayoutNode<'dom>,
|
node_info: &NodeAndStyleInfo<'dom>,
|
||||||
context: &LayoutContext,
|
context: &LayoutContext,
|
||||||
handler: &mut impl TraversalHandler<'dom>,
|
handler: &mut impl TraversalHandler<'dom>,
|
||||||
) {
|
) {
|
||||||
assert!(pseudo_element_type.is_eager());
|
assert!(pseudo_element_type.is_eager());
|
||||||
|
|
||||||
// First clear any old contents from the node.
|
// First clear any old contents from the node.
|
||||||
node.unset_pseudo_element_box(pseudo_element_type);
|
node_info.node.unset_pseudo_element_box(pseudo_element_type);
|
||||||
|
|
||||||
let Some(element) = node.to_threadsafe().as_element() else {
|
// If this node doesn't have this eager pseudo-element, exit early. This depends on
|
||||||
|
// the style applied to the element.
|
||||||
|
let Some(pseudo_element_info) = node_info.pseudo(context, pseudo_element_type) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(pseudo_element) = element.with_pseudo(pseudo_element_type) else {
|
if pseudo_element_info.style.ineffective_content_property() {
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let style = pseudo_element.style(&context.style_context);
|
|
||||||
if style.ineffective_content_property() {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let info = NodeAndStyleInfo::new_with_pseudo(node, pseudo_element_type, style);
|
match Display::from(pseudo_element_info.style.get_box().display) {
|
||||||
match Display::from(info.style.get_box().display) {
|
|
||||||
Display::None => {},
|
Display::None => {},
|
||||||
Display::Contents => {
|
Display::Contents => {
|
||||||
let items = generate_pseudo_element_content(&info.style, node, context);
|
let items = generate_pseudo_element_content(&pseudo_element_info, context);
|
||||||
let box_slot = node.pseudo_element_box_slot(pseudo_element_type);
|
let box_slot = pseudo_element_info
|
||||||
let shared_inline_styles: SharedInlineStyles = (&info).into();
|
.node
|
||||||
|
.pseudo_element_box_slot(pseudo_element_type);
|
||||||
|
let shared_inline_styles: SharedInlineStyles = (&pseudo_element_info).into();
|
||||||
box_slot.set(LayoutBox::DisplayContents(shared_inline_styles.clone()));
|
box_slot.set(LayoutBox::DisplayContents(shared_inline_styles.clone()));
|
||||||
|
|
||||||
handler.enter_display_contents(shared_inline_styles);
|
handler.enter_display_contents(shared_inline_styles);
|
||||||
traverse_pseudo_element_contents(&info, context, handler, items);
|
traverse_pseudo_element_contents(&pseudo_element_info, context, handler, items);
|
||||||
handler.leave_display_contents();
|
handler.leave_display_contents();
|
||||||
},
|
},
|
||||||
Display::GeneratingBox(display) => {
|
Display::GeneratingBox(display) => {
|
||||||
let items = generate_pseudo_element_content(&info.style, node, context);
|
let items = generate_pseudo_element_content(&pseudo_element_info, context);
|
||||||
let box_slot = node.pseudo_element_box_slot(pseudo_element_type);
|
let box_slot = pseudo_element_info
|
||||||
|
.node
|
||||||
|
.pseudo_element_box_slot(pseudo_element_type);
|
||||||
let contents = NonReplacedContents::OfPseudoElement(items).into();
|
let contents = NonReplacedContents::OfPseudoElement(items).into();
|
||||||
handler.handle_element(&info, display, contents, box_slot);
|
handler.handle_element(&pseudo_element_info, display, contents, box_slot);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -385,7 +383,7 @@ impl NonReplacedContents {
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
NonReplacedContents::OfElement | NonReplacedContents::OfTextControl => {
|
NonReplacedContents::OfElement | NonReplacedContents::OfTextControl => {
|
||||||
traverse_children_of(info.node, context, handler)
|
traverse_children_of(info, context, handler)
|
||||||
},
|
},
|
||||||
NonReplacedContents::OfPseudoElement(items) => {
|
NonReplacedContents::OfPseudoElement(items) => {
|
||||||
traverse_pseudo_element_contents(info, context, handler, items)
|
traverse_pseudo_element_contents(info, context, handler, items)
|
||||||
|
@ -407,11 +405,10 @@ where
|
||||||
|
|
||||||
/// <https://www.w3.org/TR/CSS2/generate.html#propdef-content>
|
/// <https://www.w3.org/TR/CSS2/generate.html#propdef-content>
|
||||||
fn generate_pseudo_element_content(
|
fn generate_pseudo_element_content(
|
||||||
pseudo_element_style: &ComputedValues,
|
pseudo_element_info: &NodeAndStyleInfo,
|
||||||
element: ServoLayoutNode<'_>,
|
|
||||||
context: &LayoutContext,
|
context: &LayoutContext,
|
||||||
) -> Vec<PseudoElementContentItem> {
|
) -> Vec<PseudoElementContentItem> {
|
||||||
match &pseudo_element_style.get_counters().content {
|
match &pseudo_element_info.style.get_counters().content {
|
||||||
Content::Items(items) => {
|
Content::Items(items) => {
|
||||||
let mut vec = vec![];
|
let mut vec = vec![];
|
||||||
for item in items.items.iter() {
|
for item in items.items.iter() {
|
||||||
|
@ -420,7 +417,8 @@ fn generate_pseudo_element_content(
|
||||||
vec.push(PseudoElementContentItem::Text(s.to_string()));
|
vec.push(PseudoElementContentItem::Text(s.to_string()));
|
||||||
},
|
},
|
||||||
ContentItem::Attr(attr) => {
|
ContentItem::Attr(attr) => {
|
||||||
let element = element
|
let element = pseudo_element_info
|
||||||
|
.node
|
||||||
.to_threadsafe()
|
.to_threadsafe()
|
||||||
.as_element()
|
.as_element()
|
||||||
.expect("Expected an element");
|
.expect("Expected an element");
|
||||||
|
@ -452,14 +450,14 @@ fn generate_pseudo_element_content(
|
||||||
},
|
},
|
||||||
ContentItem::Image(image) => {
|
ContentItem::Image(image) => {
|
||||||
if let Some(replaced_content) =
|
if let Some(replaced_content) =
|
||||||
ReplacedContents::from_image(element, context, image)
|
ReplacedContents::from_image(pseudo_element_info.node, context, image)
|
||||||
{
|
{
|
||||||
vec.push(PseudoElementContentItem::Replaced(replaced_content));
|
vec.push(PseudoElementContentItem::Replaced(replaced_content));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ContentItem::OpenQuote | ContentItem::CloseQuote => {
|
ContentItem::OpenQuote | ContentItem::CloseQuote => {
|
||||||
// TODO(xiaochengh): calculate quote depth
|
// TODO(xiaochengh): calculate quote depth
|
||||||
let maybe_quote = match &pseudo_element_style.get_list().quotes {
|
let maybe_quote = match &pseudo_element_info.style.get_list().quotes {
|
||||||
Quotes::QuoteList(quote_list) => {
|
Quotes::QuoteList(quote_list) => {
|
||||||
quote_list.0.first().map(|quote_pair| {
|
quote_list.0.first().map(|quote_pair| {
|
||||||
get_quote_from_pair(
|
get_quote_from_pair(
|
||||||
|
@ -470,7 +468,7 @@ fn generate_pseudo_element_content(
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Quotes::Auto => {
|
Quotes::Auto => {
|
||||||
let lang = &pseudo_element_style.get_font()._x_lang;
|
let lang = &pseudo_element_info.style.get_font()._x_lang;
|
||||||
let quotes = quotes_for_lang(lang.0.as_ref(), 0);
|
let quotes = quotes_for_lang(lang.0.as_ref(), 0);
|
||||||
Some(get_quote_from_pair(item, "es.opening, "es.closing))
|
Some(get_quote_from_pair(item, "es.opening, "es.closing))
|
||||||
},
|
},
|
||||||
|
|
|
@ -689,6 +689,22 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
|
||||||
impl BlockLevelJob<'_> {
|
impl BlockLevelJob<'_> {
|
||||||
fn finish(self, context: &LayoutContext) -> ArcRefCell<BlockLevelBox> {
|
fn finish(self, context: &LayoutContext) -> ArcRefCell<BlockLevelBox> {
|
||||||
let info = &self.info;
|
let info = &self.info;
|
||||||
|
|
||||||
|
// If this `BlockLevelBox` is undamaged and it has been laid out before, reuse
|
||||||
|
// the old one, while being sure to clear the layout cache.
|
||||||
|
if !info.damage.has_box_damage() {
|
||||||
|
if let Some(block_level_box) = match self.box_slot.slot.as_ref() {
|
||||||
|
Some(box_slot) => match &*box_slot.borrow() {
|
||||||
|
Some(LayoutBox::BlockLevel(block_level_box)) => Some(block_level_box.clone()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
} {
|
||||||
|
block_level_box.borrow().invalidate_cached_fragment();
|
||||||
|
return block_level_box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let block_level_box = match self.kind {
|
let block_level_box = match self.kind {
|
||||||
BlockLevelCreator::SameFormattingContextBlock(intermediate_block_container) => {
|
BlockLevelCreator::SameFormattingContextBlock(intermediate_block_container) => {
|
||||||
let contents = intermediate_block_container.finish(context, info);
|
let contents = intermediate_block_container.finish(context, info);
|
||||||
|
|
|
@ -140,7 +140,11 @@ fn construct_for_root_element(
|
||||||
context: &LayoutContext,
|
context: &LayoutContext,
|
||||||
root_element: ServoLayoutNode<'_>,
|
root_element: ServoLayoutNode<'_>,
|
||||||
) -> Vec<ArcRefCell<BlockLevelBox>> {
|
) -> Vec<ArcRefCell<BlockLevelBox>> {
|
||||||
let info = NodeAndStyleInfo::new(root_element, root_element.style(&context.style_context));
|
let info = NodeAndStyleInfo::new(
|
||||||
|
root_element,
|
||||||
|
root_element.style(&context.style_context),
|
||||||
|
root_element.take_restyle_damage(),
|
||||||
|
);
|
||||||
let box_style = info.style.get_box();
|
let box_style = info.style.get_box();
|
||||||
|
|
||||||
let display_inside = match Display::from(box_style.display) {
|
let display_inside = match Display::from(box_style.display) {
|
||||||
|
@ -378,7 +382,13 @@ impl<'dom> IncrementalBoxTreeUpdate<'dom> {
|
||||||
fn update_from_dirty_root(&self, context: &LayoutContext) {
|
fn update_from_dirty_root(&self, context: &LayoutContext) {
|
||||||
let contents = ReplacedContents::for_element(self.node, context)
|
let contents = ReplacedContents::for_element(self.node, context)
|
||||||
.map_or_else(|| NonReplacedContents::OfElement.into(), Contents::Replaced);
|
.map_or_else(|| NonReplacedContents::OfElement.into(), Contents::Replaced);
|
||||||
let info = NodeAndStyleInfo::new(self.node, self.primary_style.clone());
|
|
||||||
|
let info = NodeAndStyleInfo::new(
|
||||||
|
self.node,
|
||||||
|
self.primary_style.clone(),
|
||||||
|
self.node.take_restyle_damage(),
|
||||||
|
);
|
||||||
|
|
||||||
let out_of_flow_absolutely_positioned_box = ArcRefCell::new(
|
let out_of_flow_absolutely_positioned_box = ArcRefCell::new(
|
||||||
AbsolutelyPositionedBox::construct(context, &info, self.display_inside, contents),
|
AbsolutelyPositionedBox::construct(context, &info, self.display_inside, contents),
|
||||||
);
|
);
|
||||||
|
@ -428,6 +438,14 @@ impl<'dom> IncrementalBoxTreeUpdate<'dom> {
|
||||||
let mut invalidate_start_point = self.node;
|
let mut invalidate_start_point = self.node;
|
||||||
while let Some(parent_node) = invalidate_start_point.parent_node() {
|
while let Some(parent_node) = invalidate_start_point.parent_node() {
|
||||||
parent_node.invalidate_cached_fragment();
|
parent_node.invalidate_cached_fragment();
|
||||||
|
|
||||||
|
// Box tree reconstruction doesn't need to involve these ancestors, so their
|
||||||
|
// damage isn't useful for us.
|
||||||
|
//
|
||||||
|
// TODO: This isn't going to be good enough for incremental fragment tree
|
||||||
|
// reconstruction, as fragment tree damage might extend further up the tree.
|
||||||
|
parent_node.take_restyle_damage();
|
||||||
|
|
||||||
invalidate_start_point = parent_node;
|
invalidate_start_point = parent_node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use layout_api::LayoutDamage;
|
||||||
use layout_api::wrapper_traits::LayoutNode;
|
use layout_api::wrapper_traits::LayoutNode;
|
||||||
use script::layout_dom::ServoLayoutNode;
|
use script::layout_dom::ServoLayoutNode;
|
||||||
use style::context::{SharedStyleContext, StyleContext};
|
use style::context::{SharedStyleContext, StyleContext};
|
||||||
|
@ -103,39 +104,48 @@ pub(crate) fn compute_damage_and_repair_style(
|
||||||
pub(crate) fn compute_damage_and_repair_style_inner(
|
pub(crate) fn compute_damage_and_repair_style_inner(
|
||||||
context: &SharedStyleContext,
|
context: &SharedStyleContext,
|
||||||
node: ServoLayoutNode<'_>,
|
node: ServoLayoutNode<'_>,
|
||||||
parent_restyle_damage: RestyleDamage,
|
damage_from_parent: RestyleDamage,
|
||||||
) -> RestyleDamage {
|
) -> RestyleDamage {
|
||||||
let element_damage;
|
let mut element_damage;
|
||||||
|
let element_data = &node
|
||||||
|
.style_data()
|
||||||
|
.expect("Should not run `compute_damage` before styling.")
|
||||||
|
.element_data;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut element_data = node
|
let mut element_data = element_data.borrow_mut();
|
||||||
.style_data()
|
element_data.damage.insert(damage_from_parent);
|
||||||
.expect("Should not run `compute_damage` before styling.")
|
element_damage = element_data.damage;
|
||||||
.element_data
|
|
||||||
.borrow_mut();
|
|
||||||
|
|
||||||
element_damage = std::mem::take(&mut element_data.damage);
|
|
||||||
|
|
||||||
if let Some(ref style) = element_data.styles.primary {
|
if let Some(ref style) = element_data.styles.primary {
|
||||||
if style.get_box().display == Display::None {
|
if style.get_box().display == Display::None {
|
||||||
return element_damage | parent_restyle_damage;
|
return element_damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let element_and_parent_damage = element_damage | parent_restyle_damage;
|
// If we are reconstructing this node, then all of the children should be reconstructed as well.
|
||||||
|
let damage_for_children = element_damage | damage_from_parent;
|
||||||
let mut damage_from_children = RestyleDamage::empty();
|
let mut damage_from_children = RestyleDamage::empty();
|
||||||
for child in iter_child_nodes(node) {
|
for child in iter_child_nodes(node) {
|
||||||
if child.is_element() {
|
if child.is_element() {
|
||||||
damage_from_children |=
|
damage_from_children |=
|
||||||
compute_damage_and_repair_style_inner(context, child, element_and_parent_damage);
|
compute_damage_and_repair_style_inner(context, child, damage_for_children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let damage_for_parent = damage_from_children | element_and_parent_damage;
|
if element_damage != RestyleDamage::reconstruct() && !element_damage.is_empty() {
|
||||||
if !damage_for_parent.contains(RestyleDamage::RELAYOUT) && !element_damage.is_empty() {
|
|
||||||
node.repair_style(context);
|
node.repair_style(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
damage_for_parent
|
// If one of our children needed to be reconstructed, we need to recollect children
|
||||||
|
// during box tree construction.
|
||||||
|
if damage_from_children.contains(LayoutDamage::recollect_box_tree_children()) {
|
||||||
|
element_damage.insert(LayoutDamage::recollect_box_tree_children());
|
||||||
|
element_data.borrow_mut().damage.insert(element_damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only propagate up layout phases from children, as other types of damage are
|
||||||
|
// incorporated into `element_damage` above.
|
||||||
|
element_damage | (damage_from_children & RestyleDamage::RELAYOUT)
|
||||||
}
|
}
|
||||||
|
|
|
@ -936,7 +936,7 @@ impl Document {
|
||||||
|
|
||||||
// FIXME(emilio): This is very inefficient, ideally the flag above would
|
// FIXME(emilio): This is very inefficient, ideally the flag above would
|
||||||
// be enough and incremental layout could figure out from there.
|
// be enough and incremental layout could figure out from there.
|
||||||
node.dirty(NodeDamage::Other);
|
node.dirty(NodeDamage::ContentOrHeritage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove any existing association between the provided id and any elements in this document.
|
/// Remove any existing association between the provided id and any elements in this document.
|
||||||
|
|
|
@ -23,6 +23,7 @@ use html5ever::{LocalName, Namespace, Prefix, QualName, local_name, namespace_pr
|
||||||
use js::jsapi::Heap;
|
use js::jsapi::Heap;
|
||||||
use js::jsval::JSVal;
|
use js::jsval::JSVal;
|
||||||
use js::rust::HandleObject;
|
use js::rust::HandleObject;
|
||||||
|
use layout_api::LayoutDamage;
|
||||||
use net_traits::ReferrerPolicy;
|
use net_traits::ReferrerPolicy;
|
||||||
use net_traits::request::CorsSettings;
|
use net_traits::request::CorsSettings;
|
||||||
use selectors::Element as SelectorsElement;
|
use selectors::Element as SelectorsElement;
|
||||||
|
@ -358,9 +359,18 @@ impl Element {
|
||||||
// NodeStyleDamaged, but I'm preserving existing behavior.
|
// NodeStyleDamaged, but I'm preserving existing behavior.
|
||||||
restyle.hint.insert(RestyleHint::RESTYLE_SELF);
|
restyle.hint.insert(RestyleHint::RESTYLE_SELF);
|
||||||
|
|
||||||
if damage == NodeDamage::Other {
|
match damage {
|
||||||
doc.note_node_with_dirty_descendants(self.upcast());
|
NodeDamage::Style => {},
|
||||||
restyle.damage = RestyleDamage::reconstruct();
|
NodeDamage::ContentOrHeritage => {
|
||||||
|
doc.note_node_with_dirty_descendants(self.upcast());
|
||||||
|
restyle
|
||||||
|
.damage
|
||||||
|
.insert(LayoutDamage::recollect_box_tree_children());
|
||||||
|
},
|
||||||
|
NodeDamage::Other => {
|
||||||
|
doc.note_node_with_dirty_descendants(self.upcast());
|
||||||
|
restyle.damage.insert(RestyleDamage::reconstruct());
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3927,6 +3927,9 @@ impl VirtualMethods for Node {
|
||||||
pub(crate) enum NodeDamage {
|
pub(crate) enum NodeDamage {
|
||||||
/// The node's `style` attribute changed.
|
/// The node's `style` attribute changed.
|
||||||
Style,
|
Style,
|
||||||
|
/// The node's content or heritage changed, such as the addition or removal of
|
||||||
|
/// children.
|
||||||
|
ContentOrHeritage,
|
||||||
/// Other parts of a node changed; attributes, text content, etc.
|
/// Other parts of a node changed; attributes, text content, etc.
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,29 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
use style::selector_parser::RestyleDamage;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// Individual layout actions that may be necessary after restyling. This is an extension
|
/// Individual layout actions that may be necessary after restyling. This is an extension
|
||||||
/// of `RestyleDamage` from stylo, which only uses the 4 lower bits.
|
/// of `RestyleDamage` from stylo, which only uses the 4 lower bits.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Default, Eq, PartialEq)]
|
||||||
pub struct LayoutDamage: u16 {
|
pub struct LayoutDamage: u16 {
|
||||||
|
/// Recollect the box children for this element, because some of the them will be
|
||||||
|
/// rebuilt.
|
||||||
|
const RECOLLECT_BOX_TREE_CHILDREN = 0b011111111111 << 4;
|
||||||
/// Rebuild the entire box for this element, which means that every part of layout
|
/// Rebuild the entire box for this element, which means that every part of layout
|
||||||
/// needs to happena again.
|
/// needs to happena again.
|
||||||
const REBUILD_BOX = 0b111111111111 << 4;
|
const REBUILD_BOX = 0b111111111111 << 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LayoutDamage {
|
||||||
|
pub fn recollect_box_tree_children() -> RestyleDamage {
|
||||||
|
RestyleDamage::from_bits_retain(LayoutDamage::RECOLLECT_BOX_TREE_CHILDREN.bits()) |
|
||||||
|
RestyleDamage::RELAYOUT
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_box_damage(&self) -> bool {
|
||||||
|
self.intersects(Self::REBUILD_BOX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue