servo: Merge #3590 - Incremental Style Recalc (from cgaebel:slow-incremental-reflow-rebase); r=pcwalton

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

Source-Repo: https://github.com/servo/servo
Source-Revision: 85b277655f07db1cb99c4d3dee93804735ed0470
This commit is contained in:
Clark Gaebel 2014-10-09 11:21:32 -06:00
Родитель fc4bdae442
Коммит e1fa32f008
31 изменённых файлов: 566 добавлений и 398 удалений

1
servo/Cargo.lock сгенерированный
Просмотреть файл

@ -416,6 +416,7 @@ dependencies = [
"msg 0.0.1", "msg 0.0.1",
"net 0.0.1", "net 0.0.1",
"url 0.1.0 (git+https://github.com/servo/rust-url#29f70a47230c2aa736e263977247c786e0b2c243)", "url 0.1.0 (git+https://github.com/servo/rust-url#29f70a47230c2aa736e263977247c786e0b2c243)",
"util 0.0.1",
] ]
[[package]] [[package]]

Просмотреть файл

@ -293,7 +293,7 @@ pub enum BackgroundAndBorderLevel {
} }
/// A list of rendering operations to be performed. /// A list of rendering operations to be performed.
#[deriving(Clone)] #[deriving(Clone, Show)]
pub struct DisplayList { pub struct DisplayList {
pub list: DList<DisplayItem>, pub list: DList<DisplayItem>,
} }
@ -357,8 +357,9 @@ impl DisplayList {
} }
/// Returns true if this list is empty and false otherwise. /// Returns true if this list is empty and false otherwise.
#[inline]
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
self.list.len() == 0 self.list.is_empty()
} }
/// Flattens a display list into a display list with a single stacking level according to the /// Flattens a display list into a display list with a single stacking level according to the

Просмотреть файл

@ -607,8 +607,7 @@ impl<'a> FlowConstructor<'a> {
abs_descendants.push_descendants(kid_abs_descendants); abs_descendants.push_descendants(kid_abs_descendants);
} }
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node, ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
whitespace_style)) whitespace_style)) => {
=> {
// Instantiate the whitespace fragment. // Instantiate the whitespace fragment.
let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string())); let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node, let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node,
@ -1196,12 +1195,13 @@ impl FlowConstructionUtils for FlowRef {
/// ///
/// This must not be public because only the layout constructor can do this. /// This must not be public because only the layout constructor can do this.
fn add_new_child(&mut self, mut new_child: FlowRef) { 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()); let kid_base = flow::mut_base(new_child.get_mut());
kid_base.parallel.parent = parallel::mut_owned_flow_to_unsafe_flow(self); 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); base.children.push_back(new_child);
let _ = base.parallel.children_count.fetch_add(1, Relaxed); let _ = base.parallel.children_count.fetch_add(1, Relaxed);
let _ = base.parallel.children_and_absolute_descendant_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_context::FontContext;
use gfx::font_cache_task::FontCacheTask; use gfx::font_cache_task::FontCacheTask;
use script::layout_interface::LayoutChan; use script::layout_interface::LayoutChan;
use script_traits::UntrustedNodeAddress;
use servo_msg::constellation_msg::ConstellationChan; use servo_msg::constellation_msg::ConstellationChan;
use servo_net::local_image_cache::LocalImageCache; use servo_net::local_image_cache::LocalImageCache;
use servo_util::geometry::Au; use servo_util::geometry::Au;
@ -49,7 +50,7 @@ fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext) -> *
pub struct SharedLayoutContext { pub struct SharedLayoutContext {
/// The local image cache. /// The local image cache.
pub image_cache: Arc<Mutex<LocalImageCache>>, pub image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>,
/// The current screen size. /// The current screen size.
pub screen_size: Size2D<Au>, pub screen_size: Size2D<Au>,

Просмотреть файл

@ -469,7 +469,9 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
Some(shared_style) => { Some(shared_style) => {
// Yay, cache hit. Share the style. // Yay, cache hit. Share the style.
let mut layout_data_ref = self.mutate_layout_data(); let mut layout_data_ref = self.mutate_layout_data();
layout_data_ref.as_mut().unwrap().shared_data.style = Some(shared_style); let shared_data = &mut layout_data_ref.as_mut().unwrap().shared_data;
let style = &mut shared_data.style;
*style = Some(shared_style);
return StyleWasShared(i) return StyleWasShared(i)
} }
None => {} None => {}
@ -632,24 +634,27 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
layout_data.shared_data.style = Some(cloned_parent_style); layout_data.shared_data.style = Some(cloned_parent_style);
} }
_ => { _ => {
self.cascade_node_pseudo_element(parent_style, self.cascade_node_pseudo_element(
applicable_declarations.normal.as_slice(), parent_style,
&mut layout_data.shared_data.style, applicable_declarations.normal.as_slice(),
applicable_declarations_cache, &mut layout_data.shared_data.style,
applicable_declarations.normal_shareable); applicable_declarations_cache,
applicable_declarations.normal_shareable);
if applicable_declarations.before.len() > 0 { if applicable_declarations.before.len() > 0 {
self.cascade_node_pseudo_element(Some(layout_data.shared_data.style.as_ref().unwrap()), self.cascade_node_pseudo_element(
applicable_declarations.before.as_slice(), Some(layout_data.shared_data.style.as_ref().unwrap()),
&mut layout_data.data.before_style, applicable_declarations.before.as_slice(),
applicable_declarations_cache, &mut layout_data.data.before_style,
false); applicable_declarations_cache,
false);
} }
if applicable_declarations.after.len() > 0 { if applicable_declarations.after.len() > 0 {
self.cascade_node_pseudo_element(Some(layout_data.shared_data.style.as_ref().unwrap()), self.cascade_node_pseudo_element(
applicable_declarations.after.as_slice(), Some(layout_data.shared_data.style.as_ref().unwrap()),
&mut layout_data.data.after_style, applicable_declarations.after.as_slice(),
applicable_declarations_cache, &mut layout_data.data.after_style,
false); applicable_declarations_cache,
false);
} }
} }
} }

Просмотреть файл

@ -14,7 +14,8 @@ use sync::Arc;
/// Node mixin providing `style` method that returns a `NodeStyle` /// Node mixin providing `style` method that returns a `NodeStyle`
pub trait StyledNode { pub trait StyledNode {
fn style<'a>(&'a self) -> &'a Arc<ComputedValues>; 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> { impl<'ln> StyledNode for ThreadSafeLayoutNode<'ln> {
@ -23,8 +24,14 @@ impl<'ln> StyledNode for ThreadSafeLayoutNode<'ln> {
self.get_css_select_results() self.get_css_select_results()
} }
fn restyle_damage(&self) -> RestyleDamage { fn restyle_damage(self) -> RestyleDamage {
self.get_restyle_damage() 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 incremental::RestyleDamage;
use util::LayoutDataAccess; use util::LayoutDataAccess;
use wrapper::{TLayoutNode, ThreadSafeLayoutNode}; use wrapper::ThreadSafeLayoutNode;
use wrapper::{After, Before, Normal}; use wrapper::{After, Before, Normal};
use std::mem; use std::mem;
use style::ComputedValues; use style::ComputedValues;
@ -14,8 +14,8 @@ pub trait NodeUtil {
fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues>; fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues>;
fn have_css_select_results(&self) -> bool; fn have_css_select_results(&self) -> bool;
fn get_restyle_damage(&self) -> RestyleDamage; fn get_restyle_damage(self) -> RestyleDamage;
fn set_restyle_damage(&self, damage: RestyleDamage); fn set_restyle_damage(self, damage: RestyleDamage);
} }
impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> { 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. /// Get the description of how to account for recent style changes.
/// This is a simple bitfield and fine to copy by value. /// This is a simple bitfield and fine to copy by value.
fn get_restyle_damage(&self) -> RestyleDamage { 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()
};
let layout_data_ref = self.borrow_layout_data(); let layout_data_ref = self.borrow_layout_data();
layout_data_ref layout_data_ref
.as_ref().unwrap() .as_ref().unwrap()
.data .data
.restyle_damage .restyle_damage
.unwrap_or(default)
} }
/// Set the restyle damage field. /// 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(); let mut layout_data_ref = self.mutate_layout_data();
match &mut *layout_data_ref { 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"), _ => 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 /// 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 /// 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.) /// floats have no effect. (Generally this is the containing block inline-size.)
@ -437,4 +441,3 @@ impl Floats {
clearance clearance
} }
} }

Просмотреть файл

@ -471,6 +471,13 @@ pub trait PreorderFlowTraversal {
/// The operation to perform. Return true to continue or false to stop. /// The operation to perform. Return true to continue or false to stop.
fn process(&mut self, flow: &mut Flow) -> bool; 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 /// 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 /// entirely and do not process any descendant nodes. This is called *before* child nodes are
/// visited. The default implementation never prunes any nodes. /// visited. The default implementation never prunes any nodes.
@ -1031,6 +1038,10 @@ impl<'a> MutableFlowUtils for &'a mut Flow + 'a {
return true return true
} }
if !traversal.should_process(self) {
return true
}
if !traversal.process(self) { if !traversal.process(self) {
return false return false
} }
@ -1240,4 +1251,3 @@ impl ContainingBlockLink {
} }
} }
} }

Просмотреть файл

@ -35,13 +35,14 @@ use gfx::display_list::{Upright, SidewaysLeft, SidewaysRight};
use gfx::font::FontStyle; use gfx::font::FontStyle;
use gfx::text::glyph::CharIndex; use gfx::text::glyph::CharIndex;
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use script_traits::UntrustedNodeAddress;
use serialize::{Encodable, Encoder}; use serialize::{Encodable, Encoder};
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId}; use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId};
use servo_net::image::holder::ImageHolder; use servo_net::image::holder::ImageHolder;
use servo_net::local_image_cache::LocalImageCache; use servo_net::local_image_cache::LocalImageCache;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry; 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::range::*;
use servo_util::smallvec::SmallVec; use servo_util::smallvec::SmallVec;
use servo_util::str::is_whitespace; use servo_util::str::is_whitespace;
@ -202,7 +203,8 @@ impl InputFragmentInfo {
#[deriving(Clone)] #[deriving(Clone)]
pub struct ImageFragmentInfo { pub struct ImageFragmentInfo {
/// The image held within this fragment. /// 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_inline_size: Option<Au>,
pub computed_block_size: Option<Au>, pub computed_block_size: Option<Au>,
pub dom_inline_size: Option<Au>, pub dom_inline_size: Option<Au>,
@ -217,7 +219,7 @@ impl ImageFragmentInfo {
/// me. /// me.
pub fn new(node: &ThreadSafeLayoutNode, pub fn new(node: &ThreadSafeLayoutNode,
image_url: Url, image_url: Url,
local_image_cache: Arc<Mutex<LocalImageCache>>) local_image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>)
-> ImageFragmentInfo { -> ImageFragmentInfo {
fn convert_length(node: &ThreadSafeLayoutNode, name: &str) -> Option<Au> { fn convert_length(node: &ThreadSafeLayoutNode, name: &str) -> Option<Au> {
let element = node.as_element(); let element = node.as_element();
@ -230,8 +232,13 @@ impl ImageFragmentInfo {
let is_vertical = node.style().writing_mode.is_vertical(); let is_vertical = node.style().writing_mode.is_vertical();
let dom_width = convert_length(node, "width"); let dom_width = convert_length(node, "width");
let dom_height = convert_length(node, "height"); 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 { ImageFragmentInfo {
image: ImageHolder::new(image_url, local_image_cache), image: ImageHolder::new(image_url, local_image_cache),
for_node: untrusted_node,
computed_inline_size: None, computed_inline_size: None,
computed_block_size: None, computed_block_size: None,
dom_inline_size: if is_vertical { dom_height } else { dom_width }, dom_inline_size: if is_vertical { dom_height } else { dom_width },
@ -252,13 +259,13 @@ impl ImageFragmentInfo {
/// Returns the original inline-size of the image. /// Returns the original inline-size of the image.
pub fn image_inline_size(&mut self) -> Au { 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 }) Au::from_px(if self.writing_mode_is_vertical { size.height } else { size.width })
} }
/// Returns the original block-size of the image. /// Returns the original block-size of the image.
pub fn image_block_size(&mut self) -> Au { 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 }) Au::from_px(if self.writing_mode_is_vertical { size.width } else { size.height })
} }
@ -805,7 +812,7 @@ impl Fragment {
}; };
let mut holder = ImageHolder::new(image_url.clone(), layout_context.shared.image_cache.clone()); 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 => { None => {
// No image data at all? Do nothing. // No image data at all? Do nothing.
// //
@ -987,7 +994,7 @@ impl Fragment {
/// * `layout_context`: The layout context. /// * `layout_context`: The layout context.
/// * `dirty`: The dirty rectangle in the coordinate system of the owning flow. /// * `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. /// * `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, display_list: &mut DisplayList,
layout_context: &LayoutContext, layout_context: &LayoutContext,
flow_origin: Point2D<Au>, flow_origin: Point2D<Au>,
@ -995,12 +1002,12 @@ impl Fragment {
-> ChildDisplayListAccumulator { -> ChildDisplayListAccumulator {
// FIXME(#2795): Get the real container size // FIXME(#2795): Get the real container size
let container_size = Size2D::zero(); let container_size = Size2D::zero();
let rect_to_absolute = |logical_rect: LogicalRect<Au>| { let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect<Au>| {
let physical_rect = logical_rect.to_physical(self.style.writing_mode, container_size); let physical_rect = logical_rect.to_physical(writing_mode, container_size);
Rect(physical_rect.origin + flow_origin, physical_rect.size) Rect(physical_rect.origin + flow_origin, physical_rect.size)
}; };
// Fragment position wrt to the owning flow. // 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={}: {}", debug!("Fragment::build_display_list at rel={}, abs={}: {}",
self.border_box, self.border_box,
absolute_fragment_bounds, absolute_fragment_bounds,
@ -1088,7 +1095,7 @@ impl Fragment {
} }
let content_box = self.content_box(); 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. // Create special per-fragment-type display items.
match self.specific { match self.specific {
@ -1133,7 +1140,7 @@ impl Fragment {
accumulator.push(display_list, SolidColorDisplayItemClass( accumulator.push(display_list, SolidColorDisplayItemClass(
box SolidColorDisplayItem { box SolidColorDisplayItem {
base: BaseDisplayItem::new( base: BaseDisplayItem::new(
rect_to_absolute(rect()), rect_to_absolute(self.style.writing_mode, rect()),
self.node, ContentStackingLevel), self.node, ContentStackingLevel),
color: color.to_gfx_color(), color: color.to_gfx_color(),
} }
@ -1182,9 +1189,9 @@ impl Fragment {
} }
ImageFragment(_) => { ImageFragment(_) => {
match self.specific { match self.specific {
ImageFragment(ref image_fragment) => { ImageFragment(ref mut image_fragment) => {
let image_ref = &image_fragment.image; let image_ref = &mut image_fragment.image;
match image_ref.get_image_if_present() { match image_ref.get_image(self.node.to_untrusted_node_address()) {
Some(image) => { Some(image) => {
debug!("(building display list) building image fragment"); debug!("(building display list) building image fragment");
@ -1437,7 +1444,7 @@ impl Fragment {
let advance = metrics.advance_width; let advance = metrics.advance_width;
let should_continue; let should_continue;
if advance <= remaining_inline_size { if advance <= remaining_inline_size || glyphs.is_whitespace() {
should_continue = true; should_continue = true;
if starts_line && pieces_processed_count == 0 && glyphs.is_whitespace() { if starts_line && pieces_processed_count == 0 && glyphs.is_whitespace() {
@ -1452,21 +1459,8 @@ impl Fragment {
// The advance is more than the remaining inline-size. // The advance is more than the remaining inline-size.
should_continue = false; should_continue = false;
let slice_begin = offset + slice_range.begin(); let slice_begin = offset + slice_range.begin();
let slice_end = offset + slice_range.end();
if glyphs.is_whitespace() { if slice_begin < text_fragment_info.range.end() {
// 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() {
// There are still some things inline-start over at the end of the line. Create // There are still some things inline-start over at the end of the line. Create
// the inline-end chunk. // the inline-end chunk.
let inline_end_range_end = text_fragment_info.range.end() - slice_begin; 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::fmt;
use std::sync::Arc;
use style::ComputedValues; use style::ComputedValues;
bitflags! { 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 // 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 // version of this macro might be safe anyway, but we want to avoid silent
// breakage on modifications. // 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(); let mut damage = RestyleDamage::empty();
// This checks every CSS property, as enumerated in // This checks every CSS property, as enumerated in

Просмотреть файл

@ -494,7 +494,7 @@ impl LineBreaker {
self.push_fragment_to_line(in_fragment); self.push_fragment_to_line(in_fragment);
true true
} else { } 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() let (inline_start, inline_end, run) = in_fragment.find_split_info_by_new_line()
.expect("LineBreaker: This split case makes no sense!"); .expect("LineBreaker: This split case makes no sense!");
@ -621,7 +621,7 @@ impl LineBreaker {
true true
}, },
Some((None, None)) => { Some((None, None)) => {
error!("LineBreaker: This split case makes no sense!"); debug!("LineBreaker: Nothing to do.");
true true
}, },
} }
@ -1273,4 +1273,3 @@ impl InlineMetrics {
} }
} }
} }

Просмотреть файл

@ -13,7 +13,6 @@ use flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
use flow::{PreorderFlowTraversal, PostorderFlowTraversal}; use flow::{PreorderFlowTraversal, PostorderFlowTraversal};
use flow; use flow;
use flow_ref::FlowRef; use flow_ref::FlowRef;
use incremental::RestyleDamage;
use layout_debug; use layout_debug;
use parallel::UnsafeFlow; use parallel::UnsafeFlow;
use parallel; use parallel;
@ -38,10 +37,11 @@ use script::dom::element::{HTMLBodyElementTypeId, HTMLHtmlElementTypeId};
use script::layout_interface::{AddStylesheetMsg, LoadStylesheetMsg, ScriptLayoutChan}; use script::layout_interface::{AddStylesheetMsg, LoadStylesheetMsg, ScriptLayoutChan};
use script::layout_interface::{TrustedNodeAddress, ContentBoxesResponse, ExitNowMsg}; use script::layout_interface::{TrustedNodeAddress, ContentBoxesResponse, ExitNowMsg};
use script::layout_interface::{ContentBoxResponse, HitTestResponse, MouseOverResponse}; use script::layout_interface::{ContentBoxResponse, HitTestResponse, MouseOverResponse};
use script::layout_interface::{ContentChangedDocumentDamage, LayoutChan, Msg, PrepareToExitMsg}; use script::layout_interface::{LayoutChan, Msg, PrepareToExitMsg};
use script::layout_interface::{GetRPCMsg, LayoutRPC, ReapLayoutDataMsg, Reflow, UntrustedNodeAddress}; use script::layout_interface::{GetRPCMsg, LayoutRPC, ReapLayoutDataMsg, Reflow};
use script::layout_interface::{ReflowForDisplay, ReflowMsg}; 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::compositor_msg::Scrollable;
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
@ -53,7 +53,7 @@ use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
use servo_util::logical_geometry::LogicalPoint; use servo_util::logical_geometry::LogicalPoint;
use servo_util::opts::Opts; 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::task::spawn_named_with_send_on_failure;
use servo_util::time::{TimeProfilerChan, profile}; use servo_util::time::{TimeProfilerChan, profile};
use servo_util::time; use servo_util::time;
@ -63,7 +63,7 @@ use std::comm::{channel, Sender, Receiver, Select};
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use style; use style;
use style::{AuthorOrigin, Stylesheet, Stylist}; use style::{TNode, AuthorOrigin, Stylesheet, Stylist};
use style::iter_font_face_rules; use style::iter_font_face_rules;
use sync::{Arc, Mutex, MutexGuard}; use sync::{Arc, Mutex, MutexGuard};
use url::Url; use url::Url;
@ -73,7 +73,7 @@ use url::Url;
/// This needs to be protected by a mutex so we can do fast RPCs. /// This needs to be protected by a mutex so we can do fast RPCs.
pub struct LayoutTaskData { pub struct LayoutTaskData {
/// The local image cache. /// The local image cache.
pub local_image_cache: Arc<Mutex<LocalImageCache>>, pub local_image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>,
/// The size of the viewport. /// The size of the viewport.
pub screen_size: Size2D<Au>, pub screen_size: Size2D<Au>,
@ -92,6 +92,10 @@ pub struct LayoutTaskData {
/// Starts at zero, and increased by one every time a layout completes. /// Starts at zero, and increased by one every time a layout completes.
/// This can be used to easily check for invalid stale data. /// This can be used to easily check for invalid stale data.
pub generation: uint, 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. /// Information needed by the layout task.
@ -142,47 +146,6 @@ pub struct LayoutTask {
pub rw_data: Arc<Mutex<LayoutTaskData>>, 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. /// The flow tree verification traversal. This is only on in debug builds.
#[cfg(debug)] #[cfg(debug)]
struct FlowTreeVerificationTraversal; struct FlowTreeVerificationTraversal;
@ -290,14 +253,17 @@ struct LayoutImageResponder {
script_chan: ScriptControlChan, script_chan: ScriptControlChan,
} }
impl ImageResponder for LayoutImageResponder { impl ImageResponder<UntrustedNodeAddress> for LayoutImageResponder {
fn respond(&self) -> proc(ImageResponseMsg):Send { fn respond(&self) -> proc(ImageResponseMsg, UntrustedNodeAddress):Send {
let id = self.id.clone(); let id = self.id.clone();
let script_chan = self.script_chan.clone(); let script_chan = self.script_chan.clone();
let f: proc(ImageResponseMsg):Send = proc(_) { let f: proc(ImageResponseMsg, UntrustedNodeAddress):Send =
let ScriptControlChan(chan) = script_chan; proc(_, node_address) {
drop(chan.send_opt(SendEventMsg(id.clone(), ReflowEvent))) 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 f
} }
} }
@ -418,6 +384,7 @@ impl LayoutTask {
parallel_traversal: parallel_traversal, parallel_traversal: parallel_traversal,
dirty: Rect::zero(), dirty: Rect::zero(),
generation: 0, generation: 0,
stylesheet_dirty: false,
})), })),
} }
} }
@ -610,6 +577,7 @@ impl LayoutTask {
}); });
let mut rw_data = self.lock_rw_data(possibly_locked_rw_data); let mut rw_data = self.lock_rw_data(possibly_locked_rw_data);
rw_data.stylist.add_stylesheet(sheet, AuthorOrigin); rw_data.stylist.add_stylesheet(sheet, AuthorOrigin);
rw_data.stylesheet_dirty = true;
LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data); 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()); 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": // TODO: Calculate the "actual viewport":
// http://www.w3.org/TR/css-device-adapt/#actual-viewport // http://www.w3.org/TR/css-device-adapt/#actual-viewport
let viewport_size = data.window_size.initial_viewport; let viewport_size = data.window_size.initial_viewport;
let current_screen_size = Size2D(Au::from_frac32_px(viewport_size.width.get()), let current_screen_size = Size2D(Au::from_frac32_px(viewport_size.width.get()),
Au::from_frac32_px(viewport_size.height.get())); Au::from_frac32_px(viewport_size.height.get()));
if rw_data.screen_size != current_screen_size { let old_screen_size = mem::replace(&mut rw_data.screen_size, current_screen_size);
all_style_damage = true
}
rw_data.screen_size = current_screen_size;
// Create a layout context for use throughout the following passes. // Create a layout context for use throughout the following passes.
let mut shared_layout_ctx = let mut shared_layout_ctx =
@ -764,6 +722,20 @@ impl LayoutTask {
node, node,
&data.url); &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, let mut layout_root = profile(time::LayoutStyleRecalcCategory,
Some((&data.url, Some((&data.url,
data.iframe, data.iframe,
@ -804,15 +776,6 @@ impl LayoutTask {
layout_root.get_mut().dump(); 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 // Perform the primary layout passes over the flow tree to compute the locations of all
// the boxes. // the boxes.
profile(time::LayoutMainCategory, Some((&data.url, data.iframe, self.first_reflow.get())), 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 = let root_display_list =
mem::replace(&mut flow::mut_base(layout_root.get_mut()).display_list, mem::replace(&mut flow::mut_base(layout_root.get_mut()).display_list,
DisplayList::new()); DisplayList::new());
@ -939,13 +905,27 @@ impl LayoutTask {
chan.send(ReflowCompleteMsg(self.id, data.id)); 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 // When images can't be loaded in time to display they trigger
// this callback in some task somewhere. This will send a message // this callback in some task somewhere. This will send a message
// to the script task, and ultimately cause the image to be // 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 // re-requested. We probably don't need to go all the way back to
// the script task for this. // 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 // This has a crazy signature because the image cache needs to
// make multiple copies of the callback, and the dom event // make multiple copies of the callback, and the dom event
// channel is not a copyable type, so this is actually a // channel is not a copyable type, so this is actually a
@ -953,7 +933,7 @@ impl LayoutTask {
box LayoutImageResponder { box LayoutImageResponder {
id: self.id.clone(), id: self.id.clone(),
script_chan: self.script_chan.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 /// 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. //! 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 css::matching::{ApplicableDeclarations, CannotShare, MatchMethods, StyleWasShared};
use construct::FlowConstructor; use construct::FlowConstructor;
use context::{LayoutContext, SharedLayoutContext}; use context::{LayoutContext, SharedLayoutContext};
use flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal}; use flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal};
use flow; use flow;
use flow_ref::FlowRef; use flow_ref::FlowRef;
use incremental::RestyleDamage;
use layout_task::{AssignBSizesAndStoreOverflowTraversal, AssignISizesTraversal}; use layout_task::{AssignBSizesAndStoreOverflowTraversal, AssignISizesTraversal};
use layout_task::{BubbleISizesTraversal}; use layout_task::{BubbleISizesTraversal};
use url::Url; use url::Url;
@ -179,8 +181,10 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal {
// Get a real flow. // Get a real flow.
let flow: &mut FlowRef = mem::transmute(&unsafe_flow); let flow: &mut FlowRef = mem::transmute(&unsafe_flow);
// Perform the appropriate traversal. if self.should_process(flow.get_mut()) {
self.process(flow.get_mut()); // Perform the appropriate traversal.
self.process(flow.get_mut());
}
// Possibly enqueue the children. // Possibly enqueue the children.
for kid in flow::child_iter(flow.get_mut()) { for kid in flow::child_iter(flow.get_mut()) {
@ -296,7 +300,7 @@ fn insert_ancestors_into_bloom_filter(
ancestors += 1; ancestors += 1;
n.insert_into_bloom_filter(bf); n.insert_into_bloom_filter(bf);
n = match parent_node(&n, layout_context) { n = match n.layout_parent_node(layout_context.shared) {
None => break, None => break,
Some(p) => p, Some(p) => p,
}; };
@ -304,15 +308,6 @@ fn insert_ancestors_into_bloom_filter(
debug!("[{}] Inserted {} ancestors.", tid(), ancestors); 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, fn recalc_style_for_node(mut unsafe_layout_node: UnsafeLayoutNode,
proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeLayoutNode>) { proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeLayoutNode>) {
let shared_layout_context = unsafe { &**proxy.user_data() }; 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()); node.initialize_layout_data(layout_context.shared.layout_chan.clone());
// Get the parent node. // 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. // Get the style bloom filter.
let bf = take_task_local_bloom_filter(parent_opt, &layout_context); 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`. // Just needs to be wrapped in an option for `match_node`.
let some_bf = Some(bf); let some_bf = Some(bf);
// Otherwise, match and cascade selectors. if node.is_dirty() {
match sharing_result { // First, check to see whether we can share a style with someone.
CannotShare(mut shareable) => { let style_sharing_candidate_cache = layout_context.style_sharing_candidate_cache();
let mut applicable_declarations = ApplicableDeclarations::new(); 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() { if node.is_element() {
// Perform the CSS selector matching. // Perform the CSS selector matching.
let stylist = unsafe { &*layout_context.shared.stylist }; let stylist = unsafe { &*layout_context.shared.stylist };
node.match_node(stylist, &some_bf, &mut applicable_declarations, &mut shareable); node.match_node(stylist, &some_bf, &mut applicable_declarations, &mut shareable);
} }
// Perform the CSS cascade. // Perform the CSS cascade.
unsafe { unsafe {
node.cascade_node(parent_opt, node.cascade_node(parent_opt,
&applicable_declarations, &applicable_declarations,
layout_context.applicable_declarations_cache()); layout_context.applicable_declarations_cache());
} }
// Add ourselves to the LRU cache. // Add ourselves to the LRU cache.
if shareable { if shareable {
style_sharing_candidate_cache.insert_if_possible(&node); 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. // 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. // Construct flows for this node.
{ {
let node = ThreadSafeLayoutNode::new(&node);
let mut flow_constructor = FlowConstructor::new(layout_context); 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. // 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::js::JS;
use script::dom::bindings::utils::Reflectable; use script::dom::bindings::utils::Reflectable;
use script::dom::node::{Node, SharedLayoutData}; 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::mem;
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefMut};
use style::ComputedValues; use style::ComputedValues;
@ -29,7 +30,7 @@ pub struct PrivateLayoutData {
pub after_style: Option<Arc<ComputedValues>>, pub after_style: Option<Arc<ComputedValues>>,
/// Description of how to account for recent style changes. /// 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 /// 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. /// `ConstructionItem`. See comments in `construct.rs` for more details.
@ -49,7 +50,7 @@ impl PrivateLayoutData {
PrivateLayoutData { PrivateLayoutData {
before_style: None, before_style: None,
after_style: None, after_style: None,
restyle_damage: None, restyle_damage: RestyleDamage::empty(),
flow_construction_result: NoConstructionResult, flow_construction_result: NoConstructionResult,
before_flow_construction_result: NoConstructionResult, before_flow_construction_result: NoConstructionResult,
after_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) gfx::color::rgba(self.red, self.green, self.blue, self.alpha)
} }
} }

Просмотреть файл

@ -33,9 +33,11 @@
//! o Instead of `html_element_in_html_document()`, use //! o Instead of `html_element_in_html_document()`, use
//! `html_element_in_html_document_for_layout()`. //! `html_element_in_html_document_for_layout()`.
use context::SharedLayoutContext;
use css::node_style::StyledNode; 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::{HTMLIFrameElementDerived, HTMLInputElementDerived};
use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived}; use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived};
use script::dom::bindings::js::JS; 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::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId}; use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId};
use script::dom::node::{LayoutNodeHelpers, RawLayoutNodeHelpers, SharedLayoutData, TextNodeTypeId}; use script::dom::node::{LayoutNodeHelpers, RawLayoutNodeHelpers, SharedLayoutData, TextNodeTypeId};
use script::dom::node::{IsDirty, HasDirtyDescendants};
use script::dom::text::Text; use script::dom::text::Text;
use script::layout_interface::LayoutChan; use script::layout_interface::LayoutChan;
use servo_msg::constellation_msg::{PipelineId, SubpageId}; use servo_msg::constellation_msg::{PipelineId, SubpageId};
@ -158,7 +161,6 @@ impl<'a> PartialEq for LayoutNode<'a> {
} }
} }
impl<'ln> TLayoutNode for LayoutNode<'ln> { impl<'ln> TLayoutNode for LayoutNode<'ln> {
unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> LayoutNode<'ln> { unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> LayoutNode<'ln> {
LayoutNode { LayoutNode {
@ -249,6 +251,21 @@ impl<'ln> LayoutNode<'ln> {
Some(_) => {} 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> { 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> { pub struct LayoutNodeChildrenIterator<'a> {
@ -855,4 +888,3 @@ pub unsafe fn layout_node_from_unsafe_layout_node(node: &UnsafeLayoutNode) -> La
let (node, _) = *node; let (node, _) = *node;
mem::transmute(node) mem::transmute(node)
} }

Просмотреть файл

@ -18,15 +18,16 @@ use url::Url;
/// A struct to store image data. The image will be loaded once the first time it is requested, /// A struct to store image data. The image will be loaded once the first time it is requested,
/// and an Arc will be stored. Clones of this Arc are given out on demand. /// and an Arc will be stored. Clones of this Arc are given out on demand.
#[deriving(Clone)] #[deriving(Clone)]
pub struct ImageHolder { pub struct ImageHolder<NodeAddress> {
url: Url, url: Url,
image: Option<Arc<Box<Image>>>, image: Option<Arc<Box<Image>>>,
cached_size: Size2D<int>, cached_size: Size2D<int>,
local_image_cache: Arc<Mutex<LocalImageCache>>, local_image_cache: Arc<Mutex<LocalImageCache<NodeAddress>>>,
} }
impl ImageHolder { impl<NodeAddress: Send> ImageHolder<NodeAddress> {
pub fn new(url: Url, local_image_cache: Arc<Mutex<LocalImageCache>>) -> ImageHolder { pub fn new(url: Url, local_image_cache: Arc<Mutex<LocalImageCache<NodeAddress>>>)
-> ImageHolder<NodeAddress> {
debug!("ImageHolder::new() {}", url.serialize()); debug!("ImageHolder::new() {}", url.serialize());
let holder = ImageHolder { let holder = ImageHolder {
url: url, url: url,
@ -60,9 +61,9 @@ impl ImageHolder {
} }
/// Query and update the current image size. /// Query and update the current image size.
pub fn get_size(&mut self) -> Option<Size2D<int>> { pub fn get_size(&mut self, node_address: NodeAddress) -> Option<Size2D<int>> {
debug!("get_size() {}", self.url.serialize()); debug!("get_size() {}", self.url.serialize());
self.get_image().map(|img| { self.get_image(node_address).map(|img| {
self.cached_size = Size2D(img.width as int, self.cached_size = Size2D(img.width as int,
img.height as int); img.height as int);
self.cached_size.clone() self.cached_size.clone()
@ -74,7 +75,7 @@ impl ImageHolder {
self.image.clone() self.image.clone()
} }
pub fn get_image(&mut self) -> Option<Arc<Box<Image>>> { pub fn get_image(&mut self, node_address: NodeAddress) -> Option<Arc<Box<Image>>> {
debug!("get_image() {}", self.url.serialize()); debug!("get_image() {}", self.url.serialize());
// If this is the first time we've called this function, load // If this is the first time we've called this function, load
@ -83,7 +84,7 @@ impl ImageHolder {
let port = { let port = {
let val = self.local_image_cache.lock(); let val = self.local_image_cache.lock();
let mut local_image_cache = val; let mut local_image_cache = val;
local_image_cache.get_image(&self.url) local_image_cache.get_image(node_address, &self.url)
}; };
match port.recv() { match port.recv() {
ImageReady(image) => { ImageReady(image) => {
@ -105,5 +106,8 @@ impl ImageHolder {
return result; return result;
} }
}
pub fn url(&self) -> &Url {
&self.url
}
}

Просмотреть файл

@ -227,8 +227,8 @@ impl ImageCache {
} }
} }
fn get_state(&self, url: Url) -> ImageState { fn get_state(&self, url: &Url) -> ImageState {
match self.state_map.find(&url) { match self.state_map.find(url) {
Some(state) => state.clone(), Some(state) => state.clone(),
None => Init None => Init
} }
@ -239,7 +239,7 @@ impl ImageCache {
} }
fn prefetch(&mut self, url: Url) { fn prefetch(&mut self, url: Url) {
match self.get_state(url.clone()) { match self.get_state(&url) {
Init => { Init => {
let to_cache = self.chan.clone(); let to_cache = self.chan.clone();
let resource_task = self.resource_task.clone(); let resource_task = self.resource_task.clone();
@ -270,7 +270,7 @@ impl ImageCache {
} }
fn store_prefetched_image_data(&mut self, url: Url, data: Result<Vec<u8>, ()>) { fn store_prefetched_image_data(&mut self, url: Url, data: Result<Vec<u8>, ()>) {
match self.get_state(url.clone()) { match self.get_state(&url) {
Prefetching(next_step) => { Prefetching(next_step) => {
match data { match data {
Ok(data) => { Ok(data) => {
@ -298,7 +298,7 @@ impl ImageCache {
} }
fn decode(&mut self, url: Url) { fn decode(&mut self, url: Url) {
match self.get_state(url.clone()) { match self.get_state(&url) {
Init => fail!("decoding image before prefetch"), Init => fail!("decoding image before prefetch"),
Prefetching(DoNotDecode) => { Prefetching(DoNotDecode) => {
@ -338,7 +338,7 @@ impl ImageCache {
fn store_image(&mut self, url: Url, image: Option<Arc<Box<Image>>>) { fn store_image(&mut self, url: Url, image: Option<Arc<Box<Image>>>) {
match self.get_state(url.clone()) { match self.get_state(&url) {
Decoding => { Decoding => {
match image { match image {
Some(image) => { Some(image) => {
@ -376,7 +376,7 @@ impl ImageCache {
} }
fn get_image(&self, url: Url, response: Sender<ImageResponseMsg>) { fn get_image(&self, url: Url, response: Sender<ImageResponseMsg>) {
match self.get_state(url.clone()) { match self.get_state(&url) {
Init => fail!("request for image before prefetch"), Init => fail!("request for image before prefetch"),
Prefetching(DoDecode) => response.send(ImageNotReady), Prefetching(DoDecode) => response.send(ImageNotReady),
Prefetching(DoNotDecode) | Prefetched(..) => fail!("request for image before decode"), Prefetching(DoNotDecode) | Prefetched(..) => fail!("request for image before decode"),
@ -387,7 +387,7 @@ impl ImageCache {
} }
fn wait_for_image(&mut self, url: Url, response: Sender<ImageResponseMsg>) { fn wait_for_image(&mut self, url: Url, response: Sender<ImageResponseMsg>) {
match self.get_state(url.clone()) { match self.get_state(&url) {
Init => fail!("request for image before prefetch"), Init => fail!("request for image before prefetch"),
Prefetching(DoNotDecode) | Prefetched(..) => fail!("request for image before decode"), Prefetching(DoNotDecode) | Prefetched(..) => fail!("request for image before decode"),

Просмотреть файл

@ -16,19 +16,19 @@ use std::collections::hashmap::HashMap;
use servo_util::task::spawn_named; use servo_util::task::spawn_named;
use url::Url; use url::Url;
pub trait ImageResponder { pub trait ImageResponder<NodeAddress: Send> {
fn respond(&self) -> proc(ImageResponseMsg):Send; fn respond(&self) -> proc(ImageResponseMsg, NodeAddress):Send;
} }
pub struct LocalImageCache { pub struct LocalImageCache<NodeAddress> {
image_cache_task: ImageCacheTask, image_cache_task: ImageCacheTask,
round_number: uint, round_number: uint,
on_image_available: Option<Box<ImageResponder+Send>>, on_image_available: Option<Box<ImageResponder<NodeAddress>+Send>>,
state_map: HashMap<Url, ImageState> state_map: HashMap<Url, ImageState>
} }
impl LocalImageCache { impl<NodeAddress: Send> LocalImageCache<NodeAddress> {
pub fn new(image_cache_task: ImageCacheTask) -> LocalImageCache { pub fn new(image_cache_task: ImageCacheTask) -> LocalImageCache<NodeAddress> {
LocalImageCache { LocalImageCache {
image_cache_task: image_cache_task, image_cache_task: image_cache_task,
round_number: 1, round_number: 1,
@ -46,10 +46,10 @@ struct ImageState {
last_response: ImageResponseMsg last_response: ImageResponseMsg
} }
impl LocalImageCache { impl<NodeAddress: Send> LocalImageCache<NodeAddress> {
/// The local cache will only do a single remote request for a given /// The local cache will only do a single remote request for a given
/// URL in each 'round'. Layout should call this each time it begins /// URL in each 'round'. Layout should call this each time it begins
pub fn next_round(&mut self, on_image_available: Box<ImageResponder+Send>) { pub fn next_round(&mut self, on_image_available: Box<ImageResponder<NodeAddress> + Send>) {
self.round_number += 1; self.round_number += 1;
self.on_image_available = Some(on_image_available); self.on_image_available = Some(on_image_available);
} }
@ -80,7 +80,7 @@ impl LocalImageCache {
} }
// FIXME: Should return a Future // FIXME: Should return a Future
pub fn get_image(&mut self, url: &Url) -> Receiver<ImageResponseMsg> { pub fn get_image(&mut self, node_address: NodeAddress, url: &Url) -> Receiver<ImageResponseMsg> {
{ {
let round_number = self.round_number; let round_number = self.round_number;
let state = self.get_state(url); let state = self.get_state(url);
@ -127,12 +127,13 @@ impl LocalImageCache {
// on the image to load and triggering layout // on the image to load and triggering layout
let image_cache_task = self.image_cache_task.clone(); let image_cache_task = self.image_cache_task.clone();
assert!(self.on_image_available.is_some()); assert!(self.on_image_available.is_some());
let on_image_available: proc(ImageResponseMsg):Send = self.on_image_available.as_ref().unwrap().respond(); let on_image_available: proc(ImageResponseMsg, NodeAddress):Send =
self.on_image_available.as_ref().unwrap().respond();
let url = (*url).clone(); let url = (*url).clone();
spawn_named("LocalImageCache", proc() { spawn_named("LocalImageCache", proc() {
let (response_chan, response_port) = channel(); let (response_chan, response_port) = channel();
image_cache_task.send(WaitForImage(url.clone(), response_chan)); image_cache_task.send(WaitForImage(url, response_chan));
on_image_available(response_port.recv()); on_image_available(response_port.recv(), node_address);
}); });
} }
_ => () _ => ()

Просмотреть файл

@ -49,8 +49,10 @@ use http::headers::response::HeaderCollection as ResponseHeaderCollection;
use http::headers::request::HeaderCollection as RequestHeaderCollection; use http::headers::request::HeaderCollection as RequestHeaderCollection;
use http::method::Method; use http::method::Method;
use std::io::timer::Timer; use std::io::timer::Timer;
use script_traits::UntrustedNodeAddress;
use servo_msg::compositor_msg::ScriptListener; use servo_msg::compositor_msg::ScriptListener;
use servo_msg::constellation_msg::ConstellationChan; use servo_msg::constellation_msg::ConstellationChan;
use servo_util::smallvec::{SmallVec1, SmallVec};
use layout_interface::{LayoutRPC, LayoutChan}; use layout_interface::{LayoutRPC, LayoutChan};
use dom::bindings::utils::WindowProxyHandler; use dom::bindings::utils::WindowProxyHandler;
@ -148,6 +150,17 @@ impl<T: JSTraceable> JSTraceable for Vec<T> {
} }
} }
// XXXManishearth Check if the following three are optimized to no-ops
// if e.trace() is a no-op (e.g it is an untraceable type)
impl<T: JSTraceable + 'static> JSTraceable for SmallVec1<T> {
#[inline]
fn trace(&self, trc: *mut JSTracer) {
for e in self.iter() {
e.trace(trc);
}
}
}
impl<T: JSTraceable> JSTraceable for Option<T> { impl<T: JSTraceable> JSTraceable for Option<T> {
#[inline] #[inline]
fn trace(&self, trc: *mut JSTracer) { fn trace(&self, trc: *mut JSTracer) {
@ -192,6 +205,7 @@ untraceable!(ResponseHeaderCollection, RequestHeaderCollection, Method)
untraceable!(ConstellationChan) untraceable!(ConstellationChan)
untraceable!(LayoutChan) untraceable!(LayoutChan)
untraceable!(WindowProxyHandler) untraceable!(WindowProxyHandler)
untraceable!(UntrustedNodeAddress)
impl<'a> JSTraceable for &'a str { impl<'a> JSTraceable for &'a str {
#[inline] #[inline]

Просмотреть файл

@ -54,7 +54,6 @@ use dom::uievent::UIEvent;
use dom::window::{Window, WindowHelpers}; use dom::window::{Window, WindowHelpers};
use html::hubbub_html_parser::build_element_from_tag; use html::hubbub_html_parser::build_element_from_tag;
use hubbub::hubbub::{QuirksMode, NoQuirks, LimitedQuirks, FullQuirks}; use hubbub::hubbub::{QuirksMode, NoQuirks, LimitedQuirks, FullQuirks};
use layout_interface::{DocumentDamageLevel, ContentChangedDocumentDamage};
use servo_util::namespace; use servo_util::namespace;
use servo_util::str::{DOMString, split_html_space_chars}; use servo_util::str::{DOMString, split_html_space_chars};
@ -165,8 +164,8 @@ pub trait DocumentHelpers<'a> {
fn set_quirks_mode(self, mode: QuirksMode); fn set_quirks_mode(self, mode: QuirksMode);
fn set_last_modified(self, value: DOMString); fn set_last_modified(self, value: DOMString);
fn set_encoding_name(self, name: DOMString); fn set_encoding_name(self, name: DOMString);
fn content_changed(self); fn content_changed(self, node: JSRef<Node>);
fn damage_and_reflow(self, damage: DocumentDamageLevel); fn reflow(self);
fn wait_until_safe_to_modify_dom(self); fn wait_until_safe_to_modify_dom(self);
fn unregister_named_element(self, to_unregister: JSRef<Element>, id: Atom); fn unregister_named_element(self, to_unregister: JSRef<Element>, id: Atom);
fn register_named_element(self, element: JSRef<Element>, id: Atom); fn register_named_element(self, element: JSRef<Element>, id: Atom);
@ -195,19 +194,19 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
*self.encoding_name.borrow_mut() = name; *self.encoding_name.borrow_mut() = name;
} }
fn content_changed(self) { fn content_changed(self, node: JSRef<Node>) {
self.damage_and_reflow(ContentChangedDocumentDamage); node.dirty();
self.reflow();
} }
fn damage_and_reflow(self, damage: DocumentDamageLevel) { fn reflow(self) {
self.window.root().damage_and_reflow(damage); self.window.root().reflow();
} }
fn wait_until_safe_to_modify_dom(self) { fn wait_until_safe_to_modify_dom(self) {
self.window.root().wait_until_safe_to_modify_dom(); self.window.root().wait_until_safe_to_modify_dom();
} }
/// 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.
fn unregister_named_element(self, fn unregister_named_element(self,
to_unregister: JSRef<Element>, to_unregister: JSRef<Element>,

Просмотреть файл

@ -28,8 +28,6 @@ use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_fro
use dom::node::{window_from_node, LayoutNodeHelpers}; use dom::node::{window_from_node, LayoutNodeHelpers};
use dom::nodelist::NodeList; use dom::nodelist::NodeList;
use dom::virtualmethods::{VirtualMethods, vtable_for}; use dom::virtualmethods::{VirtualMethods, vtable_for};
use layout_interface::ContentChangedDocumentDamage;
use layout_interface::MatchSelectorsDocumentDamage;
use devtools_traits::AttrInfo; use devtools_traits::AttrInfo;
use style::{matches, parse_selector_list_from_str}; use style::{matches, parse_selector_list_from_str};
use style; use style;
@ -323,6 +321,7 @@ pub trait AttributeHandlers {
fn remove_attribute(self, namespace: Namespace, name: &str); fn remove_attribute(self, namespace: Namespace, name: &str);
fn notify_attribute_changed(self, local_name: &Atom); fn notify_attribute_changed(self, local_name: &Atom);
fn has_class(&self, name: &str) -> bool; fn has_class(&self, name: &str) -> bool;
fn notify_attribute_removed(self);
fn set_atomic_attribute(self, name: &str, value: DOMString); fn set_atomic_attribute(self, name: &str, value: DOMString);
@ -436,19 +435,24 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {
} }
self.attrs.borrow_mut().remove(idx); self.attrs.borrow_mut().remove(idx);
self.notify_attribute_removed();
} }
}; };
} }
fn notify_attribute_changed(self, local_name: &Atom) { fn notify_attribute_changed(self, _local_name: &Atom) {
let node: JSRef<Node> = NodeCast::from_ref(self); let node: JSRef<Node> = NodeCast::from_ref(self);
if node.is_in_doc() { if node.is_in_doc() {
let damage = match local_name.as_slice() {
"style" | "id" | "class" => MatchSelectorsDocumentDamage,
_ => ContentChangedDocumentDamage
};
let document = node.owner_doc().root(); let document = node.owner_doc().root();
document.damage_and_reflow(damage); document.content_changed(node);
}
}
fn notify_attribute_removed(self) {
let node: JSRef<Node> = NodeCast::from_ref(self);
if node.is_in_doc() {
let document = node.owner_doc().root();
document.content_changed(node);
} }
} }

Просмотреть файл

@ -175,7 +175,8 @@ fn broadcast_radio_checked(broadcaster: JSRef<HTMLInputElement>, group: Option<&
impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> { impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> {
fn force_relayout(self) { fn force_relayout(self) {
let doc = document_from_node(self).root(); let doc = document_from_node(self).root();
doc.content_changed() let node: JSRef<Node> = NodeCast::from_ref(self);
doc.content_changed(node)
} }
fn radio_group_updated(self, group: Option<&str>) { fn radio_group_updated(self, group: Option<&str>) {

Просмотреть файл

@ -46,8 +46,9 @@ use dom::window::Window;
use geom::rect::Rect; use geom::rect::Rect;
use html::hubbub_html_parser::build_element_from_tag; use html::hubbub_html_parser::build_element_from_tag;
use layout_interface::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC, use layout_interface::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC,
LayoutChan, ReapLayoutDataMsg, TrustedNodeAddress, UntrustedNodeAddress}; LayoutChan, ReapLayoutDataMsg, TrustedNodeAddress};
use devtools_traits::NodeInfo; use devtools_traits::NodeInfo;
use script_traits::UntrustedNodeAddress;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::str::{DOMString, null_str_as_empty}; use servo_util::str::{DOMString, null_str_as_empty};
use style::{parse_selector_list_from_str, matches}; use style::{parse_selector_list_from_str, matches};
@ -56,7 +57,7 @@ use js::jsapi::{JSContext, JSObject, JSTracer, JSRuntime};
use js::jsfriendapi; use js::jsfriendapi;
use libc; use libc;
use libc::uintptr_t; use libc::uintptr_t;
use std::cell::{RefCell, Ref, RefMut}; use std::cell::{Cell, RefCell, Ref, RefMut};
use std::default::Default; use std::default::Default;
use std::iter::{Map, Filter}; use std::iter::{Map, Filter};
use std::mem; use std::mem;
@ -101,7 +102,7 @@ pub struct Node {
child_list: MutNullableJS<NodeList>, child_list: MutNullableJS<NodeList>,
/// A bitfield of flags for node items. /// A bitfield of flags for node items.
flags: RefCell<NodeFlags>, flags: Cell<NodeFlags>,
/// Layout information. Only the layout task may touch this data. /// Layout information. Only the layout task may touch this data.
/// ///
@ -132,14 +133,19 @@ bitflags! {
#[doc = "Specifies whether this node is in disabled state."] #[doc = "Specifies whether this node is in disabled state."]
static InDisabledState = 0x04, static InDisabledState = 0x04,
#[doc = "Specifies whether this node is in enabled state."] #[doc = "Specifies whether this node is in enabled state."]
static InEnabledState = 0x08 static InEnabledState = 0x08,
#[doc = "Specifies whether this node has changed since the last reflow."]
static IsDirty = 0x10,
#[doc = "Specifies whether this node has descendants (inclusive of itself) which \
have changed since the last reflow."]
static HasDirtyDescendants = 0x20,
} }
} }
impl NodeFlags { impl NodeFlags {
pub fn new(type_id: NodeTypeId) -> NodeFlags { pub fn new(type_id: NodeTypeId) -> NodeFlags {
match type_id { match type_id {
DocumentNodeTypeId => IsInDoc, DocumentNodeTypeId => IsInDoc | IsDirty,
// The following elements are enabled by default. // The following elements are enabled by default.
ElementNodeTypeId(HTMLButtonElementTypeId) | ElementNodeTypeId(HTMLButtonElementTypeId) |
ElementNodeTypeId(HTMLInputElementTypeId) | ElementNodeTypeId(HTMLInputElementTypeId) |
@ -148,8 +154,8 @@ impl NodeFlags {
ElementNodeTypeId(HTMLOptGroupElementTypeId) | ElementNodeTypeId(HTMLOptGroupElementTypeId) |
ElementNodeTypeId(HTMLOptionElementTypeId) | ElementNodeTypeId(HTMLOptionElementTypeId) |
//ElementNodeTypeId(HTMLMenuItemElementTypeId) | //ElementNodeTypeId(HTMLMenuItemElementTypeId) |
ElementNodeTypeId(HTMLFieldSetElementTypeId) => InEnabledState, ElementNodeTypeId(HTMLFieldSetElementTypeId) => InEnabledState | IsDirty,
_ => NodeFlags::empty(), _ => IsDirty,
} }
} }
} }
@ -271,7 +277,7 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> {
let parent = self.parent_node().root(); let parent = self.parent_node().root();
parent.map(|parent| vtable_for(&*parent).child_inserted(self)); parent.map(|parent| vtable_for(&*parent).child_inserted(self));
document.content_changed(); document.content_changed(self);
} }
// http://dom.spec.whatwg.org/#node-is-removed // http://dom.spec.whatwg.org/#node-is-removed
@ -283,7 +289,7 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> {
vtable_for(&node).unbind_from_tree(parent_in_doc); vtable_for(&node).unbind_from_tree(parent_in_doc);
} }
document.content_changed(); document.content_changed(self);
} }
// //
@ -395,6 +401,9 @@ pub trait NodeHelpers<'a> {
fn is_text(self) -> bool; fn is_text(self) -> bool;
fn is_anchor_element(self) -> bool; fn is_anchor_element(self) -> bool;
fn get_flag(self, flag: NodeFlags) -> bool;
fn set_flag(self, flag: NodeFlags, value: bool);
fn get_hover_state(self) -> bool; fn get_hover_state(self) -> bool;
fn set_hover_state(self, state: bool); fn set_hover_state(self, state: bool);
@ -404,6 +413,17 @@ pub trait NodeHelpers<'a> {
fn get_enabled_state(self) -> bool; fn get_enabled_state(self) -> bool;
fn set_enabled_state(self, state: bool); fn set_enabled_state(self, state: bool);
fn get_is_dirty(self) -> bool;
fn set_is_dirty(self, state: bool);
fn get_has_dirty_descendants(self) -> bool;
fn set_has_dirty_descendants(self, state: bool);
/// Marks the given node as `IsDirty`, its siblings as `IsDirty` (to deal
/// with sibling selectors), its ancestors as `HasDirtyDescendants`, and its
/// descendants as `IsDirty`.
fn dirty(self);
fn dump(self); fn dump(self);
fn dump_indent(self, indent: uint); fn dump_indent(self, indent: uint);
fn debug_str(self) -> String; fn debug_str(self) -> String;
@ -426,6 +446,7 @@ pub trait NodeHelpers<'a> {
fn summarize(self) -> NodeInfo; fn summarize(self) -> NodeInfo;
} }
impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
/// Dumps the subtree rooted at this node, for debugging. /// Dumps the subtree rooted at this node, for debugging.
fn dump(self) { fn dump(self) {
@ -454,7 +475,7 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
} }
fn is_in_doc(self) -> bool { fn is_in_doc(self) -> bool {
self.flags.borrow().contains(IsInDoc) self.deref().flags.get().contains(IsInDoc)
} }
/// Returns the type ID of this node. Fails if this node is borrowed mutably. /// Returns the type ID of this node. Fails if this node is borrowed mutably.
@ -512,39 +533,98 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
self.type_id == TextNodeTypeId self.type_id == TextNodeTypeId
} }
fn get_flag(self, flag: NodeFlags) -> bool {
self.flags.get().contains(flag)
}
fn set_flag(self, flag: NodeFlags, value: bool) {
let mut flags = self.flags.get();
if value {
flags.insert(flag);
} else {
flags.remove(flag);
}
self.flags.set(flags);
}
fn get_hover_state(self) -> bool { fn get_hover_state(self) -> bool {
self.flags.borrow().contains(InHoverState) self.get_flag(InHoverState)
} }
fn set_hover_state(self, state: bool) { fn set_hover_state(self, state: bool) {
if state { self.set_flag(InHoverState, state)
self.flags.borrow_mut().insert(InHoverState);
} else {
self.flags.borrow_mut().remove(InHoverState);
}
} }
fn get_disabled_state(self) -> bool { fn get_disabled_state(self) -> bool {
self.flags.borrow().contains(InDisabledState) self.get_flag(InDisabledState)
} }
fn set_disabled_state(self, state: bool) { fn set_disabled_state(self, state: bool) {
if state { self.set_flag(InDisabledState, state)
self.flags.borrow_mut().insert(InDisabledState);
} else {
self.flags.borrow_mut().remove(InDisabledState);
}
} }
fn get_enabled_state(self) -> bool { fn get_enabled_state(self) -> bool {
self.flags.borrow().contains(InEnabledState) self.get_flag(InEnabledState)
} }
fn set_enabled_state(self, state: bool) { fn set_enabled_state(self, state: bool) {
if state { self.set_flag(InEnabledState, state)
self.flags.borrow_mut().insert(InEnabledState); }
} else {
self.flags.borrow_mut().remove(InEnabledState); fn get_is_dirty(self) -> bool {
self.get_flag(IsDirty)
}
fn set_is_dirty(self, state: bool) {
self.set_flag(IsDirty, state)
}
fn get_has_dirty_descendants(self) -> bool {
self.get_flag(HasDirtyDescendants)
}
fn set_has_dirty_descendants(self, state: bool) {
self.set_flag(HasDirtyDescendants, state)
}
fn dirty(self) {
// 1. Dirty descendants.
fn dirty_subtree(node: JSRef<Node>) {
node.set_is_dirty(true);
let mut has_dirty_descendants = false;
for kid in node.children() {
dirty_subtree(kid);
has_dirty_descendants = true;
}
if has_dirty_descendants {
node.set_has_dirty_descendants(true);
}
}
dirty_subtree(self);
// 2. Dirty siblings.
//
// TODO(cgaebel): This is a very conservative way to account for sibling
// selectors. Maybe we can do something smarter in the future.
let parent =
match self.parent_node() {
None => return,
Some(parent) => parent,
};
for sibling in parent.root().children() {
sibling.set_is_dirty(true);
}
// 3. Dirty ancestors.
for ancestor in self.ancestors() {
if ancestor.get_has_dirty_descendants() { break }
ancestor.set_has_dirty_descendants(true);
} }
} }
@ -734,6 +814,7 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
incompleteValue: false, //FIXME: reflect truncation incompleteValue: false, //FIXME: reflect truncation
} }
} }
} }
/// If the given untrusted node address represents a valid DOM node in the given runtime, /// If the given untrusted node address represents a valid DOM node in the given runtime,
@ -764,6 +845,8 @@ pub trait LayoutNodeHelpers {
unsafe fn owner_doc_for_layout(&self) -> JS<Document>; unsafe fn owner_doc_for_layout(&self) -> JS<Document>;
unsafe fn is_element_for_layout(&self) -> bool; unsafe fn is_element_for_layout(&self) -> bool;
unsafe fn get_flag(self, flag: NodeFlags) -> bool;
unsafe fn set_flag(self, flag: NodeFlags, value: bool);
} }
impl LayoutNodeHelpers for JS<Node> { impl LayoutNodeHelpers for JS<Node> {
@ -806,6 +889,25 @@ impl LayoutNodeHelpers for JS<Node> {
unsafe fn owner_doc_for_layout(&self) -> JS<Document> { unsafe fn owner_doc_for_layout(&self) -> JS<Document> {
(*self.unsafe_get()).owner_doc.get_inner().unwrap() (*self.unsafe_get()).owner_doc.get_inner().unwrap()
} }
#[inline]
unsafe fn get_flag(self, flag: NodeFlags) -> bool {
(*self.unsafe_get()).flags.get().contains(flag)
}
#[inline]
unsafe fn set_flag(self, flag: NodeFlags, value: bool) {
let this = self.unsafe_get();
let mut flags = (*this).flags.get();
if value {
flags.insert(flag);
} else {
flags.remove(flag);
}
(*this).flags.set(flags);
}
} }
pub trait RawLayoutNodeHelpers { pub trait RawLayoutNodeHelpers {
@ -1034,8 +1136,7 @@ impl Node {
prev_sibling: Default::default(), prev_sibling: Default::default(),
owner_doc: MutNullableJS::new(doc), owner_doc: MutNullableJS::new(doc),
child_list: Default::default(), child_list: Default::default(),
flags: Cell::new(NodeFlags::new(type_id)),
flags: RefCell::new(NodeFlags::new(type_id)),
layout_data: LayoutDataRef::new(), layout_data: LayoutDataRef::new(),
@ -1236,11 +1337,13 @@ impl Node {
parent.add_child(*node, child); parent.add_child(*node, child);
let is_in_doc = parent.is_in_doc(); let is_in_doc = parent.is_in_doc();
for kid in node.traverse_preorder() { for kid in node.traverse_preorder() {
let mut flags = kid.flags.get();
if is_in_doc { if is_in_doc {
kid.flags.borrow_mut().insert(IsInDoc); flags.insert(IsInDoc);
} else { } else {
kid.flags.borrow_mut().remove(IsInDoc); flags.remove(IsInDoc);
} }
kid.flags.set(flags);
} }
} }
@ -1326,7 +1429,7 @@ impl Node {
// Step 8. // Step 8.
parent.remove_child(node); parent.remove_child(node);
node.flags.borrow_mut().remove(IsInDoc); node.set_flag(IsInDoc, false);
// Step 9. // Step 9.
match suppress_observers { match suppress_observers {
@ -1660,7 +1763,7 @@ impl<'a> NodeMethods for JSRef<'a, Node> {
// Notify the document that the content of this node is different // Notify the document that the content of this node is different
let document = self.owner_doc().root(); let document = self.owner_doc().root();
document.content_changed(); document.content_changed(self);
} }
DoctypeNodeTypeId | DoctypeNodeTypeId |
DocumentNodeTypeId => {} DocumentNodeTypeId => {}
@ -2120,6 +2223,12 @@ impl<'a> style::TNode<'a, JSRef<'a, Element>> for JSRef<'a, Node> {
assert!(elem.is_some()); assert!(elem.is_some());
elem.unwrap().html_element_in_html_document() elem.unwrap().html_element_in_html_document()
} }
fn is_dirty(self) -> bool { self.get_is_dirty() }
unsafe fn set_dirty(self, value: bool) { self.set_is_dirty(value) }
fn has_dirty_descendants(self) -> bool { self.get_has_dirty_descendants() }
unsafe fn set_dirty_descendants(self, value: bool) { self.set_has_dirty_descendants(value) }
} }
pub trait DisabledStateHelpers { pub trait DisabledStateHelpers {

Просмотреть файл

@ -18,7 +18,7 @@ use dom::location::Location;
use dom::navigator::Navigator; use dom::navigator::Navigator;
use dom::performance::Performance; use dom::performance::Performance;
use dom::screen::Screen; use dom::screen::Screen;
use layout_interface::{ReflowGoal, DocumentDamageLevel}; use layout_interface::{ReflowGoal, ReflowForDisplay};
use page::Page; use page::Page;
use script_task::{ExitWindowMsg, FireTimerMsg, ScriptChan, TriggerLoadMsg, TriggerFragmentMsg}; use script_task::{ExitWindowMsg, FireTimerMsg, ScriptChan, TriggerLoadMsg, TriggerFragmentMsg};
use script_traits::ScriptControlChan; use script_traits::ScriptControlChan;
@ -366,7 +366,7 @@ impl Reflectable for Window {
} }
pub trait WindowHelpers { pub trait WindowHelpers {
fn damage_and_reflow(self, damage: DocumentDamageLevel); fn reflow(self);
fn flush_layout(self, goal: ReflowGoal); fn flush_layout(self, goal: ReflowGoal);
fn wait_until_safe_to_modify_dom(self); fn wait_until_safe_to_modify_dom(self);
fn init_browser_context(self, doc: JSRef<Document>); fn init_browser_context(self, doc: JSRef<Document>);
@ -399,9 +399,12 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
}) })
} }
fn damage_and_reflow(self, damage: DocumentDamageLevel) { fn reflow(self) {
self.page().damage(damage); self.page().damage();
self.page().avoided_reflows.set(self.page().avoided_reflows.get() + 1); // FIXME This should probably be ReflowForQuery, not Display. All queries currently
// currently rely on the display list, which means we can't destroy it by
// doing a query reflow.
self.page().reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor);
} }
fn flush_layout(self, goal: ReflowGoal) { fn flush_layout(self, goal: ReflowGoal) {

Просмотреть файл

@ -14,11 +14,10 @@ use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use js::jsapi::JSTracer; use js::jsapi::JSTracer;
use libc::c_void; use libc::c_void;
use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel}; use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel, UntrustedNodeAddress};
use servo_msg::constellation_msg::WindowSizeData; use servo_msg::constellation_msg::WindowSizeData;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use std::any::{Any, AnyRefExt}; use std::any::{Any, AnyRefExt};
use std::cmp;
use std::comm::{channel, Receiver, Sender}; use std::comm::{channel, Receiver, Sender};
use std::owned::BoxAny; use std::owned::BoxAny;
use style::Stylesheet; use style::Stylesheet;
@ -85,47 +84,13 @@ impl JSTraceable for TrustedNodeAddress {
} }
} }
/// The address of a node. Layout sends these back. They must be validated via
/// `from_untrusted_node_address` before they can be used, because we do not trust layout.
pub type UntrustedNodeAddress = *const c_void;
pub struct ContentBoxResponse(pub Rect<Au>); pub struct ContentBoxResponse(pub Rect<Au>);
pub struct ContentBoxesResponse(pub Vec<Rect<Au>>); pub struct ContentBoxesResponse(pub Vec<Rect<Au>>);
pub struct HitTestResponse(pub UntrustedNodeAddress); pub struct HitTestResponse(pub UntrustedNodeAddress);
pub struct MouseOverResponse(pub Vec<UntrustedNodeAddress>); pub struct MouseOverResponse(pub Vec<UntrustedNodeAddress>);
/// Determines which part of the
#[deriving(PartialEq, PartialOrd, Eq, Ord)]
#[jstraceable]
pub enum DocumentDamageLevel {
/// Reflow, but do not perform CSS selector matching.
ReflowDocumentDamage,
/// Perform CSS selector matching and reflow.
MatchSelectorsDocumentDamage,
/// Content changed; set full style damage and do the above.
ContentChangedDocumentDamage,
}
impl DocumentDamageLevel {
/// Sets this damage to the maximum of this damage and the given damage.
pub fn add(&mut self, new_damage: DocumentDamageLevel) {
*self = cmp::max(*self, new_damage);
}
}
/// What parts of the document have changed, as far as the script task can tell.
///
/// Note that this is fairly coarse-grained and is separate from layout's notion of the document
#[jstraceable]
pub struct DocumentDamage {
/// The topmost node in the tree that has changed.
pub root: TrustedNodeAddress,
/// The amount of damage that occurred.
pub level: DocumentDamageLevel,
}
/// Why we're doing reflow. /// Why we're doing reflow.
#[deriving(PartialEq)] #[deriving(PartialEq, Show)]
pub enum ReflowGoal { pub enum ReflowGoal {
/// We're reflowing in order to send a display list to the screen. /// We're reflowing in order to send a display list to the screen.
ReflowForDisplay, ReflowForDisplay,
@ -137,8 +102,6 @@ pub enum ReflowGoal {
pub struct Reflow { pub struct Reflow {
/// The document node. /// The document node.
pub document_root: TrustedNodeAddress, pub document_root: TrustedNodeAddress,
/// The style changes that need to be done.
pub damage: DocumentDamage,
/// The goal of reflow: either to render to the screen or to flush layout info for script. /// The goal of reflow: either to render to the screen or to flush layout info for script.
pub goal: ReflowGoal, pub goal: ReflowGoal,
/// The URL of the page. /// The URL of the page.
@ -190,21 +153,3 @@ impl ScriptLayoutChan for OpaqueScriptLayoutChannel {
*receiver.downcast::<Receiver<Msg>>().unwrap() *receiver.downcast::<Receiver<Msg>>().unwrap()
} }
} }
#[test]
fn test_add_damage() {
fn assert_add(mut a: DocumentDamageLevel, b: DocumentDamageLevel,
result: DocumentDamageLevel) {
a.add(b);
assert!(a == result);
}
assert_add(ReflowDocumentDamage, ReflowDocumentDamage, ReflowDocumentDamage);
assert_add(ContentChangedDocumentDamage, ContentChangedDocumentDamage, ContentChangedDocumentDamage);
assert_add(ReflowDocumentDamage, MatchSelectorsDocumentDamage, MatchSelectorsDocumentDamage);
assert_add(MatchSelectorsDocumentDamage, ReflowDocumentDamage, MatchSelectorsDocumentDamage);
assert_add(ReflowDocumentDamage, ContentChangedDocumentDamage, ContentChangedDocumentDamage);
assert_add(ContentChangedDocumentDamage, ReflowDocumentDamage, ContentChangedDocumentDamage);
assert_add(MatchSelectorsDocumentDamage, ContentChangedDocumentDamage, ContentChangedDocumentDamage);
assert_add(ContentChangedDocumentDamage, MatchSelectorsDocumentDamage, ContentChangedDocumentDamage);
}

Просмотреть файл

@ -10,12 +10,11 @@ use dom::document::{Document, DocumentHelpers};
use dom::element::Element; use dom::element::Element;
use dom::node::{Node, NodeHelpers}; use dom::node::{Node, NodeHelpers};
use dom::window::Window; use dom::window::Window;
use layout_interface::{DocumentDamage, ReflowForDisplay}; use layout_interface::{ReflowForDisplay};
use layout_interface::{DocumentDamageLevel, HitTestResponse, MouseOverResponse}; use layout_interface::{HitTestResponse, MouseOverResponse};
use layout_interface::{GetRPCMsg, LayoutChan, LayoutRPC}; use layout_interface::{GetRPCMsg, LayoutChan, LayoutRPC};
use layout_interface::{Reflow, ReflowGoal, ReflowMsg}; use layout_interface::{Reflow, ReflowGoal, ReflowMsg};
use layout_interface::UntrustedNodeAddress; use script_traits::{UntrustedNodeAddress, ScriptControlChan};
use script_traits::ScriptControlChan;
use geom::point::Point2D; use geom::point::Point2D;
use js::rust::Cx; use js::rust::Cx;
@ -25,6 +24,7 @@ use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData};
use servo_msg::constellation_msg::{PipelineId, SubpageId}; use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_net::resource_task::ResourceTask; use servo_net::resource_task::ResourceTask;
use servo_util::str::DOMString; use servo_util::str::DOMString;
use servo_util::smallvec::{SmallVec1, SmallVec};
use std::cell::{Cell, RefCell, Ref, RefMut}; use std::cell::{Cell, RefCell, Ref, RefMut};
use std::comm::{channel, Receiver, Empty, Disconnected}; use std::comm::{channel, Receiver, Empty, Disconnected};
use std::mem::replace; use std::mem::replace;
@ -55,9 +55,6 @@ pub struct Page {
/// The port that we will use to join layout. If this is `None`, then layout is not running. /// The port that we will use to join layout. If this is `None`, then layout is not running.
pub layout_join_port: RefCell<Option<Receiver<()>>>, pub layout_join_port: RefCell<Option<Receiver<()>>>,
/// What parts of the document are dirty, if any.
damage: RefCell<Option<DocumentDamage>>,
/// The current size of the window, in pixels. /// The current size of the window, in pixels.
pub window_size: Cell<WindowSizeData>, pub window_size: Cell<WindowSizeData>,
@ -74,6 +71,9 @@ pub struct Page {
/// Pending resize event, if any. /// Pending resize event, if any.
pub resize_event: Cell<Option<WindowSizeData>>, pub resize_event: Cell<Option<WindowSizeData>>,
/// Any nodes that need to be dirtied before the next reflow.
pub pending_dirty_nodes: RefCell<SmallVec1<UntrustedNodeAddress>>,
/// Pending scroll to fragment event, if any /// Pending scroll to fragment event, if any
pub fragment_name: RefCell<Option<String>>, pub fragment_name: RefCell<Option<String>>,
@ -86,6 +86,9 @@ pub struct Page {
// Child Pages. // Child Pages.
pub children: RefCell<Vec<Rc<Page>>>, pub children: RefCell<Vec<Rc<Page>>>,
/// Whether layout needs to be run at all.
pub damaged: Cell<bool>,
/// Number of pending reflows that were sent while layout was active. /// Number of pending reflows that were sent while layout was active.
pub pending_reflows: Cell<int>, pub pending_reflows: Cell<int>,
@ -143,25 +146,25 @@ impl Page {
layout_chan: layout_chan, layout_chan: layout_chan,
layout_rpc: layout_rpc, layout_rpc: layout_rpc,
layout_join_port: RefCell::new(None), layout_join_port: RefCell::new(None),
damage: RefCell::new(None),
window_size: Cell::new(window_size), window_size: Cell::new(window_size),
js_info: RefCell::new(Some(js_info)), js_info: RefCell::new(Some(js_info)),
url: RefCell::new(None), url: RefCell::new(None),
next_subpage_id: Cell::new(SubpageId(0)), next_subpage_id: Cell::new(SubpageId(0)),
resize_event: Cell::new(None), resize_event: Cell::new(None),
pending_dirty_nodes: RefCell::new(SmallVec1::new()),
fragment_name: RefCell::new(None), fragment_name: RefCell::new(None),
last_reflow_id: Cell::new(0), last_reflow_id: Cell::new(0),
resource_task: resource_task, resource_task: resource_task,
constellation_chan: constellation_chan, constellation_chan: constellation_chan,
children: RefCell::new(vec!()), children: RefCell::new(vec!()),
damaged: Cell::new(false),
pending_reflows: Cell::new(0), pending_reflows: Cell::new(0),
avoided_reflows: Cell::new(0), avoided_reflows: Cell::new(0),
} }
} }
pub fn flush_layout(&self, goal: ReflowGoal) { pub fn flush_layout(&self, goal: ReflowGoal) {
let damaged = self.damage.borrow().is_some(); if self.damaged.get() {
if damaged {
let frame = self.frame(); let frame = self.frame();
let window = frame.as_ref().unwrap().window.root(); let window = frame.as_ref().unwrap().window.root();
self.reflow(goal, window.control_chan.clone(), &*window.compositor); self.reflow(goal, window.control_chan.clone(), &*window.compositor);
@ -255,35 +258,6 @@ impl Page {
subpage_id subpage_id
} }
/// Adds the given damage.
pub fn damage(&self, level: DocumentDamageLevel) {
let root = match *self.frame() {
None => return,
Some(ref frame) => frame.document.root().GetDocumentElement()
};
match root.root() {
None => {},
Some(root) => {
let root: JSRef<Node> = NodeCast::from_ref(*root);
let mut damage = *self.damage.borrow_mut();
match damage {
None => {}
Some(ref mut damage) => {
// FIXME(pcwalton): This is wrong. We should trace up to the nearest ancestor.
damage.root = root.to_trusted_node_address();
damage.level.add(level);
return
}
}
*self.damage.borrow_mut() = Some(DocumentDamage {
root: root.to_trusted_node_address(),
level: level,
})
}
};
}
pub fn get_url(&self) -> Url { pub fn get_url(&self) -> Url {
self.url().as_ref().unwrap().ref0().clone() self.url().as_ref().unwrap().ref0().clone()
} }
@ -360,8 +334,9 @@ impl Page {
last_reflow_id.set(last_reflow_id.get() + 1); last_reflow_id.set(last_reflow_id.get() + 1);
let root: JSRef<Node> = NodeCast::from_ref(*root); let root: JSRef<Node> = NodeCast::from_ref(*root);
let mut damage = self.damage.borrow_mut();
let window_size = self.window_size.get(); let window_size = self.window_size.get();
self.damaged.set(false);
// Send new document and relevant styles to layout. // Send new document and relevant styles to layout.
let reflow = box Reflow { let reflow = box Reflow {
@ -372,7 +347,6 @@ impl Page {
window_size: window_size, window_size: window_size,
script_chan: script_chan, script_chan: script_chan,
script_join_chan: join_chan, script_join_chan: join_chan,
damage: replace(&mut *damage, None).unwrap(),
id: last_reflow_id.get(), id: last_reflow_id.get(),
}; };
@ -384,6 +358,10 @@ impl Page {
} }
} }
pub fn damage(&self) {
self.damaged.set(true);
}
/// Attempt to find a named element in this page's document. /// Attempt to find a named element in this page's document.
pub fn find_fragment_node(&self, fragid: DOMString) -> Option<Temporary<Element>> { pub fn find_fragment_node(&self, fragid: DOMString) -> Option<Temporary<Element>> {
let document = self.frame().as_ref().unwrap().document.root(); let document = self.frame().as_ref().unwrap().document.root();

Просмотреть файл

@ -29,9 +29,7 @@ use dom::worker::{Worker, TrustedWorkerAddress};
use dom::xmlhttprequest::{TrustedXHRAddress, XMLHttpRequest, XHRProgress}; use dom::xmlhttprequest::{TrustedXHRAddress, XMLHttpRequest, XHRProgress};
use html::hubbub_html_parser::{InputString, InputUrl, HtmlParserResult, HtmlDiscoveredScript}; use html::hubbub_html_parser::{InputString, InputUrl, HtmlParserResult, HtmlDiscoveredScript};
use html::hubbub_html_parser; use html::hubbub_html_parser;
use layout_interface::{ScriptLayoutChan, LayoutChan, MatchSelectorsDocumentDamage}; use layout_interface::{ScriptLayoutChan, LayoutChan, ReflowForDisplay};
use layout_interface::{ReflowDocumentDamage, ReflowForDisplay};
use layout_interface::ContentChangedDocumentDamage;
use layout_interface; use layout_interface;
use page::{Page, IterablePage, Frame}; use page::{Page, IterablePage, Frame};
@ -52,6 +50,7 @@ use servo_msg::constellation_msg;
use servo_net::image_cache_task::ImageCacheTask; use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask; use servo_net::resource_task::ResourceTask;
use servo_util::geometry::to_frac_px; use servo_util::geometry::to_frac_px;
use servo_util::smallvec::{SmallVec1, SmallVec};
use servo_util::task::spawn_named_with_send_on_failure; use servo_util::task::spawn_named_with_send_on_failure;
use geom::point::Point2D; use geom::point::Point2D;
@ -66,6 +65,7 @@ use url::Url;
use libc::size_t; use libc::size_t;
use std::any::{Any, AnyRefExt}; use std::any::{Any, AnyRefExt};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashSet;
use std::comm::{channel, Sender, Receiver, Select}; use std::comm::{channel, Sender, Receiver, Select};
use std::mem::replace; use std::mem::replace;
use std::rc::Rc; use std::rc::Rc;
@ -445,7 +445,9 @@ impl ScriptTask {
} }
}; };
// Squash any pending resize events in the queue. let mut needs_reflow = HashSet::new();
// Squash any pending resize and reflow events in the queue.
loop { loop {
match event { match event {
// This has to be handled before the ResizeMsg below, // This has to be handled before the ResizeMsg below,
@ -459,6 +461,13 @@ impl ScriptTask {
let page = page.find(id).expect("resize sent to nonexistent pipeline"); let page = page.find(id).expect("resize sent to nonexistent pipeline");
page.resize_event.set(Some(size)); page.resize_event.set(Some(size));
} }
FromConstellation(SendEventMsg(id, ReflowEvent(node_addresses))) => {
let mut page = self.page.borrow_mut();
let inner_page = page.find(id).expect("Reflow sent to nonexistent pipeline");
let mut pending = inner_page.pending_dirty_nodes.borrow_mut();
pending.push_all_move(node_addresses);
needs_reflow.insert(id);
}
_ => { _ => {
sequential.push(event); sequential.push(event);
} }
@ -507,6 +516,11 @@ impl ScriptTask {
} }
} }
// Now process any pending reflows.
for id in needs_reflow.into_iter() {
self.handle_event(id, ReflowEvent(SmallVec1::new()));
}
true true
} }
@ -632,8 +646,7 @@ impl ScriptTask {
if page.pending_reflows.get() > 0 { if page.pending_reflows.get() > 0 {
page.pending_reflows.set(0); page.pending_reflows.set(0);
page.damage(MatchSelectorsDocumentDamage); self.force_reflow(&*page);
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor);
} }
} }
@ -711,8 +724,7 @@ impl ScriptTask {
Some((ref loaded, needs_reflow)) if *loaded == url => { Some((ref loaded, needs_reflow)) if *loaded == url => {
*page.mut_url() = Some((loaded.clone(), false)); *page.mut_url() = Some((loaded.clone(), false));
if needs_reflow { if needs_reflow {
page.damage(ContentChangedDocumentDamage); self.force_reflow(&*page);
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor);
} }
return; return;
}, },
@ -796,7 +808,11 @@ impl ScriptTask {
// Kick off the initial reflow of the page. // Kick off the initial reflow of the page.
debug!("kicking off initial reflow of {}", url); debug!("kicking off initial reflow of {}", url);
document.content_changed(); {
let document_js_ref = (&*document).clone();
let document_as_node = NodeCast::from_ref(document_js_ref);
document.content_changed(document_as_node);
}
window.flush_layout(ReflowForDisplay); window.flush_layout(ReflowForDisplay);
{ {
@ -856,6 +872,21 @@ impl ScriptTask {
self.compositor.scroll_fragment_point(pipeline_id, LayerId::null(), point); self.compositor.scroll_fragment_point(pipeline_id, LayerId::null(), point);
} }
fn force_reflow(&self, page: &Page) {
{
let mut pending = page.pending_dirty_nodes.borrow_mut();
let js_runtime = self.js_runtime.deref().ptr;
for untrusted_node in pending.into_iter() {
let node = node::from_untrusted_node_address(js_runtime, untrusted_node).root();
node.dirty();
}
}
page.damage();
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor);
}
/// This is the main entry point for receiving and dispatching DOM events. /// This is the main entry point for receiving and dispatching DOM events.
/// ///
/// TODO: Actually perform DOM event dispatch. /// TODO: Actually perform DOM event dispatch.
@ -870,8 +901,7 @@ impl ScriptTask {
let frame = page.frame(); let frame = page.frame();
if frame.is_some() { if frame.is_some() {
page.damage(ReflowDocumentDamage); self.force_reflow(&*page);
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor)
} }
let fragment_node = let fragment_node =
@ -906,8 +936,9 @@ impl ScriptTask {
} }
// FIXME(pcwalton): This reflows the entire document and is not incremental-y. // FIXME(pcwalton): This reflows the entire document and is not incremental-y.
ReflowEvent => { ReflowEvent(to_dirty) => {
debug!("script got reflow event"); debug!("script got reflow event");
assert_eq!(to_dirty.len(), 0);
let page = get_page(&*self.page.borrow(), pipeline_id); let page = get_page(&*self.page.borrow(), pipeline_id);
let frame = page.frame(); let frame = page.frame();
if frame.is_some() { if frame.is_some() {
@ -915,8 +946,7 @@ impl ScriptTask {
if in_layout { if in_layout {
page.pending_reflows.set(page.pending_reflows.get() + 1); page.pending_reflows.set(page.pending_reflows.get() + 1);
} else { } else {
page.damage(MatchSelectorsDocumentDamage); self.force_reflow(&*page);
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor)
} }
} }
} }
@ -1021,8 +1051,7 @@ impl ScriptTask {
if target_compare { if target_compare {
if mouse_over_targets.is_some() { if mouse_over_targets.is_some() {
page.damage(MatchSelectorsDocumentDamage); self.force_reflow(&*page);
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor);
} }
*mouse_over_targets = Some(target_list); *mouse_over_targets = Some(target_list);
} }

Просмотреть файл

@ -13,6 +13,9 @@ path = "../msg"
[dependencies.net] [dependencies.net]
path = "../net" path = "../net"
[dependencies.util]
path = "../util"
[dependencies.devtools_traits] [dependencies.devtools_traits]
path = "../devtools_traits" path = "../devtools_traits"

Просмотреть файл

@ -9,8 +9,10 @@
extern crate devtools_traits; extern crate devtools_traits;
extern crate geom; extern crate geom;
extern crate libc;
extern crate "msg" as servo_msg; extern crate "msg" as servo_msg;
extern crate "net" as servo_net; extern crate "net" as servo_net;
extern crate "util" as servo_util;
extern crate url; extern crate url;
extern crate serialize; extern crate serialize;
@ -20,11 +22,13 @@ extern crate serialize;
// that these modules won't have to depend on script. // that these modules won't have to depend on script.
use devtools_traits::DevtoolsControlChan; use devtools_traits::DevtoolsControlChan;
use libc::c_void;
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, WindowSizeData}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, WindowSizeData};
use servo_msg::constellation_msg::SubpageId; use servo_msg::constellation_msg::SubpageId;
use servo_msg::compositor_msg::ScriptListener; use servo_msg::compositor_msg::ScriptListener;
use servo_net::image_cache_task::ImageCacheTask; use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask; use servo_net::resource_task::ResourceTask;
use servo_util::smallvec::SmallVec1;
use std::any::Any; use std::any::Any;
use url::Url; use url::Url;
@ -32,6 +36,10 @@ use geom::point::Point2D;
use serialize::{Encodable, Encoder}; use serialize::{Encodable, Encoder};
/// The address of a node. Layout sends these back. They must be validated via
/// `from_untrusted_node_address` before they can be used, because we do not trust layout.
pub type UntrustedNodeAddress = *const c_void;
pub struct NewLayoutInfo { pub struct NewLayoutInfo {
pub old_pipeline_id: PipelineId, pub old_pipeline_id: PipelineId,
pub new_pipeline_id: PipelineId, pub new_pipeline_id: PipelineId,
@ -60,7 +68,7 @@ pub enum ConstellationControlMsg {
/// Events from the compositor that the script task needs to know about /// Events from the compositor that the script task needs to know about
pub enum CompositorEvent { pub enum CompositorEvent {
ResizeEvent(WindowSizeData), ResizeEvent(WindowSizeData),
ReflowEvent, ReflowEvent(SmallVec1<UntrustedNodeAddress>),
ClickEvent(uint, Point2D<f32>), ClickEvent(uint, Point2D<f32>),
MouseDownEvent(uint, Point2D<f32>), MouseDownEvent(uint, Point2D<f32>),
MouseUpEvent(uint, Point2D<f32>), MouseUpEvent(uint, Point2D<f32>),

Просмотреть файл

@ -19,6 +19,12 @@ pub trait TNode<'a, E: TElement<'a>> : Clone + Copy {
fn as_element(self) -> E; fn as_element(self) -> E;
fn match_attr(self, attr: &AttrSelector, test: |&str| -> bool) -> bool; fn match_attr(self, attr: &AttrSelector, test: |&str| -> bool) -> bool;
fn is_html_element_in_html_document(self) -> bool; fn is_html_element_in_html_document(self) -> bool;
fn is_dirty(self) -> bool;
unsafe fn set_dirty(self, value: bool);
fn has_dirty_descendants(self) -> bool;
unsafe fn set_dirty_descendants(self, value: bool);
} }
pub trait TElement<'a> : Copy { pub trait TElement<'a> : Copy {