mirror of
https://github.com/servo/servo.git
synced 2025-08-07 22:45:34 +01:00
Incremental Style Recalc
This patch puts in the initial framework for incremental reflow. Nodes' styles are no longer recalculated unless the node has changed. I've been hacking on the general problem of incremental reflow for the past couple weeks, and I've yet to get a full implementation that actually passes all the reftests + wikipedia + cnn. Therefore, I'm going to try to land the different parts of it one by one. This patch only does incremental style recalc, without incremental flow construction, inline-size bubbling, reflow, or display lists. Those will be coming in that order as I finish them. At least with this strategy, I can land a working version of incremental reflow, even if not yet complete. r? @pcwalton
This commit is contained in:
parent
510f8a817f
commit
d12c6e7383
31 changed files with 641 additions and 424 deletions
|
@ -34,6 +34,7 @@ use fragment::{InlineBlockFragmentInfo, InputFragment, InputFragmentInfo, Specif
|
|||
use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo, TableFragment};
|
||||
use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment};
|
||||
use fragment::{UnscannedTextFragmentInfo};
|
||||
use incremental::RestyleDamage;
|
||||
use inline::{InlineFragments, InlineFlow};
|
||||
use parallel;
|
||||
use table_wrapper::TableWrapperFlow;
|
||||
|
@ -88,7 +89,7 @@ pub enum ConstructionItem {
|
|||
/// Inline fragments and associated {ib} splits that have not yet found flows.
|
||||
InlineFragmentsConstructionItem(InlineFragmentsConstructionResult),
|
||||
/// Potentially ignorable whitespace.
|
||||
WhitespaceConstructionItem(OpaqueNode, Arc<ComputedValues>),
|
||||
WhitespaceConstructionItem(OpaqueNode, Arc<ComputedValues>, RestyleDamage),
|
||||
/// TableColumn Fragment
|
||||
TableColumnFragmentConstructionItem(Fragment),
|
||||
}
|
||||
|
@ -441,13 +442,15 @@ impl<'a> FlowConstructor<'a> {
|
|||
abs_descendants.push_descendants(kid_abs_descendants);
|
||||
}
|
||||
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
|
||||
whitespace_style)) => {
|
||||
whitespace_style,
|
||||
whitespace_damage)) => {
|
||||
// Add whitespace results. They will be stripped out later on when
|
||||
// between block elements, and retained when between inline elements.
|
||||
let fragment_info =
|
||||
UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
|
||||
let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node,
|
||||
whitespace_style,
|
||||
whitespace_damage,
|
||||
fragment_info);
|
||||
inline_fragment_accumulator.fragments.push(&mut fragment);
|
||||
}
|
||||
|
@ -607,12 +610,14 @@ impl<'a> FlowConstructor<'a> {
|
|||
abs_descendants.push_descendants(kid_abs_descendants);
|
||||
}
|
||||
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
|
||||
whitespace_style))
|
||||
whitespace_style,
|
||||
whitespace_damage))
|
||||
=> {
|
||||
// Instantiate the whitespace fragment.
|
||||
let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
|
||||
let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node,
|
||||
whitespace_style,
|
||||
whitespace_damage,
|
||||
fragment_info);
|
||||
fragment_accumulator.fragments.push(&mut fragment)
|
||||
}
|
||||
|
@ -654,7 +659,8 @@ impl<'a> FlowConstructor<'a> {
|
|||
let opaque_node = OpaqueNodeMethods::from_thread_safe_layout_node(node);
|
||||
return ConstructionItemConstructionResult(WhitespaceConstructionItem(
|
||||
opaque_node,
|
||||
node.style().clone()))
|
||||
node.style().clone(),
|
||||
node.restyle_damage()))
|
||||
}
|
||||
|
||||
// If this is generated content, then we need to initialize the accumulator with the
|
||||
|
@ -1196,12 +1202,16 @@ impl FlowConstructionUtils for FlowRef {
|
|||
///
|
||||
/// This must not be public because only the layout constructor can do this.
|
||||
fn add_new_child(&mut self, mut new_child: FlowRef) {
|
||||
let base = flow::mut_base(self.get_mut());
|
||||
|
||||
{
|
||||
let kid_base = flow::mut_base(new_child.get_mut());
|
||||
|
||||
base.restyle_damage.insert(kid_base.restyle_damage.propagate_up());
|
||||
|
||||
kid_base.parallel.parent = parallel::mut_owned_flow_to_unsafe_flow(self);
|
||||
}
|
||||
|
||||
let base = flow::mut_base(self.get_mut());
|
||||
base.children.push_back(new_child);
|
||||
let _ = base.parallel.children_count.fetch_add(1, Relaxed);
|
||||
let _ = base.parallel.children_and_absolute_descendant_count.fetch_add(1, Relaxed);
|
||||
|
|
|
@ -11,6 +11,7 @@ use gfx::display_list::OpaqueNode;
|
|||
use gfx::font_context::FontContext;
|
||||
use gfx::font_cache_task::FontCacheTask;
|
||||
use script::layout_interface::LayoutChan;
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
use servo_msg::constellation_msg::ConstellationChan;
|
||||
use servo_net::local_image_cache::LocalImageCache;
|
||||
use servo_util::geometry::Au;
|
||||
|
@ -49,7 +50,7 @@ fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext) -> *
|
|||
|
||||
pub struct SharedLayoutContext {
|
||||
/// The local image cache.
|
||||
pub image_cache: Arc<Mutex<LocalImageCache>>,
|
||||
pub image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>,
|
||||
|
||||
/// The current screen size.
|
||||
pub screen_size: Size2D<Au>,
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
use css::node_style::StyledNode;
|
||||
use construct::FlowConstructor;
|
||||
use context::LayoutContext;
|
||||
use incremental;
|
||||
use incremental::RestyleDamage;
|
||||
use util::{LayoutDataAccess, LayoutDataWrapper};
|
||||
use wrapper::{LayoutElement, LayoutNode, PostorderNodeMutTraversal, ThreadSafeLayoutNode};
|
||||
use wrapper::{TLayoutNode};
|
||||
|
@ -331,7 +333,7 @@ trait PrivateMatchMethods {
|
|||
style: &mut Option<Arc<ComputedValues>>,
|
||||
applicable_declarations_cache: &mut
|
||||
ApplicableDeclarationsCache,
|
||||
shareable: bool);
|
||||
shareable: bool) -> RestyleDamage;
|
||||
|
||||
fn share_style_with_candidate_if_possible(&self,
|
||||
parent_node: Option<LayoutNode>,
|
||||
|
@ -346,7 +348,7 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
|||
style: &mut Option<Arc<ComputedValues>>,
|
||||
applicable_declarations_cache: &mut
|
||||
ApplicableDeclarationsCache,
|
||||
shareable: bool) {
|
||||
shareable: bool) -> RestyleDamage {
|
||||
let this_style;
|
||||
let cacheable;
|
||||
match parent_style {
|
||||
|
@ -378,7 +380,9 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
|||
applicable_declarations_cache.insert(applicable_declarations, this_style.clone());
|
||||
}
|
||||
|
||||
let damage = incremental::compute_damage(style, &*this_style);
|
||||
*style = Some(this_style);
|
||||
damage
|
||||
}
|
||||
|
||||
|
||||
|
@ -468,8 +472,15 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
|||
match self.share_style_with_candidate_if_possible(parent.clone(), candidate) {
|
||||
Some(shared_style) => {
|
||||
// Yay, cache hit. Share the style.
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
layout_data_ref.as_mut().unwrap().shared_data.style = Some(shared_style);
|
||||
let damage = {
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
let shared_data = &mut layout_data_ref.as_mut().unwrap().shared_data;
|
||||
let style = &mut shared_data.style;
|
||||
let damage = incremental::compute_damage(style, &*shared_style);
|
||||
*style = Some(shared_style);
|
||||
damage
|
||||
};
|
||||
ThreadSafeLayoutNode::new(self).set_restyle_damage(damage);
|
||||
return StyleWasShared(i)
|
||||
}
|
||||
None => {}
|
||||
|
@ -602,58 +613,71 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
|||
//
|
||||
// FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow
|
||||
// enforced safe, race-free access to the parent style.
|
||||
let parent_style = match parent {
|
||||
None => None,
|
||||
let (down_restyle_damage, parent_style) = match parent {
|
||||
None => (RestyleDamage::empty(), None),
|
||||
Some(parent_node) => {
|
||||
let parent_layout_data = parent_node.borrow_layout_data_unchecked();
|
||||
match *parent_layout_data {
|
||||
None => fail!("no parent data?!"),
|
||||
Some(ref parent_layout_data) => {
|
||||
let down_restyle_damage =
|
||||
parent_layout_data.data.restyle_damage.propagate_down();
|
||||
match parent_layout_data.shared_data.style {
|
||||
None => fail!("parent hasn't been styled yet?!"),
|
||||
Some(ref style) => Some(style),
|
||||
Some(ref style) => (down_restyle_damage, Some(style)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match &mut *layout_data_ref {
|
||||
&None => fail!("no layout data"),
|
||||
&Some(ref mut layout_data) => {
|
||||
match self.type_id() {
|
||||
Some(TextNodeTypeId) => {
|
||||
// Text nodes get a copy of the parent style. This ensures
|
||||
// that during fragment construction any non-inherited
|
||||
// CSS properties (such as vertical-align) are correctly
|
||||
// set on the fragment(s).
|
||||
let cloned_parent_style = parent_style.unwrap().clone();
|
||||
layout_data.shared_data.style = Some(cloned_parent_style);
|
||||
}
|
||||
_ => {
|
||||
self.cascade_node_pseudo_element(parent_style,
|
||||
applicable_declarations.normal.as_slice(),
|
||||
&mut layout_data.shared_data.style,
|
||||
applicable_declarations_cache,
|
||||
applicable_declarations.normal_shareable);
|
||||
if applicable_declarations.before.len() > 0 {
|
||||
self.cascade_node_pseudo_element(Some(layout_data.shared_data.style.as_ref().unwrap()),
|
||||
applicable_declarations.before.as_slice(),
|
||||
&mut layout_data.data.before_style,
|
||||
applicable_declarations_cache,
|
||||
false);
|
||||
let mut damage = down_restyle_damage;
|
||||
|
||||
{
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match &mut *layout_data_ref {
|
||||
&None => fail!("no layout data"),
|
||||
&Some(ref mut layout_data) => {
|
||||
match self.type_id() {
|
||||
Some(TextNodeTypeId) => {
|
||||
// Text nodes get a copy of the parent style. This ensures
|
||||
// that during fragment construction any non-inherited
|
||||
// CSS properties (such as vertical-align) are correctly
|
||||
// set on the fragment(s).
|
||||
let cloned_parent_style = parent_style.unwrap().clone();
|
||||
layout_data.shared_data.style = Some(cloned_parent_style);
|
||||
}
|
||||
if applicable_declarations.after.len() > 0 {
|
||||
self.cascade_node_pseudo_element(Some(layout_data.shared_data.style.as_ref().unwrap()),
|
||||
applicable_declarations.after.as_slice(),
|
||||
&mut layout_data.data.after_style,
|
||||
applicable_declarations_cache,
|
||||
false);
|
||||
_ => {
|
||||
damage = damage
|
||||
| self.cascade_node_pseudo_element(
|
||||
parent_style,
|
||||
applicable_declarations.normal.as_slice(),
|
||||
&mut layout_data.shared_data.style,
|
||||
applicable_declarations_cache,
|
||||
applicable_declarations.normal_shareable);
|
||||
if applicable_declarations.before.len() > 0 {
|
||||
damage = damage
|
||||
| self.cascade_node_pseudo_element(
|
||||
Some(layout_data.shared_data.style.as_ref().unwrap()),
|
||||
applicable_declarations.before.as_slice(),
|
||||
&mut layout_data.data.before_style,
|
||||
applicable_declarations_cache,
|
||||
false);
|
||||
}
|
||||
if applicable_declarations.after.len() > 0 {
|
||||
damage = damage
|
||||
| self.cascade_node_pseudo_element(Some(layout_data.shared_data.style.as_ref().unwrap()),
|
||||
applicable_declarations.after.as_slice(),
|
||||
&mut layout_data.data.after_style,
|
||||
applicable_declarations_cache,
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThreadSafeLayoutNode::new(self).set_restyle_damage(damage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@ use sync::Arc;
|
|||
/// Node mixin providing `style` method that returns a `NodeStyle`
|
||||
pub trait StyledNode {
|
||||
fn style<'a>(&'a self) -> &'a Arc<ComputedValues>;
|
||||
fn restyle_damage(&self) -> RestyleDamage;
|
||||
fn restyle_damage(self) -> RestyleDamage;
|
||||
fn set_restyle_damage(self, damage: RestyleDamage);
|
||||
}
|
||||
|
||||
impl<'ln> StyledNode for ThreadSafeLayoutNode<'ln> {
|
||||
|
@ -23,8 +24,14 @@ impl<'ln> StyledNode for ThreadSafeLayoutNode<'ln> {
|
|||
self.get_css_select_results()
|
||||
}
|
||||
|
||||
fn restyle_damage(&self) -> RestyleDamage {
|
||||
fn restyle_damage(self) -> RestyleDamage {
|
||||
self.get_restyle_damage()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_restyle_damage(self, damage: RestyleDamage) {
|
||||
fn doit<N: NodeUtil>(n: N, damage: RestyleDamage) {
|
||||
n.set_restyle_damage(damage);
|
||||
}
|
||||
doit(self, damage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use incremental::RestyleDamage;
|
||||
use util::LayoutDataAccess;
|
||||
use wrapper::{TLayoutNode, ThreadSafeLayoutNode};
|
||||
use wrapper::ThreadSafeLayoutNode;
|
||||
use wrapper::{After, Before, Normal};
|
||||
use std::mem;
|
||||
use style::ComputedValues;
|
||||
|
@ -14,8 +14,8 @@ pub trait NodeUtil {
|
|||
fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues>;
|
||||
fn have_css_select_results(&self) -> bool;
|
||||
|
||||
fn get_restyle_damage(&self) -> RestyleDamage;
|
||||
fn set_restyle_damage(&self, damage: RestyleDamage);
|
||||
fn get_restyle_damage(self) -> RestyleDamage;
|
||||
fn set_restyle_damage(self, damage: RestyleDamage);
|
||||
}
|
||||
|
||||
impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> {
|
||||
|
@ -62,28 +62,19 @@ impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> {
|
|||
|
||||
/// Get the description of how to account for recent style changes.
|
||||
/// This is a simple bitfield and fine to copy by value.
|
||||
fn get_restyle_damage(&self) -> RestyleDamage {
|
||||
// For DOM elements, if we haven't computed damage yet, assume the worst.
|
||||
// Other nodes don't have styles.
|
||||
let default = if self.node_is_element() {
|
||||
RestyleDamage::all()
|
||||
} else {
|
||||
RestyleDamage::empty()
|
||||
};
|
||||
|
||||
fn get_restyle_damage(self) -> RestyleDamage {
|
||||
let layout_data_ref = self.borrow_layout_data();
|
||||
layout_data_ref
|
||||
.as_ref().unwrap()
|
||||
.data
|
||||
.restyle_damage
|
||||
.unwrap_or(default)
|
||||
}
|
||||
|
||||
/// Set the restyle damage field.
|
||||
fn set_restyle_damage(&self, damage: RestyleDamage) {
|
||||
fn set_restyle_damage(self, damage: RestyleDamage) {
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match &mut *layout_data_ref {
|
||||
&Some(ref mut layout_data) => layout_data.data.restyle_damage = Some(damage),
|
||||
&Some(ref mut layout_data) => layout_data.data.restyle_damage = damage,
|
||||
_ => fail!("no layout data for this node"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -190,6 +190,10 @@ impl Floats {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> uint {
|
||||
self.list.list.as_ref().map(|list| list.floats.len()).unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Returns a rectangle that encloses the region from block-start to block-start + block-size, with inline-size small
|
||||
/// enough that it doesn't collide with any floats. max_x is the x-coordinate beyond which
|
||||
/// floats have no effect. (Generally this is the containing block inline-size.)
|
||||
|
@ -437,4 +441,3 @@ impl Floats {
|
|||
clearance
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -471,6 +471,13 @@ pub trait PreorderFlowTraversal {
|
|||
/// The operation to perform. Return true to continue or false to stop.
|
||||
fn process(&mut self, flow: &mut Flow) -> bool;
|
||||
|
||||
/// Returns true if this node must be processed in-order. If this returns false,
|
||||
/// we skip the operation for this node, but continue processing the descendants.
|
||||
/// This is called *after* parent nodes are visited.
|
||||
fn should_process(&mut self, _flow: &mut Flow) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns true if this node should be pruned. If this returns true, we skip the operation
|
||||
/// entirely and do not process any descendant nodes. This is called *before* child nodes are
|
||||
/// visited. The default implementation never prunes any nodes.
|
||||
|
@ -1031,6 +1038,10 @@ impl<'a> MutableFlowUtils for &'a mut Flow + 'a {
|
|||
return true
|
||||
}
|
||||
|
||||
if !traversal.should_process(self) {
|
||||
return true
|
||||
}
|
||||
|
||||
if !traversal.process(self) {
|
||||
return false
|
||||
}
|
||||
|
@ -1240,4 +1251,3 @@ impl ContainingBlockLink {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ use floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
|
|||
use flow::Flow;
|
||||
use flow;
|
||||
use flow_ref::FlowRef;
|
||||
use incremental::RestyleDamage;
|
||||
use inline::{InlineFragmentContext, InlineMetrics};
|
||||
use layout_debug;
|
||||
use model::{Auto, IntrinsicISizes, MaybeAuto, Specified, specified};
|
||||
|
@ -35,13 +36,14 @@ use gfx::display_list::{Upright, SidewaysLeft, SidewaysRight};
|
|||
use gfx::font::FontStyle;
|
||||
use gfx::text::glyph::CharIndex;
|
||||
use gfx::text::text_run::TextRun;
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
use serialize::{Encodable, Encoder};
|
||||
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId};
|
||||
use servo_net::image::holder::ImageHolder;
|
||||
use servo_net::local_image_cache::LocalImageCache;
|
||||
use servo_util::geometry::Au;
|
||||
use servo_util::geometry;
|
||||
use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin};
|
||||
use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin, WritingMode};
|
||||
use servo_util::range::*;
|
||||
use servo_util::smallvec::SmallVec;
|
||||
use servo_util::str::is_whitespace;
|
||||
|
@ -87,6 +89,9 @@ pub struct Fragment {
|
|||
/// The CSS style of this fragment.
|
||||
pub style: Arc<ComputedValues>,
|
||||
|
||||
/// The incremental damage done to this fragment during the current layout round.
|
||||
pub restyle_damage: RestyleDamage,
|
||||
|
||||
/// The position of this fragment relative to its owning flow.
|
||||
/// The size includes padding and border, but not margin.
|
||||
pub border_box: LogicalRect<Au>,
|
||||
|
@ -202,7 +207,8 @@ impl InputFragmentInfo {
|
|||
#[deriving(Clone)]
|
||||
pub struct ImageFragmentInfo {
|
||||
/// The image held within this fragment.
|
||||
pub image: ImageHolder,
|
||||
pub image: ImageHolder<UntrustedNodeAddress>,
|
||||
pub for_node: UntrustedNodeAddress,
|
||||
pub computed_inline_size: Option<Au>,
|
||||
pub computed_block_size: Option<Au>,
|
||||
pub dom_inline_size: Option<Au>,
|
||||
|
@ -217,7 +223,7 @@ impl ImageFragmentInfo {
|
|||
/// me.
|
||||
pub fn new(node: &ThreadSafeLayoutNode,
|
||||
image_url: Url,
|
||||
local_image_cache: Arc<Mutex<LocalImageCache>>)
|
||||
local_image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>)
|
||||
-> ImageFragmentInfo {
|
||||
fn convert_length(node: &ThreadSafeLayoutNode, name: &str) -> Option<Au> {
|
||||
let element = node.as_element();
|
||||
|
@ -230,8 +236,13 @@ impl ImageFragmentInfo {
|
|||
let is_vertical = node.style().writing_mode.is_vertical();
|
||||
let dom_width = convert_length(node, "width");
|
||||
let dom_height = convert_length(node, "height");
|
||||
|
||||
let opaque_node: OpaqueNode = OpaqueNodeMethods::from_thread_safe_layout_node(node);
|
||||
let untrusted_node: UntrustedNodeAddress = opaque_node.to_untrusted_node_address();
|
||||
|
||||
ImageFragmentInfo {
|
||||
image: ImageHolder::new(image_url, local_image_cache),
|
||||
for_node: untrusted_node,
|
||||
computed_inline_size: None,
|
||||
computed_block_size: None,
|
||||
dom_inline_size: if is_vertical { dom_height } else { dom_width },
|
||||
|
@ -252,13 +263,13 @@ impl ImageFragmentInfo {
|
|||
|
||||
/// Returns the original inline-size of the image.
|
||||
pub fn image_inline_size(&mut self) -> Au {
|
||||
let size = self.image.get_size().unwrap_or(Size2D::zero());
|
||||
let size = self.image.get_size(self.for_node).unwrap_or(Size2D::zero());
|
||||
Au::from_px(if self.writing_mode_is_vertical { size.height } else { size.width })
|
||||
}
|
||||
|
||||
/// Returns the original block-size of the image.
|
||||
pub fn image_block_size(&mut self) -> Au {
|
||||
let size = self.image.get_size().unwrap_or(Size2D::zero());
|
||||
let size = self.image.get_size(self.for_node).unwrap_or(Size2D::zero());
|
||||
Au::from_px(if self.writing_mode_is_vertical { size.width } else { size.height })
|
||||
}
|
||||
|
||||
|
@ -433,6 +444,7 @@ impl Fragment {
|
|||
Fragment {
|
||||
node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
|
||||
style: style,
|
||||
restyle_damage: node.restyle_damage(),
|
||||
border_box: LogicalRect::zero(writing_mode),
|
||||
border_padding: LogicalMargin::zero(writing_mode),
|
||||
margin: LogicalMargin::zero(writing_mode),
|
||||
|
@ -451,6 +463,7 @@ impl Fragment {
|
|||
Fragment {
|
||||
node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
|
||||
style: style,
|
||||
restyle_damage: node.restyle_damage(),
|
||||
border_box: LogicalRect::zero(writing_mode),
|
||||
border_padding: LogicalMargin::zero(writing_mode),
|
||||
margin: LogicalMargin::zero(writing_mode),
|
||||
|
@ -477,6 +490,7 @@ impl Fragment {
|
|||
Fragment {
|
||||
node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
|
||||
style: Arc::new(node_style),
|
||||
restyle_damage: node.restyle_damage(),
|
||||
border_box: LogicalRect::zero(writing_mode),
|
||||
border_padding: LogicalMargin::zero(writing_mode),
|
||||
margin: LogicalMargin::zero(writing_mode),
|
||||
|
@ -490,12 +504,14 @@ impl Fragment {
|
|||
/// Constructs a new `Fragment` instance from an opaque node.
|
||||
pub fn from_opaque_node_and_style(node: OpaqueNode,
|
||||
style: Arc<ComputedValues>,
|
||||
restyle_damage: RestyleDamage,
|
||||
specific: SpecificFragmentInfo)
|
||||
-> Fragment {
|
||||
let writing_mode = style.writing_mode;
|
||||
Fragment {
|
||||
node: node,
|
||||
style: style,
|
||||
restyle_damage: restyle_damage,
|
||||
border_box: LogicalRect::zero(writing_mode),
|
||||
border_padding: LogicalMargin::zero(writing_mode),
|
||||
margin: LogicalMargin::zero(writing_mode),
|
||||
|
@ -518,6 +534,7 @@ impl Fragment {
|
|||
Fragment {
|
||||
node: self.node,
|
||||
style: self.style.clone(),
|
||||
restyle_damage: self.restyle_damage,
|
||||
border_box: LogicalRect::from_point_size(
|
||||
self.style.writing_mode, self.border_box.start, size),
|
||||
border_padding: self.border_padding,
|
||||
|
@ -805,7 +822,7 @@ impl Fragment {
|
|||
};
|
||||
|
||||
let mut holder = ImageHolder::new(image_url.clone(), layout_context.shared.image_cache.clone());
|
||||
let image = match holder.get_image() {
|
||||
let image = match holder.get_image(self.node.to_untrusted_node_address()) {
|
||||
None => {
|
||||
// No image data at all? Do nothing.
|
||||
//
|
||||
|
@ -987,7 +1004,7 @@ impl Fragment {
|
|||
/// * `layout_context`: The layout context.
|
||||
/// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
|
||||
/// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow.
|
||||
pub fn build_display_list(&self,
|
||||
pub fn build_display_list(&mut self,
|
||||
display_list: &mut DisplayList,
|
||||
layout_context: &LayoutContext,
|
||||
flow_origin: Point2D<Au>,
|
||||
|
@ -995,12 +1012,12 @@ impl Fragment {
|
|||
-> ChildDisplayListAccumulator {
|
||||
// FIXME(#2795): Get the real container size
|
||||
let container_size = Size2D::zero();
|
||||
let rect_to_absolute = |logical_rect: LogicalRect<Au>| {
|
||||
let physical_rect = logical_rect.to_physical(self.style.writing_mode, container_size);
|
||||
let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect<Au>| {
|
||||
let physical_rect = logical_rect.to_physical(writing_mode, container_size);
|
||||
Rect(physical_rect.origin + flow_origin, physical_rect.size)
|
||||
};
|
||||
// Fragment position wrt to the owning flow.
|
||||
let absolute_fragment_bounds = rect_to_absolute(self.border_box);
|
||||
let absolute_fragment_bounds = rect_to_absolute(self.style.writing_mode, self.border_box);
|
||||
debug!("Fragment::build_display_list at rel={}, abs={}: {}",
|
||||
self.border_box,
|
||||
absolute_fragment_bounds,
|
||||
|
@ -1088,7 +1105,7 @@ impl Fragment {
|
|||
}
|
||||
|
||||
let content_box = self.content_box();
|
||||
let absolute_content_box = rect_to_absolute(content_box);
|
||||
let absolute_content_box = rect_to_absolute(self.style.writing_mode, content_box);
|
||||
|
||||
// Create special per-fragment-type display items.
|
||||
match self.specific {
|
||||
|
@ -1133,7 +1150,7 @@ impl Fragment {
|
|||
accumulator.push(display_list, SolidColorDisplayItemClass(
|
||||
box SolidColorDisplayItem {
|
||||
base: BaseDisplayItem::new(
|
||||
rect_to_absolute(rect()),
|
||||
rect_to_absolute(self.style.writing_mode, rect()),
|
||||
self.node, ContentStackingLevel),
|
||||
color: color.to_gfx_color(),
|
||||
}
|
||||
|
@ -1182,9 +1199,9 @@ impl Fragment {
|
|||
}
|
||||
ImageFragment(_) => {
|
||||
match self.specific {
|
||||
ImageFragment(ref image_fragment) => {
|
||||
let image_ref = &image_fragment.image;
|
||||
match image_ref.get_image_if_present() {
|
||||
ImageFragment(ref mut image_fragment) => {
|
||||
let image_ref = &mut image_fragment.image;
|
||||
match image_ref.get_image(self.node.to_untrusted_node_address()) {
|
||||
Some(image) => {
|
||||
debug!("(building display list) building image fragment");
|
||||
|
||||
|
@ -1437,7 +1454,7 @@ impl Fragment {
|
|||
let advance = metrics.advance_width;
|
||||
|
||||
let should_continue;
|
||||
if advance <= remaining_inline_size {
|
||||
if advance <= remaining_inline_size || glyphs.is_whitespace() {
|
||||
should_continue = true;
|
||||
|
||||
if starts_line && pieces_processed_count == 0 && glyphs.is_whitespace() {
|
||||
|
@ -1452,21 +1469,8 @@ impl Fragment {
|
|||
// The advance is more than the remaining inline-size.
|
||||
should_continue = false;
|
||||
let slice_begin = offset + slice_range.begin();
|
||||
let slice_end = offset + slice_range.end();
|
||||
|
||||
if glyphs.is_whitespace() {
|
||||
// If there are still things after the trimmable whitespace, create the
|
||||
// inline-end chunk.
|
||||
if slice_end < text_fragment_info.range.end() {
|
||||
debug!("split_to_inline_size: case=skipping trimmable trailing \
|
||||
whitespace, then split remainder");
|
||||
let inline_end_range_end = text_fragment_info.range.end() - slice_end;
|
||||
inline_end_range = Some(Range::new(slice_end, inline_end_range_end));
|
||||
} else {
|
||||
debug!("split_to_inline_size: case=skipping trimmable trailing \
|
||||
whitespace");
|
||||
}
|
||||
} else if slice_begin < text_fragment_info.range.end() {
|
||||
if slice_begin < text_fragment_info.range.end() {
|
||||
// There are still some things inline-start over at the end of the line. Create
|
||||
// the inline-end chunk.
|
||||
let inline_end_range_end = text_fragment_info.range.end() - slice_begin;
|
||||
|
|
|
@ -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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use style::ComputedValues;
|
||||
|
||||
bitflags! {
|
||||
|
@ -35,6 +37,32 @@ impl RestyleDamage {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for RestyleDamage {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::FormatError> {
|
||||
let mut first_elem = true;
|
||||
|
||||
let to_iter =
|
||||
[ (Repaint, "Repaint")
|
||||
, (BubbleISizes, "BubbleISizes")
|
||||
, (Reflow, "Reflow")
|
||||
];
|
||||
|
||||
for &(damage, damage_str) in to_iter.iter() {
|
||||
if self.contains(damage) {
|
||||
if !first_elem { try!(write!(f, " | ")); }
|
||||
try!(write!(f, "{}", damage_str));
|
||||
first_elem = false;
|
||||
}
|
||||
}
|
||||
|
||||
if first_elem {
|
||||
try!(write!(f, "NoDamage"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// NB: We need the braces inside the RHS due to Rust #8012. This particular
|
||||
// version of this macro might be safe anyway, but we want to avoid silent
|
||||
// breakage on modifications.
|
||||
|
@ -47,7 +75,13 @@ macro_rules! add_if_not_equal(
|
|||
})
|
||||
)
|
||||
|
||||
pub fn compute_damage(old: &ComputedValues, new: &ComputedValues) -> RestyleDamage {
|
||||
pub fn compute_damage(old: &Option<Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage {
|
||||
let old: &ComputedValues =
|
||||
match old.as_ref() {
|
||||
None => return Repaint | BubbleISizes | Reflow,
|
||||
Some(cv) => &**cv,
|
||||
};
|
||||
|
||||
let mut damage = RestyleDamage::empty();
|
||||
|
||||
// This checks every CSS property, as enumerated in
|
||||
|
|
|
@ -11,6 +11,7 @@ use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils};
|
|||
use flow;
|
||||
use fragment::{Fragment, InlineBlockFragment, ScannedTextFragment, ScannedTextFragmentInfo};
|
||||
use fragment::{SplitInfo};
|
||||
use incremental;
|
||||
use layout_debug;
|
||||
use model::IntrinsicISizes;
|
||||
use text;
|
||||
|
@ -494,7 +495,7 @@ impl LineBreaker {
|
|||
self.push_fragment_to_line(in_fragment);
|
||||
true
|
||||
} else {
|
||||
debug!("LineBreaker: Found a new-line character, so splitting theline.");
|
||||
debug!("LineBreaker: Found a new-line character, so splitting the line.");
|
||||
|
||||
let (inline_start, inline_end, run) = in_fragment.find_split_info_by_new_line()
|
||||
.expect("LineBreaker: This split case makes no sense!");
|
||||
|
@ -621,7 +622,7 @@ impl LineBreaker {
|
|||
true
|
||||
},
|
||||
Some((None, None)) => {
|
||||
error!("LineBreaker: This split case makes no sense!");
|
||||
debug!("LineBreaker: Nothing to do.");
|
||||
true
|
||||
},
|
||||
}
|
||||
|
@ -763,13 +764,22 @@ pub struct InlineFlow {
|
|||
|
||||
impl InlineFlow {
|
||||
pub fn from_fragments(node: ThreadSafeLayoutNode, fragments: InlineFragments) -> InlineFlow {
|
||||
InlineFlow {
|
||||
let fragment_damage =
|
||||
fragments.fragments.iter().fold(
|
||||
incremental::RestyleDamage::empty(),
|
||||
|dmg, frag| dmg | frag.restyle_damage);
|
||||
|
||||
let mut ret = InlineFlow {
|
||||
base: BaseFlow::new(node),
|
||||
fragments: fragments,
|
||||
lines: Vec::new(),
|
||||
minimum_block_size_above_baseline: Au(0),
|
||||
minimum_depth_below_baseline: Au(0),
|
||||
}
|
||||
};
|
||||
|
||||
ret.base.restyle_damage.insert(fragment_damage);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn build_display_list_inline(&mut self, layout_context: &LayoutContext) {
|
||||
|
@ -1273,4 +1283,3 @@ impl InlineMetrics {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ use flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
|
|||
use flow::{PreorderFlowTraversal, PostorderFlowTraversal};
|
||||
use flow;
|
||||
use flow_ref::FlowRef;
|
||||
use incremental::RestyleDamage;
|
||||
use layout_debug;
|
||||
use parallel::UnsafeFlow;
|
||||
use parallel;
|
||||
|
@ -38,10 +37,11 @@ use script::dom::element::{HTMLBodyElementTypeId, HTMLHtmlElementTypeId};
|
|||
use script::layout_interface::{AddStylesheetMsg, LoadStylesheetMsg, ScriptLayoutChan};
|
||||
use script::layout_interface::{TrustedNodeAddress, ContentBoxesResponse, ExitNowMsg};
|
||||
use script::layout_interface::{ContentBoxResponse, HitTestResponse, MouseOverResponse};
|
||||
use script::layout_interface::{ContentChangedDocumentDamage, LayoutChan, Msg, PrepareToExitMsg};
|
||||
use script::layout_interface::{GetRPCMsg, LayoutRPC, ReapLayoutDataMsg, Reflow, UntrustedNodeAddress};
|
||||
use script::layout_interface::{LayoutChan, Msg, PrepareToExitMsg};
|
||||
use script::layout_interface::{GetRPCMsg, LayoutRPC, ReapLayoutDataMsg, Reflow};
|
||||
use script::layout_interface::{ReflowForDisplay, ReflowMsg};
|
||||
use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel, ScriptControlChan};
|
||||
use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel};
|
||||
use script_traits::{ScriptControlChan, UntrustedNodeAddress};
|
||||
use servo_msg::compositor_msg::Scrollable;
|
||||
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
|
||||
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
|
||||
|
@ -53,7 +53,7 @@ use servo_util::geometry::Au;
|
|||
use servo_util::geometry;
|
||||
use servo_util::logical_geometry::LogicalPoint;
|
||||
use servo_util::opts::Opts;
|
||||
use servo_util::smallvec::{SmallVec, SmallVec1};
|
||||
use servo_util::smallvec::{SmallVec, SmallVec1, VecLike};
|
||||
use servo_util::task::spawn_named_with_send_on_failure;
|
||||
use servo_util::time::{TimeProfilerChan, profile};
|
||||
use servo_util::time;
|
||||
|
@ -63,7 +63,7 @@ use std::comm::{channel, Sender, Receiver, Select};
|
|||
use std::mem;
|
||||
use std::ptr;
|
||||
use style;
|
||||
use style::{AuthorOrigin, Stylesheet, Stylist};
|
||||
use style::{TNode, AuthorOrigin, Stylesheet, Stylist};
|
||||
use style::iter_font_face_rules;
|
||||
use sync::{Arc, Mutex, MutexGuard};
|
||||
use url::Url;
|
||||
|
@ -73,7 +73,7 @@ use url::Url;
|
|||
/// This needs to be protected by a mutex so we can do fast RPCs.
|
||||
pub struct LayoutTaskData {
|
||||
/// The local image cache.
|
||||
pub local_image_cache: Arc<Mutex<LocalImageCache>>,
|
||||
pub local_image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>,
|
||||
|
||||
/// The size of the viewport.
|
||||
pub screen_size: Size2D<Au>,
|
||||
|
@ -92,6 +92,10 @@ pub struct LayoutTaskData {
|
|||
/// Starts at zero, and increased by one every time a layout completes.
|
||||
/// This can be used to easily check for invalid stale data.
|
||||
pub generation: uint,
|
||||
|
||||
/// True if a style sheet was added since the last reflow. Currently, this causes all nodes to
|
||||
/// be dirtied at the next reflow.
|
||||
pub stylesheet_dirty: bool,
|
||||
}
|
||||
|
||||
/// Information needed by the layout task.
|
||||
|
@ -142,47 +146,6 @@ pub struct LayoutTask {
|
|||
pub rw_data: Arc<Mutex<LayoutTaskData>>,
|
||||
}
|
||||
|
||||
/// The damage computation traversal.
|
||||
#[deriving(Clone)]
|
||||
struct ComputeDamageTraversal;
|
||||
|
||||
impl PostorderFlowTraversal for ComputeDamageTraversal {
|
||||
#[inline]
|
||||
fn process(&mut self, flow: &mut Flow) -> bool {
|
||||
let mut damage = flow::base(flow).restyle_damage;
|
||||
for child in flow::child_iter(flow) {
|
||||
damage.insert(flow::base(child).restyle_damage.propagate_up())
|
||||
}
|
||||
flow::mut_base(flow).restyle_damage = damage;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Propagates restyle damage up and down the tree as appropriate.
|
||||
///
|
||||
/// FIXME(pcwalton): Merge this with flow tree building and/or other traversals.
|
||||
struct PropagateDamageTraversal {
|
||||
all_style_damage: bool,
|
||||
}
|
||||
|
||||
impl PreorderFlowTraversal for PropagateDamageTraversal {
|
||||
#[inline]
|
||||
fn process(&mut self, flow: &mut Flow) -> bool {
|
||||
if self.all_style_damage {
|
||||
flow::mut_base(flow).restyle_damage.insert(RestyleDamage::all())
|
||||
}
|
||||
debug!("restyle damage = {:?}", flow::base(flow).restyle_damage);
|
||||
|
||||
let prop = flow::base(flow).restyle_damage.propagate_down();
|
||||
if !prop.is_empty() {
|
||||
for kid_ctx in flow::child_iter(flow) {
|
||||
flow::mut_base(kid_ctx).restyle_damage.insert(prop)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// The flow tree verification traversal. This is only on in debug builds.
|
||||
#[cfg(debug)]
|
||||
struct FlowTreeVerificationTraversal;
|
||||
|
@ -290,14 +253,17 @@ struct LayoutImageResponder {
|
|||
script_chan: ScriptControlChan,
|
||||
}
|
||||
|
||||
impl ImageResponder for LayoutImageResponder {
|
||||
fn respond(&self) -> proc(ImageResponseMsg):Send {
|
||||
impl ImageResponder<UntrustedNodeAddress> for LayoutImageResponder {
|
||||
fn respond(&self) -> proc(ImageResponseMsg, UntrustedNodeAddress):Send {
|
||||
let id = self.id.clone();
|
||||
let script_chan = self.script_chan.clone();
|
||||
let f: proc(ImageResponseMsg):Send = proc(_) {
|
||||
let ScriptControlChan(chan) = script_chan;
|
||||
drop(chan.send_opt(SendEventMsg(id.clone(), ReflowEvent)))
|
||||
};
|
||||
let f: proc(ImageResponseMsg, UntrustedNodeAddress):Send =
|
||||
proc(_, node_address) {
|
||||
let ScriptControlChan(chan) = script_chan;
|
||||
let mut nodes = SmallVec1::new();
|
||||
nodes.vec_push(node_address);
|
||||
drop(chan.send_opt(SendEventMsg(id.clone(), ReflowEvent(nodes))))
|
||||
};
|
||||
f
|
||||
}
|
||||
}
|
||||
|
@ -418,6 +384,7 @@ impl LayoutTask {
|
|||
parallel_traversal: parallel_traversal,
|
||||
dirty: Rect::zero(),
|
||||
generation: 0,
|
||||
stylesheet_dirty: false,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
@ -610,6 +577,7 @@ impl LayoutTask {
|
|||
});
|
||||
let mut rw_data = self.lock_rw_data(possibly_locked_rw_data);
|
||||
rw_data.stylist.add_stylesheet(sheet, AuthorOrigin);
|
||||
rw_data.stylesheet_dirty = true;
|
||||
LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data);
|
||||
}
|
||||
|
||||
|
@ -739,23 +707,13 @@ impl LayoutTask {
|
|||
local_image_cache.next_round(self.make_on_image_available_cb());
|
||||
}
|
||||
|
||||
// true => Do the reflow with full style damage, because content
|
||||
// changed or the window was resized.
|
||||
let mut all_style_damage = match data.damage.level {
|
||||
ContentChangedDocumentDamage => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
// TODO: Calculate the "actual viewport":
|
||||
// http://www.w3.org/TR/css-device-adapt/#actual-viewport
|
||||
let viewport_size = data.window_size.initial_viewport;
|
||||
|
||||
let current_screen_size = Size2D(Au::from_frac32_px(viewport_size.width.get()),
|
||||
Au::from_frac32_px(viewport_size.height.get()));
|
||||
if rw_data.screen_size != current_screen_size {
|
||||
all_style_damage = true
|
||||
}
|
||||
rw_data.screen_size = current_screen_size;
|
||||
let old_screen_size = mem::replace(&mut rw_data.screen_size, current_screen_size);
|
||||
|
||||
// Create a layout context for use throughout the following passes.
|
||||
let mut shared_layout_ctx =
|
||||
|
@ -764,6 +722,20 @@ impl LayoutTask {
|
|||
node,
|
||||
&data.url);
|
||||
|
||||
// Handle conditions where the entire flow tree is invalid.
|
||||
let mut needs_dirtying = false;
|
||||
|
||||
needs_dirtying |= current_screen_size != old_screen_size;
|
||||
needs_dirtying |= rw_data.stylesheet_dirty;
|
||||
|
||||
unsafe {
|
||||
if needs_dirtying {
|
||||
LayoutTask::dirty_all_nodes(node);
|
||||
}
|
||||
}
|
||||
|
||||
rw_data.stylesheet_dirty = false;
|
||||
|
||||
let mut layout_root = profile(time::LayoutStyleRecalcCategory,
|
||||
Some((&data.url,
|
||||
data.iframe,
|
||||
|
@ -804,15 +776,6 @@ impl LayoutTask {
|
|||
layout_root.get_mut().dump();
|
||||
}
|
||||
|
||||
// Propagate damage.
|
||||
profile(time::LayoutDamagePropagateCategory, Some((&data.url, data.iframe, self.first_reflow.get())),
|
||||
self.time_profiler_chan.clone(), || {
|
||||
layout_root.get_mut().traverse_preorder(&mut PropagateDamageTraversal {
|
||||
all_style_damage: all_style_damage
|
||||
});
|
||||
layout_root.get_mut().traverse_postorder(&mut ComputeDamageTraversal.clone());
|
||||
});
|
||||
|
||||
// Perform the primary layout passes over the flow tree to compute the locations of all
|
||||
// the boxes.
|
||||
profile(time::LayoutMainCategory, Some((&data.url, data.iframe, self.first_reflow.get())),
|
||||
|
@ -860,6 +823,9 @@ impl LayoutTask {
|
|||
}
|
||||
}
|
||||
|
||||
debug!("Done building display list. Display List = {}",
|
||||
flow::base(layout_root.get()).display_list);
|
||||
|
||||
let root_display_list =
|
||||
mem::replace(&mut flow::mut_base(layout_root.get_mut()).display_list,
|
||||
DisplayList::new());
|
||||
|
@ -939,13 +905,27 @@ impl LayoutTask {
|
|||
chan.send(ReflowCompleteMsg(self.id, data.id));
|
||||
}
|
||||
|
||||
unsafe fn dirty_all_nodes(node: &mut LayoutNode) {
|
||||
node.set_dirty(true);
|
||||
|
||||
let mut has_children = false;
|
||||
|
||||
for mut kid in node.children() {
|
||||
LayoutTask::dirty_all_nodes(&mut kid);
|
||||
has_children = true;
|
||||
}
|
||||
|
||||
if has_children {
|
||||
node.set_dirty_descendants(true);
|
||||
}
|
||||
}
|
||||
|
||||
// When images can't be loaded in time to display they trigger
|
||||
// this callback in some task somewhere. This will send a message
|
||||
// to the script task, and ultimately cause the image to be
|
||||
// re-requested. We probably don't need to go all the way back to
|
||||
// the script task for this.
|
||||
fn make_on_image_available_cb(&self) -> Box<ImageResponder+Send> {
|
||||
fn make_on_image_available_cb(&self) -> Box<ImageResponder<UntrustedNodeAddress>+Send> {
|
||||
// This has a crazy signature because the image cache needs to
|
||||
// make multiple copies of the callback, and the dom event
|
||||
// channel is not a copyable type, so this is actually a
|
||||
|
@ -953,7 +933,7 @@ impl LayoutTask {
|
|||
box LayoutImageResponder {
|
||||
id: self.id.clone(),
|
||||
script_chan: self.script_chan.clone(),
|
||||
} as Box<ImageResponder+Send>
|
||||
} as Box<ImageResponder<UntrustedNodeAddress>+Send>
|
||||
}
|
||||
|
||||
/// Handles a message to destroy layout data. Layout data must be destroyed on *this* task
|
||||
|
|
|
@ -6,12 +6,14 @@
|
|||
//!
|
||||
//! This code is highly unsafe. Keep this file small and easy to audit.
|
||||
|
||||
use css::node_style::StyledNode;
|
||||
use css::matching::{ApplicableDeclarations, CannotShare, MatchMethods, StyleWasShared};
|
||||
use construct::FlowConstructor;
|
||||
use context::{LayoutContext, SharedLayoutContext};
|
||||
use flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal};
|
||||
use flow;
|
||||
use flow_ref::FlowRef;
|
||||
use incremental::RestyleDamage;
|
||||
use layout_task::{AssignBSizesAndStoreOverflowTraversal, AssignISizesTraversal};
|
||||
use layout_task::{BubbleISizesTraversal};
|
||||
use url::Url;
|
||||
|
@ -179,8 +181,10 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal {
|
|||
// Get a real flow.
|
||||
let flow: &mut FlowRef = mem::transmute(&unsafe_flow);
|
||||
|
||||
// Perform the appropriate traversal.
|
||||
self.process(flow.get_mut());
|
||||
if self.should_process(flow.get_mut()) {
|
||||
// Perform the appropriate traversal.
|
||||
self.process(flow.get_mut());
|
||||
}
|
||||
|
||||
// Possibly enqueue the children.
|
||||
for kid in flow::child_iter(flow.get_mut()) {
|
||||
|
@ -296,7 +300,7 @@ fn insert_ancestors_into_bloom_filter(
|
|||
ancestors += 1;
|
||||
|
||||
n.insert_into_bloom_filter(bf);
|
||||
n = match parent_node(&n, layout_context) {
|
||||
n = match n.layout_parent_node(layout_context.shared) {
|
||||
None => break,
|
||||
Some(p) => p,
|
||||
};
|
||||
|
@ -304,15 +308,6 @@ fn insert_ancestors_into_bloom_filter(
|
|||
debug!("[{}] Inserted {} ancestors.", tid(), ancestors);
|
||||
}
|
||||
|
||||
fn parent_node<'ln>(node: &LayoutNode<'ln>, layout_context: &LayoutContext) -> Option<LayoutNode<'ln>> {
|
||||
let opaque_node: OpaqueNode = OpaqueNodeMethods::from_layout_node(node);
|
||||
if opaque_node == layout_context.shared.reflow_root {
|
||||
None
|
||||
} else {
|
||||
node.parent_node()
|
||||
}
|
||||
}
|
||||
|
||||
fn recalc_style_for_node(mut unsafe_layout_node: UnsafeLayoutNode,
|
||||
proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeLayoutNode>) {
|
||||
let shared_layout_context = unsafe { &**proxy.user_data() };
|
||||
|
@ -330,45 +325,46 @@ fn recalc_style_for_node(mut unsafe_layout_node: UnsafeLayoutNode,
|
|||
node.initialize_layout_data(layout_context.shared.layout_chan.clone());
|
||||
|
||||
// Get the parent node.
|
||||
let parent_opt = parent_node(&node, &layout_context);
|
||||
let parent_opt = node.layout_parent_node(layout_context.shared);
|
||||
|
||||
// Get the style bloom filter.
|
||||
let bf = take_task_local_bloom_filter(parent_opt, &layout_context);
|
||||
|
||||
// First, check to see whether we can share a style with someone.
|
||||
let style_sharing_candidate_cache = layout_context.style_sharing_candidate_cache();
|
||||
let sharing_result = unsafe {
|
||||
node.share_style_if_possible(style_sharing_candidate_cache,
|
||||
parent_opt.clone())
|
||||
};
|
||||
|
||||
// Just needs to be wrapped in an option for `match_node`.
|
||||
let some_bf = Some(bf);
|
||||
|
||||
// Otherwise, match and cascade selectors.
|
||||
match sharing_result {
|
||||
CannotShare(mut shareable) => {
|
||||
let mut applicable_declarations = ApplicableDeclarations::new();
|
||||
if node.is_dirty() {
|
||||
// First, check to see whether we can share a style with someone.
|
||||
let style_sharing_candidate_cache = layout_context.style_sharing_candidate_cache();
|
||||
let sharing_result = unsafe {
|
||||
node.share_style_if_possible(style_sharing_candidate_cache,
|
||||
parent_opt.clone())
|
||||
};
|
||||
// Otherwise, match and cascade selectors.
|
||||
match sharing_result {
|
||||
CannotShare(mut shareable) => {
|
||||
let mut applicable_declarations = ApplicableDeclarations::new();
|
||||
|
||||
if node.is_element() {
|
||||
// Perform the CSS selector matching.
|
||||
let stylist = unsafe { &*layout_context.shared.stylist };
|
||||
node.match_node(stylist, &some_bf, &mut applicable_declarations, &mut shareable);
|
||||
}
|
||||
if node.is_element() {
|
||||
// Perform the CSS selector matching.
|
||||
let stylist = unsafe { &*layout_context.shared.stylist };
|
||||
node.match_node(stylist, &some_bf, &mut applicable_declarations, &mut shareable);
|
||||
}
|
||||
|
||||
// Perform the CSS cascade.
|
||||
unsafe {
|
||||
node.cascade_node(parent_opt,
|
||||
&applicable_declarations,
|
||||
layout_context.applicable_declarations_cache());
|
||||
}
|
||||
// Perform the CSS cascade.
|
||||
unsafe {
|
||||
node.cascade_node(parent_opt,
|
||||
&applicable_declarations,
|
||||
layout_context.applicable_declarations_cache());
|
||||
}
|
||||
|
||||
// Add ourselves to the LRU cache.
|
||||
if shareable {
|
||||
style_sharing_candidate_cache.insert_if_possible(&node);
|
||||
// Add ourselves to the LRU cache.
|
||||
if shareable {
|
||||
style_sharing_candidate_cache.insert_if_possible(&node);
|
||||
}
|
||||
}
|
||||
StyleWasShared(index) => style_sharing_candidate_cache.touch(index),
|
||||
}
|
||||
StyleWasShared(index) => style_sharing_candidate_cache.touch(index),
|
||||
}
|
||||
|
||||
// Prepare for flow construction by counting the node's children and storing that count.
|
||||
|
@ -427,8 +423,18 @@ fn construct_flows<'a>(unsafe_layout_node: &mut UnsafeLayoutNode,
|
|||
|
||||
// Construct flows for this node.
|
||||
{
|
||||
let node = ThreadSafeLayoutNode::new(&node);
|
||||
let mut flow_constructor = FlowConstructor::new(layout_context);
|
||||
flow_constructor.process(&ThreadSafeLayoutNode::new(&node));
|
||||
flow_constructor.process(&node);
|
||||
|
||||
// Reset the layout damage in this node. It's been propagated to the
|
||||
// flow by the flow constructor.
|
||||
node.set_restyle_damage(RestyleDamage::empty());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
node.set_dirty(false);
|
||||
node.set_dirty_descendants(false);
|
||||
}
|
||||
|
||||
// Reset the count of children for the next traversal.
|
||||
|
|
|
@ -13,7 +13,8 @@ use libc::uintptr_t;
|
|||
use script::dom::bindings::js::JS;
|
||||
use script::dom::bindings::utils::Reflectable;
|
||||
use script::dom::node::{Node, SharedLayoutData};
|
||||
use script::layout_interface::{LayoutChan, UntrustedNodeAddress, TrustedNodeAddress};
|
||||
use script::layout_interface::{LayoutChan, TrustedNodeAddress};
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
use std::mem;
|
||||
use std::cell::{Ref, RefMut};
|
||||
use style::ComputedValues;
|
||||
|
@ -29,7 +30,7 @@ pub struct PrivateLayoutData {
|
|||
pub after_style: Option<Arc<ComputedValues>>,
|
||||
|
||||
/// Description of how to account for recent style changes.
|
||||
pub restyle_damage: Option<RestyleDamage>,
|
||||
pub restyle_damage: RestyleDamage,
|
||||
|
||||
/// The current results of flow construction for this node. This is either a flow or a
|
||||
/// `ConstructionItem`. See comments in `construct.rs` for more details.
|
||||
|
@ -49,7 +50,7 @@ impl PrivateLayoutData {
|
|||
PrivateLayoutData {
|
||||
before_style: None,
|
||||
after_style: None,
|
||||
restyle_damage: None,
|
||||
restyle_damage: RestyleDamage::empty(),
|
||||
flow_construction_result: NoConstructionResult,
|
||||
before_flow_construction_result: NoConstructionResult,
|
||||
after_flow_construction_result: NoConstructionResult,
|
||||
|
@ -161,4 +162,3 @@ impl ToGfxColor for style::computed_values::RGBA {
|
|||
gfx::color::rgba(self.red, self.green, self.blue, self.alpha)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,9 +33,11 @@
|
|||
//! o Instead of `html_element_in_html_document()`, use
|
||||
//! `html_element_in_html_document_for_layout()`.
|
||||
|
||||
use context::SharedLayoutContext;
|
||||
use css::node_style::StyledNode;
|
||||
use util::{LayoutDataAccess, LayoutDataWrapper, PrivateLayoutData};
|
||||
use util::{LayoutDataAccess, LayoutDataWrapper, PrivateLayoutData, OpaqueNodeMethods};
|
||||
|
||||
use gfx::display_list::OpaqueNode;
|
||||
use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived, HTMLInputElementDerived};
|
||||
use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived};
|
||||
use script::dom::bindings::js::JS;
|
||||
|
@ -46,6 +48,7 @@ use script::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelp
|
|||
use script::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
|
||||
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId};
|
||||
use script::dom::node::{LayoutNodeHelpers, RawLayoutNodeHelpers, SharedLayoutData, TextNodeTypeId};
|
||||
use script::dom::node::{IsDirty, HasDirtyDescendants};
|
||||
use script::dom::text::Text;
|
||||
use script::layout_interface::LayoutChan;
|
||||
use servo_msg::constellation_msg::{PipelineId, SubpageId};
|
||||
|
@ -158,7 +161,6 @@ impl<'a> PartialEq for LayoutNode<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'ln> TLayoutNode for LayoutNode<'ln> {
|
||||
unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> LayoutNode<'ln> {
|
||||
LayoutNode {
|
||||
|
@ -249,6 +251,21 @@ impl<'ln> LayoutNode<'ln> {
|
|||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_children(&self) -> bool {
|
||||
self.first_child().is_some()
|
||||
}
|
||||
|
||||
/// While doing a reflow, the node at the root has no parent, as far as we're
|
||||
/// concerned. This method returns `None` at the reflow root.
|
||||
pub fn layout_parent_node(&self, shared: &SharedLayoutContext) -> Option<LayoutNode<'ln>> {
|
||||
let opaque_node: OpaqueNode = OpaqueNodeMethods::from_layout_node(self);
|
||||
if opaque_node == shared.reflow_root {
|
||||
None
|
||||
} else {
|
||||
self.parent_node()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ln> TNode<'ln, LayoutElement<'ln>> for LayoutNode<'ln> {
|
||||
|
@ -325,6 +342,22 @@ impl<'ln> TNode<'ln, LayoutElement<'ln>> for LayoutNode<'ln> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_dirty(self) -> bool {
|
||||
unsafe { self.node.get_flag(IsDirty) }
|
||||
}
|
||||
|
||||
unsafe fn set_dirty(self, value: bool) {
|
||||
self.node.set_flag(IsDirty, value)
|
||||
}
|
||||
|
||||
fn has_dirty_descendants(self) -> bool {
|
||||
unsafe { self.node.get_flag(HasDirtyDescendants) }
|
||||
}
|
||||
|
||||
unsafe fn set_dirty_descendants(self, value: bool) {
|
||||
self.node.set_flag(HasDirtyDescendants, value)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LayoutNodeChildrenIterator<'a> {
|
||||
|
@ -855,4 +888,3 @@ pub unsafe fn layout_node_from_unsafe_layout_node(node: &UnsafeLayoutNode) -> La
|
|||
let (node, _) = *node;
|
||||
mem::transmute(node)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue