layout: When repairing styles for incremental reflow, only repair

styles of nodes that represent the dirty node, *including its
pseudo-element*.

Fixes lots more jumpiness.

A manual test, `inline-pseudo-repair-jumpiness.html`, has been added. I
was unable to automate it, so I will file a followup issue on that.
This commit is contained in:
Patrick Walton 2015-08-03 16:15:11 -07:00
parent 0565df8596
commit 0a589d413d
6 changed files with 55 additions and 13 deletions

View file

@ -105,7 +105,7 @@ pub enum ConstructionItem {
/// Inline fragments and associated {ib} splits that have not yet found flows. /// Inline fragments and associated {ib} splits that have not yet found flows.
InlineFragments(InlineFragmentsConstructionResult), InlineFragments(InlineFragmentsConstructionResult),
/// Potentially ignorable whitespace. /// Potentially ignorable whitespace.
Whitespace(OpaqueNode, Arc<ComputedValues>, RestyleDamage), Whitespace(OpaqueNode, PseudoElementType<()>, Arc<ComputedValues>, RestyleDamage),
/// TableColumn Fragment /// TableColumn Fragment
TableColumnFragment(Fragment), TableColumnFragment(Fragment),
} }
@ -217,6 +217,7 @@ impl InlineFragmentsAccumulator {
fragments: IntermediateInlineFragments::new(), fragments: IntermediateInlineFragments::new(),
enclosing_node: Some(InlineFragmentNodeInfo { enclosing_node: Some(InlineFragmentNodeInfo {
address: node.opaque(), address: node.opaque(),
pseudo: node.get_pseudo_element_type().strip(),
style: node.style().clone(), style: node.style().clone(),
}), }),
bidi_control_chars: None, bidi_control_chars: None,
@ -573,6 +574,7 @@ impl<'a> FlowConstructor<'a> {
} }
ConstructionResult::ConstructionItem(ConstructionItem::Whitespace( ConstructionResult::ConstructionItem(ConstructionItem::Whitespace(
whitespace_node, whitespace_node,
whitespace_pseudo,
mut whitespace_style, mut whitespace_style,
whitespace_damage)) => { whitespace_damage)) => {
// Add whitespace results. They will be stripped out later on when // Add whitespace results. They will be stripped out later on when
@ -581,6 +583,7 @@ impl<'a> FlowConstructor<'a> {
UnscannedTextFragmentInfo::from_text(" ".to_owned())); UnscannedTextFragmentInfo::from_text(" ".to_owned()));
properties::modify_style_for_replaced_content(&mut whitespace_style); properties::modify_style_for_replaced_content(&mut whitespace_style);
let fragment = Fragment::from_opaque_node_and_style(whitespace_node, let fragment = Fragment::from_opaque_node_and_style(whitespace_node,
whitespace_pseudo,
whitespace_style, whitespace_style,
whitespace_damage, whitespace_damage,
fragment_info); fragment_info);
@ -712,6 +715,8 @@ impl<'a> FlowConstructor<'a> {
fragments.fragments fragments.fragments
.push_back(Fragment::from_opaque_node_and_style(node.opaque(), .push_back(Fragment::from_opaque_node_and_style(node.opaque(),
node.get_pseudo_element_type()
.strip(),
style.clone(), style.clone(),
node.restyle_damage(), node.restyle_damage(),
specific)) specific))
@ -793,12 +798,14 @@ impl<'a> FlowConstructor<'a> {
} else { } else {
// Push the absolutely-positioned kid as an inline containing block. // Push the absolutely-positioned kid as an inline containing block.
let kid_node = flow.as_block().fragment.node; let kid_node = flow.as_block().fragment.node;
let kid_pseudo = flow.as_block().fragment.pseudo.clone();
let kid_style = flow.as_block().fragment.style.clone(); let kid_style = flow.as_block().fragment.style.clone();
let kid_restyle_damage = flow.as_block().fragment.restyle_damage; let kid_restyle_damage = flow.as_block().fragment.restyle_damage;
let fragment_info = SpecificFragmentInfo::InlineAbsolute( let fragment_info = SpecificFragmentInfo::InlineAbsolute(
InlineAbsoluteFragmentInfo::new(flow)); InlineAbsoluteFragmentInfo::new(flow));
fragment_accumulator.push(Fragment::from_opaque_node_and_style( fragment_accumulator.push(Fragment::from_opaque_node_and_style(
kid_node, kid_node,
kid_pseudo,
kid_style, kid_style,
kid_restyle_damage, kid_restyle_damage,
fragment_info)); fragment_info));
@ -826,6 +833,7 @@ impl<'a> FlowConstructor<'a> {
} }
ConstructionResult::ConstructionItem(ConstructionItem::Whitespace( ConstructionResult::ConstructionItem(ConstructionItem::Whitespace(
whitespace_node, whitespace_node,
whitespace_pseudo,
mut whitespace_style, mut whitespace_style,
whitespace_damage)) => { whitespace_damage)) => {
// Instantiate the whitespace fragment. // Instantiate the whitespace fragment.
@ -833,6 +841,7 @@ impl<'a> FlowConstructor<'a> {
UnscannedTextFragmentInfo::from_text(" ".to_owned())); UnscannedTextFragmentInfo::from_text(" ".to_owned()));
properties::modify_style_for_replaced_content(&mut whitespace_style); properties::modify_style_for_replaced_content(&mut whitespace_style);
let fragment = Fragment::from_opaque_node_and_style(whitespace_node, let fragment = Fragment::from_opaque_node_and_style(whitespace_node,
whitespace_pseudo,
whitespace_style, whitespace_style,
whitespace_damage, whitespace_damage,
fragment_info); fragment_info);
@ -875,6 +884,7 @@ impl<'a> FlowConstructor<'a> {
if node.is_ignorable_whitespace() { if node.is_ignorable_whitespace() {
return ConstructionResult::ConstructionItem(ConstructionItem::Whitespace( return ConstructionResult::ConstructionItem(ConstructionItem::Whitespace(
node.opaque(), node.opaque(),
node.get_pseudo_element_type().strip(),
node.style().clone(), node.style().clone(),
node.restyle_damage())) node.restyle_damage()))
} }
@ -1295,10 +1305,14 @@ impl<'a> FlowConstructor<'a> {
for fragment in inline_fragments_construction_result.fragments for fragment in inline_fragments_construction_result.fragments
.fragments .fragments
.iter_mut() { .iter_mut() {
// Only mutate the styles of fragments that represent the dirty node. // Only mutate the styles of fragments that represent the dirty node (including
// pseudo-element).
if fragment.node != node.opaque() { if fragment.node != node.opaque() {
continue continue
} }
if fragment.pseudo != node.get_pseudo_element_type().strip() {
continue
}
match fragment.specific { match fragment.specific {
SpecificFragmentInfo::InlineBlock(ref mut inline_block_fragment) => { SpecificFragmentInfo::InlineBlock(ref mut inline_block_fragment) => {
@ -1689,6 +1703,7 @@ fn control_chars_to_fragment(node: &InlineFragmentNodeInfo, text: &str,
let info = SpecificFragmentInfo::UnscannedText( let info = SpecificFragmentInfo::UnscannedText(
UnscannedTextFragmentInfo::from_text(String::from(text))); UnscannedTextFragmentInfo::from_text(String::from(text)));
Fragment::from_opaque_node_and_style(node.address, Fragment::from_opaque_node_and_style(node.address,
node.pseudo,
node.style.clone(), node.style.clone(),
restyle_damage, restyle_damage,
info) info)

View file

@ -17,7 +17,7 @@ use inline::{InlineFragmentContext, InlineFragmentNodeInfo, InlineMetrics};
use layout_debug; use layout_debug;
use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, specified}; use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, specified};
use text; use text;
use wrapper::ThreadSafeLayoutNode; use wrapper::{PseudoElementType, ThreadSafeLayoutNode};
use euclid::{Point2D, Rect, Size2D}; use euclid::{Point2D, Rect, Size2D};
use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode}; use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode};
@ -107,6 +107,9 @@ pub struct Fragment {
/// How damaged this fragment is since last reflow. /// How damaged this fragment is since last reflow.
pub restyle_damage: RestyleDamage, pub restyle_damage: RestyleDamage,
/// The pseudo-element that this fragment represents.
pub pseudo: PseudoElementType<()>,
/// A debug ID that is consistent for the life of this fragment (via transform etc). /// A debug ID that is consistent for the life of this fragment (via transform etc).
pub debug_id: u16, pub debug_id: u16,
} }
@ -752,6 +755,7 @@ impl Fragment {
margin: LogicalMargin::zero(writing_mode), margin: LogicalMargin::zero(writing_mode),
specific: specific, specific: specific,
inline_context: None, inline_context: None,
pseudo: node.get_pseudo_element_type().strip(),
debug_id: layout_debug::generate_unique_debug_id(), debug_id: layout_debug::generate_unique_debug_id(),
} }
} }
@ -782,12 +786,14 @@ impl Fragment {
margin: LogicalMargin::zero(writing_mode), margin: LogicalMargin::zero(writing_mode),
specific: specific, specific: specific,
inline_context: None, inline_context: None,
pseudo: node.get_pseudo_element_type().strip(),
debug_id: layout_debug::generate_unique_debug_id(), debug_id: layout_debug::generate_unique_debug_id(),
} }
} }
/// Constructs a new `Fragment` instance from an opaque node. /// Constructs a new `Fragment` instance from an opaque node.
pub fn from_opaque_node_and_style(node: OpaqueNode, pub fn from_opaque_node_and_style(node: OpaqueNode,
pseudo: PseudoElementType<()>,
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
restyle_damage: RestyleDamage, restyle_damage: RestyleDamage,
specific: SpecificFragmentInfo) specific: SpecificFragmentInfo)
@ -802,6 +808,7 @@ impl Fragment {
margin: LogicalMargin::zero(writing_mode), margin: LogicalMargin::zero(writing_mode),
specific: specific, specific: specific,
inline_context: None, inline_context: None,
pseudo: pseudo,
debug_id: layout_debug::generate_unique_debug_id(), debug_id: layout_debug::generate_unique_debug_id(),
} }
} }
@ -834,6 +841,7 @@ impl Fragment {
margin: self.margin, margin: self.margin,
specific: info, specific: info,
inline_context: self.inline_context.clone(), inline_context: self.inline_context.clone(),
pseudo: self.pseudo.clone(),
debug_id: self.debug_id, debug_id: self.debug_id,
} }
} }

View file

@ -15,6 +15,7 @@ use fragment::{Fragment, GeneratedContentInfo, SpecificFragmentInfo, UnscannedTe
use incremental::{self, RESOLVE_GENERATED_CONTENT}; use incremental::{self, RESOLVE_GENERATED_CONTENT};
use smallvec::SmallVec; use smallvec::SmallVec;
use text::TextRunScanner; use text::TextRunScanner;
use wrapper::PseudoElementType;
use gfx::display_list::OpaqueNode; use gfx::display_list::OpaqueNode;
use std::collections::{LinkedList, HashMap}; use std::collections::{LinkedList, HashMap};
@ -174,6 +175,7 @@ impl<'a,'b> ResolveGeneratedContentFragmentMutator<'a,'b> {
GeneratedContentInfo::ListItem => { GeneratedContentInfo::ListItem => {
new_info = self.traversal.list_item.render(self.traversal.layout_context, new_info = self.traversal.list_item.render(self.traversal.layout_context,
fragment.node, fragment.node,
fragment.pseudo.clone(),
fragment.style.clone(), fragment.style.clone(),
list_style_type, list_style_type,
RenderingMode::Suffix(".\u{00a0}")) RenderingMode::Suffix(".\u{00a0}"))
@ -190,6 +192,7 @@ impl<'a,'b> ResolveGeneratedContentFragmentMutator<'a,'b> {
.unwrap_or(&mut temporary_counter); .unwrap_or(&mut temporary_counter);
new_info = counter.render(self.traversal.layout_context, new_info = counter.render(self.traversal.layout_context,
fragment.node, fragment.node,
fragment.pseudo.clone(),
fragment.style.clone(), fragment.style.clone(),
counter_style, counter_style,
RenderingMode::Plain) RenderingMode::Plain)
@ -204,6 +207,7 @@ impl<'a,'b> ResolveGeneratedContentFragmentMutator<'a,'b> {
.unwrap_or(&mut temporary_counter); .unwrap_or(&mut temporary_counter);
new_info = counter.render(self.traversal.layout_context, new_info = counter.render(self.traversal.layout_context,
fragment.node, fragment.node,
fragment.pseudo,
fragment.style.clone(), fragment.style.clone(),
counter_style, counter_style,
RenderingMode::All(&separator)); RenderingMode::All(&separator));
@ -211,6 +215,7 @@ impl<'a,'b> ResolveGeneratedContentFragmentMutator<'a,'b> {
GeneratedContentInfo::ContentItem(ContentItem::OpenQuote) => { GeneratedContentInfo::ContentItem(ContentItem::OpenQuote) => {
new_info = Some(render_text(self.traversal.layout_context, new_info = Some(render_text(self.traversal.layout_context,
fragment.node, fragment.node,
fragment.pseudo,
fragment.style.clone(), fragment.style.clone(),
self.quote(&*fragment.style, false))); self.quote(&*fragment.style, false)));
self.traversal.quote += 1 self.traversal.quote += 1
@ -222,6 +227,7 @@ impl<'a,'b> ResolveGeneratedContentFragmentMutator<'a,'b> {
new_info = Some(render_text(self.traversal.layout_context, new_info = Some(render_text(self.traversal.layout_context,
fragment.node, fragment.node,
fragment.pseudo,
fragment.style.clone(), fragment.style.clone(),
self.quote(&*fragment.style, true))); self.quote(&*fragment.style, true)));
} }
@ -356,6 +362,7 @@ impl Counter {
fn render(&self, fn render(&self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
node: OpaqueNode, node: OpaqueNode,
pseudo: PseudoElementType<()>,
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
list_style_type: list_style_type::T, list_style_type: list_style_type::T,
mode: RenderingMode) mode: RenderingMode)
@ -392,7 +399,7 @@ impl Counter {
if string.is_empty() { if string.is_empty() {
None None
} else { } else {
Some(render_text(layout_context, node, style, string)) Some(render_text(layout_context, node, pseudo, style, string))
} }
} }
} }
@ -418,12 +425,14 @@ struct CounterValue {
/// Creates fragment info for a literal string. /// Creates fragment info for a literal string.
fn render_text(layout_context: &LayoutContext, fn render_text(layout_context: &LayoutContext,
node: OpaqueNode, node: OpaqueNode,
pseudo: PseudoElementType<()>,
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
string: String) string: String)
-> SpecificFragmentInfo { -> SpecificFragmentInfo {
let mut fragments = LinkedList::new(); let mut fragments = LinkedList::new();
let info = SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::from_text(string)); let info = SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::from_text(string));
fragments.push_back(Fragment::from_opaque_node_and_style(node, fragments.push_back(Fragment::from_opaque_node_and_style(node,
pseudo,
style, style,
incremental::rebuild_and_reflow(), incremental::rebuild_and_reflow(),
info)); info));

View file

@ -15,6 +15,7 @@ use incremental::{REFLOW, REFLOW_OUT_OF_FLOW, RESOLVE_GENERATED_CONTENT};
use layout_debug; use layout_debug;
use model::IntrinsicISizesContribution; use model::IntrinsicISizesContribution;
use text; use text;
use wrapper::PseudoElementType;
use euclid::{Point2D, Rect, Size2D}; use euclid::{Point2D, Rect, Size2D};
use gfx::display_list::OpaqueNode; use gfx::display_list::OpaqueNode;
@ -1349,8 +1350,7 @@ impl Flow for InlineFlow {
self.base.block_container_explicit_block_size; self.base.block_container_explicit_block_size;
for fragment in self.fragments.fragments.iter_mut() { for fragment in self.fragments.fragments.iter_mut() {
fragment.update_late_computed_replaced_inline_size_if_necessary(); fragment.update_late_computed_replaced_inline_size_if_necessary();
fragment.assign_replaced_block_size_if_necessary( fragment.assign_replaced_block_size_if_necessary(containing_block_block_size);
containing_block_block_size);
} }
// Reset our state, so that we handle incremental reflow correctly. // Reset our state, so that we handle incremental reflow correctly.
@ -1687,6 +1687,7 @@ impl fmt::Debug for InlineFlow {
pub struct InlineFragmentNodeInfo { pub struct InlineFragmentNodeInfo {
pub address: OpaqueNode, pub address: OpaqueNode,
pub style: Arc<ComputedValues>, pub style: Arc<ComputedValues>,
pub pseudo: PseudoElementType<()>,
} }
#[derive(Clone)] #[derive(Clone)]

View file

@ -588,13 +588,13 @@ impl<'le> TElementAttributes for LayoutElement<'le> {
} }
#[derive(Copy, PartialEq, Clone)] #[derive(Copy, PartialEq, Clone)]
pub enum PseudoElementType { pub enum PseudoElementType<T> {
Normal, Normal,
Before(display::T), Before(T),
After(display::T), After(T),
} }
impl PseudoElementType { impl<T> PseudoElementType<T> {
pub fn is_before(&self) -> bool { pub fn is_before(&self) -> bool {
match *self { match *self {
PseudoElementType::Before(_) => true, PseudoElementType::Before(_) => true,
@ -608,6 +608,14 @@ impl PseudoElementType {
_ => false, _ => false,
} }
} }
pub fn strip(&self) -> PseudoElementType<()> {
match *self {
PseudoElementType::Normal => PseudoElementType::Normal,
PseudoElementType::Before(_) => PseudoElementType::Before(()),
PseudoElementType::After(_) => PseudoElementType::After(()),
}
}
} }
/// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout /// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout
@ -617,7 +625,7 @@ pub struct ThreadSafeLayoutNode<'ln> {
/// The wrapped node. /// The wrapped node.
node: LayoutNode<'ln>, node: LayoutNode<'ln>,
pseudo: PseudoElementType, pseudo: PseudoElementType<display::T>,
} }
impl<'ln> ThreadSafeLayoutNode<'ln> { impl<'ln> ThreadSafeLayoutNode<'ln> {
@ -639,7 +647,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
/// Creates a new `ThreadSafeLayoutNode` for the same `LayoutNode` /// Creates a new `ThreadSafeLayoutNode` for the same `LayoutNode`
/// with a different pseudo-element type. /// with a different pseudo-element type.
fn with_pseudo(&self, pseudo: PseudoElementType) -> ThreadSafeLayoutNode<'ln> { fn with_pseudo(&self, pseudo: PseudoElementType<display::T>) -> ThreadSafeLayoutNode<'ln> {
ThreadSafeLayoutNode { ThreadSafeLayoutNode {
node: self.node.clone(), node: self.node.clone(),
pseudo: pseudo, pseudo: pseudo,
@ -697,7 +705,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
} }
#[inline] #[inline]
pub fn get_pseudo_element_type(&self) -> PseudoElementType { pub fn get_pseudo_element_type(&self) -> PseudoElementType<display::T> {
self.pseudo self.pseudo
} }

View file

@ -0,0 +1 @@
<body>a<br>b</body>