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",
"net 0.0.1",
"url 0.1.0 (git+https://github.com/servo/rust-url#29f70a47230c2aa736e263977247c786e0b2c243)",
"util 0.0.1",
]
[[package]]

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

@ -293,7 +293,7 @@ pub enum BackgroundAndBorderLevel {
}
/// A list of rendering operations to be performed.
#[deriving(Clone)]
#[deriving(Clone, Show)]
pub struct DisplayList {
pub list: DList<DisplayItem>,
}
@ -357,8 +357,9 @@ impl DisplayList {
}
/// Returns true if this list is empty and false otherwise.
#[inline]
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

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

@ -607,8 +607,7 @@ impl<'a> FlowConstructor<'a> {
abs_descendants.push_descendants(kid_abs_descendants);
}
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
whitespace_style))
=> {
whitespace_style)) => {
// Instantiate the whitespace fragment.
let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
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.
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());
kid_base.parallel.parent = parallel::mut_owned_flow_to_unsafe_flow(self);
}
let base = flow::mut_base(self.get_mut());
base.children.push_back(new_child);
let _ = base.parallel.children_count.fetch_add(1, Relaxed);
let _ = base.parallel.children_and_absolute_descendant_count.fetch_add(1, Relaxed);

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

@ -11,6 +11,7 @@ use gfx::display_list::OpaqueNode;
use gfx::font_context::FontContext;
use gfx::font_cache_task::FontCacheTask;
use script::layout_interface::LayoutChan;
use script_traits::UntrustedNodeAddress;
use servo_msg::constellation_msg::ConstellationChan;
use servo_net::local_image_cache::LocalImageCache;
use servo_util::geometry::Au;
@ -49,7 +50,7 @@ fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext) -> *
pub struct SharedLayoutContext {
/// The local image cache.
pub image_cache: Arc<Mutex<LocalImageCache>>,
pub image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>,
/// The current screen size.
pub screen_size: Size2D<Au>,

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

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

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

@ -14,7 +14,8 @@ use sync::Arc;
/// Node mixin providing `style` method that returns a `NodeStyle`
pub trait StyledNode {
fn style<'a>(&'a self) -> &'a Arc<ComputedValues>;
fn restyle_damage(&self) -> RestyleDamage;
fn restyle_damage(self) -> RestyleDamage;
fn set_restyle_damage(self, damage: RestyleDamage);
}
impl<'ln> StyledNode for ThreadSafeLayoutNode<'ln> {
@ -23,8 +24,14 @@ impl<'ln> StyledNode for ThreadSafeLayoutNode<'ln> {
self.get_css_select_results()
}
fn restyle_damage(&self) -> RestyleDamage {
fn restyle_damage(self) -> RestyleDamage {
self.get_restyle_damage()
}
}
fn set_restyle_damage(self, damage: RestyleDamage) {
fn doit<N: NodeUtil>(n: N, damage: RestyleDamage) {
n.set_restyle_damage(damage);
}
doit(self, damage);
}
}

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

@ -4,7 +4,7 @@
use incremental::RestyleDamage;
use util::LayoutDataAccess;
use wrapper::{TLayoutNode, ThreadSafeLayoutNode};
use wrapper::ThreadSafeLayoutNode;
use wrapper::{After, Before, Normal};
use std::mem;
use style::ComputedValues;
@ -14,8 +14,8 @@ pub trait NodeUtil {
fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues>;
fn have_css_select_results(&self) -> bool;
fn get_restyle_damage(&self) -> RestyleDamage;
fn set_restyle_damage(&self, damage: RestyleDamage);
fn get_restyle_damage(self) -> RestyleDamage;
fn set_restyle_damage(self, damage: RestyleDamage);
}
impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> {
@ -62,28 +62,19 @@ impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> {
/// Get the description of how to account for recent style changes.
/// This is a simple bitfield and fine to copy by value.
fn get_restyle_damage(&self) -> RestyleDamage {
// For DOM elements, if we haven't computed damage yet, assume the worst.
// Other nodes don't have styles.
let default = if self.node_is_element() {
RestyleDamage::all()
} else {
RestyleDamage::empty()
};
fn get_restyle_damage(self) -> RestyleDamage {
let layout_data_ref = self.borrow_layout_data();
layout_data_ref
.as_ref().unwrap()
.data
.restyle_damage
.unwrap_or(default)
}
/// Set the restyle damage field.
fn set_restyle_damage(&self, damage: RestyleDamage) {
fn set_restyle_damage(self, damage: RestyleDamage) {
let mut layout_data_ref = self.mutate_layout_data();
match &mut *layout_data_ref {
&Some(ref mut layout_data) => layout_data.data.restyle_damage = Some(damage),
&Some(ref mut layout_data) => layout_data.data.restyle_damage = damage,
_ => fail!("no layout data for this node"),
}
}

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

@ -190,6 +190,10 @@ impl Floats {
}
}
pub fn len(&self) -> uint {
self.list.list.as_ref().map(|list| list.floats.len()).unwrap_or(0)
}
/// Returns a rectangle that encloses the region from block-start to block-start + block-size, with inline-size small
/// enough that it doesn't collide with any floats. max_x is the x-coordinate beyond which
/// floats have no effect. (Generally this is the containing block inline-size.)
@ -437,4 +441,3 @@ impl Floats {
clearance
}
}

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

@ -471,6 +471,13 @@ pub trait PreorderFlowTraversal {
/// The operation to perform. Return true to continue or false to stop.
fn process(&mut self, flow: &mut Flow) -> bool;
/// Returns true if this node must be processed in-order. If this returns false,
/// we skip the operation for this node, but continue processing the descendants.
/// This is called *after* parent nodes are visited.
fn should_process(&mut self, _flow: &mut Flow) -> bool {
true
}
/// Returns true if this node should be pruned. If this returns true, we skip the operation
/// entirely and do not process any descendant nodes. This is called *before* child nodes are
/// visited. The default implementation never prunes any nodes.
@ -1031,6 +1038,10 @@ impl<'a> MutableFlowUtils for &'a mut Flow + 'a {
return true
}
if !traversal.should_process(self) {
return true
}
if !traversal.process(self) {
return false
}
@ -1240,4 +1251,3 @@ impl ContainingBlockLink {
}
}
}

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

@ -35,13 +35,14 @@ use gfx::display_list::{Upright, SidewaysLeft, SidewaysRight};
use gfx::font::FontStyle;
use gfx::text::glyph::CharIndex;
use gfx::text::text_run::TextRun;
use script_traits::UntrustedNodeAddress;
use serialize::{Encodable, Encoder};
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId};
use servo_net::image::holder::ImageHolder;
use servo_net::local_image_cache::LocalImageCache;
use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin};
use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin, WritingMode};
use servo_util::range::*;
use servo_util::smallvec::SmallVec;
use servo_util::str::is_whitespace;
@ -202,7 +203,8 @@ impl InputFragmentInfo {
#[deriving(Clone)]
pub struct ImageFragmentInfo {
/// The image held within this fragment.
pub image: ImageHolder,
pub image: ImageHolder<UntrustedNodeAddress>,
pub for_node: UntrustedNodeAddress,
pub computed_inline_size: Option<Au>,
pub computed_block_size: Option<Au>,
pub dom_inline_size: Option<Au>,
@ -217,7 +219,7 @@ impl ImageFragmentInfo {
/// me.
pub fn new(node: &ThreadSafeLayoutNode,
image_url: Url,
local_image_cache: Arc<Mutex<LocalImageCache>>)
local_image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>)
-> ImageFragmentInfo {
fn convert_length(node: &ThreadSafeLayoutNode, name: &str) -> Option<Au> {
let element = node.as_element();
@ -230,8 +232,13 @@ impl ImageFragmentInfo {
let is_vertical = node.style().writing_mode.is_vertical();
let dom_width = convert_length(node, "width");
let dom_height = convert_length(node, "height");
let opaque_node: OpaqueNode = OpaqueNodeMethods::from_thread_safe_layout_node(node);
let untrusted_node: UntrustedNodeAddress = opaque_node.to_untrusted_node_address();
ImageFragmentInfo {
image: ImageHolder::new(image_url, local_image_cache),
for_node: untrusted_node,
computed_inline_size: None,
computed_block_size: None,
dom_inline_size: if is_vertical { dom_height } else { dom_width },
@ -252,13 +259,13 @@ impl ImageFragmentInfo {
/// Returns the original inline-size of the image.
pub fn image_inline_size(&mut self) -> Au {
let size = self.image.get_size().unwrap_or(Size2D::zero());
let size = self.image.get_size(self.for_node).unwrap_or(Size2D::zero());
Au::from_px(if self.writing_mode_is_vertical { size.height } else { size.width })
}
/// Returns the original block-size of the image.
pub fn image_block_size(&mut self) -> Au {
let size = self.image.get_size().unwrap_or(Size2D::zero());
let size = self.image.get_size(self.for_node).unwrap_or(Size2D::zero());
Au::from_px(if self.writing_mode_is_vertical { size.width } else { size.height })
}
@ -805,7 +812,7 @@ impl Fragment {
};
let mut holder = ImageHolder::new(image_url.clone(), layout_context.shared.image_cache.clone());
let image = match holder.get_image() {
let image = match holder.get_image(self.node.to_untrusted_node_address()) {
None => {
// No image data at all? Do nothing.
//
@ -987,7 +994,7 @@ impl Fragment {
/// * `layout_context`: The layout context.
/// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
/// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow.
pub fn build_display_list(&self,
pub fn build_display_list(&mut self,
display_list: &mut DisplayList,
layout_context: &LayoutContext,
flow_origin: Point2D<Au>,
@ -995,12 +1002,12 @@ impl Fragment {
-> ChildDisplayListAccumulator {
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
let rect_to_absolute = |logical_rect: LogicalRect<Au>| {
let physical_rect = logical_rect.to_physical(self.style.writing_mode, container_size);
let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect<Au>| {
let physical_rect = logical_rect.to_physical(writing_mode, container_size);
Rect(physical_rect.origin + flow_origin, physical_rect.size)
};
// Fragment position wrt to the owning flow.
let absolute_fragment_bounds = rect_to_absolute(self.border_box);
let absolute_fragment_bounds = rect_to_absolute(self.style.writing_mode, self.border_box);
debug!("Fragment::build_display_list at rel={}, abs={}: {}",
self.border_box,
absolute_fragment_bounds,
@ -1088,7 +1095,7 @@ impl Fragment {
}
let content_box = self.content_box();
let absolute_content_box = rect_to_absolute(content_box);
let absolute_content_box = rect_to_absolute(self.style.writing_mode, content_box);
// Create special per-fragment-type display items.
match self.specific {
@ -1133,7 +1140,7 @@ impl Fragment {
accumulator.push(display_list, SolidColorDisplayItemClass(
box SolidColorDisplayItem {
base: BaseDisplayItem::new(
rect_to_absolute(rect()),
rect_to_absolute(self.style.writing_mode, rect()),
self.node, ContentStackingLevel),
color: color.to_gfx_color(),
}
@ -1182,9 +1189,9 @@ impl Fragment {
}
ImageFragment(_) => {
match self.specific {
ImageFragment(ref image_fragment) => {
let image_ref = &image_fragment.image;
match image_ref.get_image_if_present() {
ImageFragment(ref mut image_fragment) => {
let image_ref = &mut image_fragment.image;
match image_ref.get_image(self.node.to_untrusted_node_address()) {
Some(image) => {
debug!("(building display list) building image fragment");
@ -1437,7 +1444,7 @@ impl Fragment {
let advance = metrics.advance_width;
let should_continue;
if advance <= remaining_inline_size {
if advance <= remaining_inline_size || glyphs.is_whitespace() {
should_continue = true;
if starts_line && pieces_processed_count == 0 && glyphs.is_whitespace() {
@ -1452,21 +1459,8 @@ impl Fragment {
// The advance is more than the remaining inline-size.
should_continue = false;
let slice_begin = offset + slice_range.begin();
let slice_end = offset + slice_range.end();
if glyphs.is_whitespace() {
// If there are still things after the trimmable whitespace, create the
// inline-end chunk.
if slice_end < text_fragment_info.range.end() {
debug!("split_to_inline_size: case=skipping trimmable trailing \
whitespace, then split remainder");
let inline_end_range_end = text_fragment_info.range.end() - slice_end;
inline_end_range = Some(Range::new(slice_end, inline_end_range_end));
} else {
debug!("split_to_inline_size: case=skipping trimmable trailing \
whitespace");
}
} else if slice_begin < text_fragment_info.range.end() {
if slice_begin < text_fragment_info.range.end() {
// There are still some things inline-start over at the end of the line. Create
// the inline-end chunk.
let inline_end_range_end = text_fragment_info.range.end() - slice_begin;

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

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::fmt;
use std::sync::Arc;
use style::ComputedValues;
bitflags! {
@ -35,6 +37,32 @@ impl RestyleDamage {
}
}
impl fmt::Show for RestyleDamage {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::FormatError> {
let mut first_elem = true;
let to_iter =
[ (Repaint, "Repaint")
, (BubbleISizes, "BubbleISizes")
, (Reflow, "Reflow")
];
for &(damage, damage_str) in to_iter.iter() {
if self.contains(damage) {
if !first_elem { try!(write!(f, " | ")); }
try!(write!(f, "{}", damage_str));
first_elem = false;
}
}
if first_elem {
try!(write!(f, "NoDamage"));
}
Ok(())
}
}
// NB: We need the braces inside the RHS due to Rust #8012. This particular
// version of this macro might be safe anyway, but we want to avoid silent
// breakage on modifications.
@ -47,7 +75,13 @@ macro_rules! add_if_not_equal(
})
)
pub fn compute_damage(old: &ComputedValues, new: &ComputedValues) -> RestyleDamage {
pub fn compute_damage(old: &Option<Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage {
let old: &ComputedValues =
match old.as_ref() {
None => return Repaint | BubbleISizes | Reflow,
Some(cv) => &**cv,
};
let mut damage = RestyleDamage::empty();
// This checks every CSS property, as enumerated in

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

@ -494,7 +494,7 @@ impl LineBreaker {
self.push_fragment_to_line(in_fragment);
true
} else {
debug!("LineBreaker: Found a new-line character, so splitting theline.");
debug!("LineBreaker: Found a new-line character, so splitting the line.");
let (inline_start, inline_end, run) = in_fragment.find_split_info_by_new_line()
.expect("LineBreaker: This split case makes no sense!");
@ -621,7 +621,7 @@ impl LineBreaker {
true
},
Some((None, None)) => {
error!("LineBreaker: This split case makes no sense!");
debug!("LineBreaker: Nothing to do.");
true
},
}
@ -1273,4 +1273,3 @@ impl InlineMetrics {
}
}
}

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

@ -13,7 +13,6 @@ use flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
use flow::{PreorderFlowTraversal, PostorderFlowTraversal};
use flow;
use flow_ref::FlowRef;
use incremental::RestyleDamage;
use layout_debug;
use parallel::UnsafeFlow;
use parallel;
@ -38,10 +37,11 @@ use script::dom::element::{HTMLBodyElementTypeId, HTMLHtmlElementTypeId};
use script::layout_interface::{AddStylesheetMsg, LoadStylesheetMsg, ScriptLayoutChan};
use script::layout_interface::{TrustedNodeAddress, ContentBoxesResponse, ExitNowMsg};
use script::layout_interface::{ContentBoxResponse, HitTestResponse, MouseOverResponse};
use script::layout_interface::{ContentChangedDocumentDamage, LayoutChan, Msg, PrepareToExitMsg};
use script::layout_interface::{GetRPCMsg, LayoutRPC, ReapLayoutDataMsg, Reflow, UntrustedNodeAddress};
use script::layout_interface::{LayoutChan, Msg, PrepareToExitMsg};
use script::layout_interface::{GetRPCMsg, LayoutRPC, ReapLayoutDataMsg, Reflow};
use script::layout_interface::{ReflowForDisplay, ReflowMsg};
use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel, ScriptControlChan};
use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel};
use script_traits::{ScriptControlChan, UntrustedNodeAddress};
use servo_msg::compositor_msg::Scrollable;
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
@ -53,7 +53,7 @@ use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::logical_geometry::LogicalPoint;
use servo_util::opts::Opts;
use servo_util::smallvec::{SmallVec, SmallVec1};
use servo_util::smallvec::{SmallVec, SmallVec1, VecLike};
use servo_util::task::spawn_named_with_send_on_failure;
use servo_util::time::{TimeProfilerChan, profile};
use servo_util::time;
@ -63,7 +63,7 @@ use std::comm::{channel, Sender, Receiver, Select};
use std::mem;
use std::ptr;
use style;
use style::{AuthorOrigin, Stylesheet, Stylist};
use style::{TNode, AuthorOrigin, Stylesheet, Stylist};
use style::iter_font_face_rules;
use sync::{Arc, Mutex, MutexGuard};
use url::Url;
@ -73,7 +73,7 @@ use url::Url;
/// This needs to be protected by a mutex so we can do fast RPCs.
pub struct LayoutTaskData {
/// The local image cache.
pub local_image_cache: Arc<Mutex<LocalImageCache>>,
pub local_image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>,
/// The size of the viewport.
pub screen_size: Size2D<Au>,
@ -92,6 +92,10 @@ pub struct LayoutTaskData {
/// Starts at zero, and increased by one every time a layout completes.
/// This can be used to easily check for invalid stale data.
pub generation: uint,
/// True if a style sheet was added since the last reflow. Currently, this causes all nodes to
/// be dirtied at the next reflow.
pub stylesheet_dirty: bool,
}
/// Information needed by the layout task.
@ -142,47 +146,6 @@ pub struct LayoutTask {
pub rw_data: Arc<Mutex<LayoutTaskData>>,
}
/// The damage computation traversal.
#[deriving(Clone)]
struct ComputeDamageTraversal;
impl PostorderFlowTraversal for ComputeDamageTraversal {
#[inline]
fn process(&mut self, flow: &mut Flow) -> bool {
let mut damage = flow::base(flow).restyle_damage;
for child in flow::child_iter(flow) {
damage.insert(flow::base(child).restyle_damage.propagate_up())
}
flow::mut_base(flow).restyle_damage = damage;
true
}
}
/// Propagates restyle damage up and down the tree as appropriate.
///
/// FIXME(pcwalton): Merge this with flow tree building and/or other traversals.
struct PropagateDamageTraversal {
all_style_damage: bool,
}
impl PreorderFlowTraversal for PropagateDamageTraversal {
#[inline]
fn process(&mut self, flow: &mut Flow) -> bool {
if self.all_style_damage {
flow::mut_base(flow).restyle_damage.insert(RestyleDamage::all())
}
debug!("restyle damage = {:?}", flow::base(flow).restyle_damage);
let prop = flow::base(flow).restyle_damage.propagate_down();
if !prop.is_empty() {
for kid_ctx in flow::child_iter(flow) {
flow::mut_base(kid_ctx).restyle_damage.insert(prop)
}
}
true
}
}
/// The flow tree verification traversal. This is only on in debug builds.
#[cfg(debug)]
struct FlowTreeVerificationTraversal;
@ -290,14 +253,17 @@ struct LayoutImageResponder {
script_chan: ScriptControlChan,
}
impl ImageResponder for LayoutImageResponder {
fn respond(&self) -> proc(ImageResponseMsg):Send {
impl ImageResponder<UntrustedNodeAddress> for LayoutImageResponder {
fn respond(&self) -> proc(ImageResponseMsg, UntrustedNodeAddress):Send {
let id = self.id.clone();
let script_chan = self.script_chan.clone();
let f: proc(ImageResponseMsg):Send = proc(_) {
let ScriptControlChan(chan) = script_chan;
drop(chan.send_opt(SendEventMsg(id.clone(), ReflowEvent)))
};
let f: proc(ImageResponseMsg, UntrustedNodeAddress):Send =
proc(_, node_address) {
let ScriptControlChan(chan) = script_chan;
let mut nodes = SmallVec1::new();
nodes.vec_push(node_address);
drop(chan.send_opt(SendEventMsg(id.clone(), ReflowEvent(nodes))))
};
f
}
}
@ -418,6 +384,7 @@ impl LayoutTask {
parallel_traversal: parallel_traversal,
dirty: Rect::zero(),
generation: 0,
stylesheet_dirty: false,
})),
}
}
@ -610,6 +577,7 @@ impl LayoutTask {
});
let mut rw_data = self.lock_rw_data(possibly_locked_rw_data);
rw_data.stylist.add_stylesheet(sheet, AuthorOrigin);
rw_data.stylesheet_dirty = true;
LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data);
}
@ -739,23 +707,13 @@ impl LayoutTask {
local_image_cache.next_round(self.make_on_image_available_cb());
}
// true => Do the reflow with full style damage, because content
// changed or the window was resized.
let mut all_style_damage = match data.damage.level {
ContentChangedDocumentDamage => true,
_ => false
};
// TODO: Calculate the "actual viewport":
// http://www.w3.org/TR/css-device-adapt/#actual-viewport
let viewport_size = data.window_size.initial_viewport;
let current_screen_size = Size2D(Au::from_frac32_px(viewport_size.width.get()),
Au::from_frac32_px(viewport_size.height.get()));
if rw_data.screen_size != current_screen_size {
all_style_damage = true
}
rw_data.screen_size = current_screen_size;
let old_screen_size = mem::replace(&mut rw_data.screen_size, current_screen_size);
// Create a layout context for use throughout the following passes.
let mut shared_layout_ctx =
@ -764,6 +722,20 @@ impl LayoutTask {
node,
&data.url);
// Handle conditions where the entire flow tree is invalid.
let mut needs_dirtying = false;
needs_dirtying |= current_screen_size != old_screen_size;
needs_dirtying |= rw_data.stylesheet_dirty;
unsafe {
if needs_dirtying {
LayoutTask::dirty_all_nodes(node);
}
}
rw_data.stylesheet_dirty = false;
let mut layout_root = profile(time::LayoutStyleRecalcCategory,
Some((&data.url,
data.iframe,
@ -804,15 +776,6 @@ impl LayoutTask {
layout_root.get_mut().dump();
}
// Propagate damage.
profile(time::LayoutDamagePropagateCategory, Some((&data.url, data.iframe, self.first_reflow.get())),
self.time_profiler_chan.clone(), || {
layout_root.get_mut().traverse_preorder(&mut PropagateDamageTraversal {
all_style_damage: all_style_damage
});
layout_root.get_mut().traverse_postorder(&mut ComputeDamageTraversal.clone());
});
// Perform the primary layout passes over the flow tree to compute the locations of all
// the boxes.
profile(time::LayoutMainCategory, Some((&data.url, data.iframe, self.first_reflow.get())),
@ -860,6 +823,9 @@ impl LayoutTask {
}
}
debug!("Done building display list. Display List = {}",
flow::base(layout_root.get()).display_list);
let root_display_list =
mem::replace(&mut flow::mut_base(layout_root.get_mut()).display_list,
DisplayList::new());
@ -939,13 +905,27 @@ impl LayoutTask {
chan.send(ReflowCompleteMsg(self.id, data.id));
}
unsafe fn dirty_all_nodes(node: &mut LayoutNode) {
node.set_dirty(true);
let mut has_children = false;
for mut kid in node.children() {
LayoutTask::dirty_all_nodes(&mut kid);
has_children = true;
}
if has_children {
node.set_dirty_descendants(true);
}
}
// When images can't be loaded in time to display they trigger
// this callback in some task somewhere. This will send a message
// to the script task, and ultimately cause the image to be
// re-requested. We probably don't need to go all the way back to
// the script task for this.
fn make_on_image_available_cb(&self) -> Box<ImageResponder+Send> {
fn make_on_image_available_cb(&self) -> Box<ImageResponder<UntrustedNodeAddress>+Send> {
// This has a crazy signature because the image cache needs to
// make multiple copies of the callback, and the dom event
// channel is not a copyable type, so this is actually a
@ -953,7 +933,7 @@ impl LayoutTask {
box LayoutImageResponder {
id: self.id.clone(),
script_chan: self.script_chan.clone(),
} as Box<ImageResponder+Send>
} as Box<ImageResponder<UntrustedNodeAddress>+Send>
}
/// Handles a message to destroy layout data. Layout data must be destroyed on *this* task

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

@ -6,12 +6,14 @@
//!
//! This code is highly unsafe. Keep this file small and easy to audit.
use css::node_style::StyledNode;
use css::matching::{ApplicableDeclarations, CannotShare, MatchMethods, StyleWasShared};
use construct::FlowConstructor;
use context::{LayoutContext, SharedLayoutContext};
use flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal};
use flow;
use flow_ref::FlowRef;
use incremental::RestyleDamage;
use layout_task::{AssignBSizesAndStoreOverflowTraversal, AssignISizesTraversal};
use layout_task::{BubbleISizesTraversal};
use url::Url;
@ -179,8 +181,10 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal {
// Get a real flow.
let flow: &mut FlowRef = mem::transmute(&unsafe_flow);
// Perform the appropriate traversal.
self.process(flow.get_mut());
if self.should_process(flow.get_mut()) {
// Perform the appropriate traversal.
self.process(flow.get_mut());
}
// Possibly enqueue the children.
for kid in flow::child_iter(flow.get_mut()) {
@ -296,7 +300,7 @@ fn insert_ancestors_into_bloom_filter(
ancestors += 1;
n.insert_into_bloom_filter(bf);
n = match parent_node(&n, layout_context) {
n = match n.layout_parent_node(layout_context.shared) {
None => break,
Some(p) => p,
};
@ -304,15 +308,6 @@ fn insert_ancestors_into_bloom_filter(
debug!("[{}] Inserted {} ancestors.", tid(), ancestors);
}
fn parent_node<'ln>(node: &LayoutNode<'ln>, layout_context: &LayoutContext) -> Option<LayoutNode<'ln>> {
let opaque_node: OpaqueNode = OpaqueNodeMethods::from_layout_node(node);
if opaque_node == layout_context.shared.reflow_root {
None
} else {
node.parent_node()
}
}
fn recalc_style_for_node(mut unsafe_layout_node: UnsafeLayoutNode,
proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeLayoutNode>) {
let shared_layout_context = unsafe { &**proxy.user_data() };
@ -330,45 +325,46 @@ fn recalc_style_for_node(mut unsafe_layout_node: UnsafeLayoutNode,
node.initialize_layout_data(layout_context.shared.layout_chan.clone());
// Get the parent node.
let parent_opt = parent_node(&node, &layout_context);
let parent_opt = node.layout_parent_node(layout_context.shared);
// Get the style bloom filter.
let bf = take_task_local_bloom_filter(parent_opt, &layout_context);
// First, check to see whether we can share a style with someone.
let style_sharing_candidate_cache = layout_context.style_sharing_candidate_cache();
let sharing_result = unsafe {
node.share_style_if_possible(style_sharing_candidate_cache,
parent_opt.clone())
};
// Just needs to be wrapped in an option for `match_node`.
let some_bf = Some(bf);
// Otherwise, match and cascade selectors.
match sharing_result {
CannotShare(mut shareable) => {
let mut applicable_declarations = ApplicableDeclarations::new();
if node.is_dirty() {
// First, check to see whether we can share a style with someone.
let style_sharing_candidate_cache = layout_context.style_sharing_candidate_cache();
let sharing_result = unsafe {
node.share_style_if_possible(style_sharing_candidate_cache,
parent_opt.clone())
};
// Otherwise, match and cascade selectors.
match sharing_result {
CannotShare(mut shareable) => {
let mut applicable_declarations = ApplicableDeclarations::new();
if node.is_element() {
// Perform the CSS selector matching.
let stylist = unsafe { &*layout_context.shared.stylist };
node.match_node(stylist, &some_bf, &mut applicable_declarations, &mut shareable);
}
if node.is_element() {
// Perform the CSS selector matching.
let stylist = unsafe { &*layout_context.shared.stylist };
node.match_node(stylist, &some_bf, &mut applicable_declarations, &mut shareable);
}
// Perform the CSS cascade.
unsafe {
node.cascade_node(parent_opt,
&applicable_declarations,
layout_context.applicable_declarations_cache());
}
// Perform the CSS cascade.
unsafe {
node.cascade_node(parent_opt,
&applicable_declarations,
layout_context.applicable_declarations_cache());
}
// Add ourselves to the LRU cache.
if shareable {
style_sharing_candidate_cache.insert_if_possible(&node);
// Add ourselves to the LRU cache.
if shareable {
style_sharing_candidate_cache.insert_if_possible(&node);
}
}
StyleWasShared(index) => style_sharing_candidate_cache.touch(index),
}
StyleWasShared(index) => style_sharing_candidate_cache.touch(index),
}
// Prepare for flow construction by counting the node's children and storing that count.
@ -427,8 +423,18 @@ fn construct_flows<'a>(unsafe_layout_node: &mut UnsafeLayoutNode,
// Construct flows for this node.
{
let node = ThreadSafeLayoutNode::new(&node);
let mut flow_constructor = FlowConstructor::new(layout_context);
flow_constructor.process(&ThreadSafeLayoutNode::new(&node));
flow_constructor.process(&node);
// Reset the layout damage in this node. It's been propagated to the
// flow by the flow constructor.
node.set_restyle_damage(RestyleDamage::empty());
}
unsafe {
node.set_dirty(false);
node.set_dirty_descendants(false);
}
// Reset the count of children for the next traversal.

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

@ -13,7 +13,8 @@ use libc::uintptr_t;
use script::dom::bindings::js::JS;
use script::dom::bindings::utils::Reflectable;
use script::dom::node::{Node, SharedLayoutData};
use script::layout_interface::{LayoutChan, UntrustedNodeAddress, TrustedNodeAddress};
use script::layout_interface::{LayoutChan, TrustedNodeAddress};
use script_traits::UntrustedNodeAddress;
use std::mem;
use std::cell::{Ref, RefMut};
use style::ComputedValues;
@ -29,7 +30,7 @@ pub struct PrivateLayoutData {
pub after_style: Option<Arc<ComputedValues>>,
/// Description of how to account for recent style changes.
pub restyle_damage: Option<RestyleDamage>,
pub restyle_damage: RestyleDamage,
/// The current results of flow construction for this node. This is either a flow or a
/// `ConstructionItem`. See comments in `construct.rs` for more details.
@ -49,7 +50,7 @@ impl PrivateLayoutData {
PrivateLayoutData {
before_style: None,
after_style: None,
restyle_damage: None,
restyle_damage: RestyleDamage::empty(),
flow_construction_result: NoConstructionResult,
before_flow_construction_result: NoConstructionResult,
after_flow_construction_result: NoConstructionResult,
@ -161,4 +162,3 @@ impl ToGfxColor for style::computed_values::RGBA {
gfx::color::rgba(self.red, self.green, self.blue, self.alpha)
}
}

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

@ -33,9 +33,11 @@
//! o Instead of `html_element_in_html_document()`, use
//! `html_element_in_html_document_for_layout()`.
use context::SharedLayoutContext;
use css::node_style::StyledNode;
use util::{LayoutDataAccess, LayoutDataWrapper, PrivateLayoutData};
use util::{LayoutDataAccess, LayoutDataWrapper, PrivateLayoutData, OpaqueNodeMethods};
use gfx::display_list::OpaqueNode;
use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived, HTMLInputElementDerived};
use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived};
use script::dom::bindings::js::JS;
@ -46,6 +48,7 @@ use script::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelp
use script::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId};
use script::dom::node::{LayoutNodeHelpers, RawLayoutNodeHelpers, SharedLayoutData, TextNodeTypeId};
use script::dom::node::{IsDirty, HasDirtyDescendants};
use script::dom::text::Text;
use script::layout_interface::LayoutChan;
use servo_msg::constellation_msg::{PipelineId, SubpageId};
@ -158,7 +161,6 @@ impl<'a> PartialEq for LayoutNode<'a> {
}
}
impl<'ln> TLayoutNode for LayoutNode<'ln> {
unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> LayoutNode<'ln> {
LayoutNode {
@ -249,6 +251,21 @@ impl<'ln> LayoutNode<'ln> {
Some(_) => {}
}
}
pub fn has_children(&self) -> bool {
self.first_child().is_some()
}
/// While doing a reflow, the node at the root has no parent, as far as we're
/// concerned. This method returns `None` at the reflow root.
pub fn layout_parent_node(&self, shared: &SharedLayoutContext) -> Option<LayoutNode<'ln>> {
let opaque_node: OpaqueNode = OpaqueNodeMethods::from_layout_node(self);
if opaque_node == shared.reflow_root {
None
} else {
self.parent_node()
}
}
}
impl<'ln> TNode<'ln, LayoutElement<'ln>> for LayoutNode<'ln> {
@ -325,6 +342,22 @@ impl<'ln> TNode<'ln, LayoutElement<'ln>> for LayoutNode<'ln> {
}
}
}
fn is_dirty(self) -> bool {
unsafe { self.node.get_flag(IsDirty) }
}
unsafe fn set_dirty(self, value: bool) {
self.node.set_flag(IsDirty, value)
}
fn has_dirty_descendants(self) -> bool {
unsafe { self.node.get_flag(HasDirtyDescendants) }
}
unsafe fn set_dirty_descendants(self, value: bool) {
self.node.set_flag(HasDirtyDescendants, value)
}
}
pub struct LayoutNodeChildrenIterator<'a> {
@ -855,4 +888,3 @@ pub unsafe fn layout_node_from_unsafe_layout_node(node: &UnsafeLayoutNode) -> La
let (node, _) = *node;
mem::transmute(node)
}

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

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

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

@ -227,8 +227,8 @@ impl ImageCache {
}
}
fn get_state(&self, url: Url) -> ImageState {
match self.state_map.find(&url) {
fn get_state(&self, url: &Url) -> ImageState {
match self.state_map.find(url) {
Some(state) => state.clone(),
None => Init
}
@ -239,7 +239,7 @@ impl ImageCache {
}
fn prefetch(&mut self, url: Url) {
match self.get_state(url.clone()) {
match self.get_state(&url) {
Init => {
let to_cache = self.chan.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>, ()>) {
match self.get_state(url.clone()) {
match self.get_state(&url) {
Prefetching(next_step) => {
match data {
Ok(data) => {
@ -298,7 +298,7 @@ impl ImageCache {
}
fn decode(&mut self, url: Url) {
match self.get_state(url.clone()) {
match self.get_state(&url) {
Init => fail!("decoding image before prefetch"),
Prefetching(DoNotDecode) => {
@ -338,7 +338,7 @@ impl ImageCache {
fn store_image(&mut self, url: Url, image: Option<Arc<Box<Image>>>) {
match self.get_state(url.clone()) {
match self.get_state(&url) {
Decoding => {
match image {
Some(image) => {
@ -376,7 +376,7 @@ impl ImageCache {
}
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"),
Prefetching(DoDecode) => response.send(ImageNotReady),
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>) {
match self.get_state(url.clone()) {
match self.get_state(&url) {
Init => fail!("request for image before prefetch"),
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 url::Url;
pub trait ImageResponder {
fn respond(&self) -> proc(ImageResponseMsg):Send;
pub trait ImageResponder<NodeAddress: Send> {
fn respond(&self) -> proc(ImageResponseMsg, NodeAddress):Send;
}
pub struct LocalImageCache {
pub struct LocalImageCache<NodeAddress> {
image_cache_task: ImageCacheTask,
round_number: uint,
on_image_available: Option<Box<ImageResponder+Send>>,
on_image_available: Option<Box<ImageResponder<NodeAddress>+Send>>,
state_map: HashMap<Url, ImageState>
}
impl LocalImageCache {
pub fn new(image_cache_task: ImageCacheTask) -> LocalImageCache {
impl<NodeAddress: Send> LocalImageCache<NodeAddress> {
pub fn new(image_cache_task: ImageCacheTask) -> LocalImageCache<NodeAddress> {
LocalImageCache {
image_cache_task: image_cache_task,
round_number: 1,
@ -46,10 +46,10 @@ struct ImageState {
last_response: ImageResponseMsg
}
impl LocalImageCache {
impl<NodeAddress: Send> LocalImageCache<NodeAddress> {
/// 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
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.on_image_available = Some(on_image_available);
}
@ -80,7 +80,7 @@ impl LocalImageCache {
}
// 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 state = self.get_state(url);
@ -127,12 +127,13 @@ impl LocalImageCache {
// on the image to load and triggering layout
let image_cache_task = self.image_cache_task.clone();
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();
spawn_named("LocalImageCache", proc() {
let (response_chan, response_port) = channel();
image_cache_task.send(WaitForImage(url.clone(), response_chan));
on_image_available(response_port.recv());
image_cache_task.send(WaitForImage(url, response_chan));
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::method::Method;
use std::io::timer::Timer;
use script_traits::UntrustedNodeAddress;
use servo_msg::compositor_msg::ScriptListener;
use servo_msg::constellation_msg::ConstellationChan;
use servo_util::smallvec::{SmallVec1, SmallVec};
use layout_interface::{LayoutRPC, LayoutChan};
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> {
#[inline]
fn trace(&self, trc: *mut JSTracer) {
@ -192,6 +205,7 @@ untraceable!(ResponseHeaderCollection, RequestHeaderCollection, Method)
untraceable!(ConstellationChan)
untraceable!(LayoutChan)
untraceable!(WindowProxyHandler)
untraceable!(UntrustedNodeAddress)
impl<'a> JSTraceable for &'a str {
#[inline]

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

@ -54,7 +54,6 @@ use dom::uievent::UIEvent;
use dom::window::{Window, WindowHelpers};
use html::hubbub_html_parser::build_element_from_tag;
use hubbub::hubbub::{QuirksMode, NoQuirks, LimitedQuirks, FullQuirks};
use layout_interface::{DocumentDamageLevel, ContentChangedDocumentDamage};
use servo_util::namespace;
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_last_modified(self, value: DOMString);
fn set_encoding_name(self, name: DOMString);
fn content_changed(self);
fn damage_and_reflow(self, damage: DocumentDamageLevel);
fn content_changed(self, node: JSRef<Node>);
fn reflow(self);
fn wait_until_safe_to_modify_dom(self);
fn unregister_named_element(self, to_unregister: 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;
}
fn content_changed(self) {
self.damage_and_reflow(ContentChangedDocumentDamage);
fn content_changed(self, node: JSRef<Node>) {
node.dirty();
self.reflow();
}
fn damage_and_reflow(self, damage: DocumentDamageLevel) {
self.window.root().damage_and_reflow(damage);
fn reflow(self) {
self.window.root().reflow();
}
fn wait_until_safe_to_modify_dom(self) {
self.window.root().wait_until_safe_to_modify_dom();
}
/// Remove any existing association between the provided id and any elements in this document.
fn unregister_named_element(self,
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::nodelist::NodeList;
use dom::virtualmethods::{VirtualMethods, vtable_for};
use layout_interface::ContentChangedDocumentDamage;
use layout_interface::MatchSelectorsDocumentDamage;
use devtools_traits::AttrInfo;
use style::{matches, parse_selector_list_from_str};
use style;
@ -323,6 +321,7 @@ pub trait AttributeHandlers {
fn remove_attribute(self, namespace: Namespace, name: &str);
fn notify_attribute_changed(self, local_name: &Atom);
fn has_class(&self, name: &str) -> bool;
fn notify_attribute_removed(self);
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.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);
if node.is_in_doc() {
let damage = match local_name.as_slice() {
"style" | "id" | "class" => MatchSelectorsDocumentDamage,
_ => ContentChangedDocumentDamage
};
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> {
fn force_relayout(self) {
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>) {

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

@ -46,8 +46,9 @@ use dom::window::Window;
use geom::rect::Rect;
use html::hubbub_html_parser::build_element_from_tag;
use layout_interface::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC,
LayoutChan, ReapLayoutDataMsg, TrustedNodeAddress, UntrustedNodeAddress};
LayoutChan, ReapLayoutDataMsg, TrustedNodeAddress};
use devtools_traits::NodeInfo;
use script_traits::UntrustedNodeAddress;
use servo_util::geometry::Au;
use servo_util::str::{DOMString, null_str_as_empty};
use style::{parse_selector_list_from_str, matches};
@ -56,7 +57,7 @@ use js::jsapi::{JSContext, JSObject, JSTracer, JSRuntime};
use js::jsfriendapi;
use libc;
use libc::uintptr_t;
use std::cell::{RefCell, Ref, RefMut};
use std::cell::{Cell, RefCell, Ref, RefMut};
use std::default::Default;
use std::iter::{Map, Filter};
use std::mem;
@ -101,7 +102,7 @@ pub struct Node {
child_list: MutNullableJS<NodeList>,
/// A bitfield of flags for node items.
flags: RefCell<NodeFlags>,
flags: Cell<NodeFlags>,
/// Layout information. Only the layout task may touch this data.
///
@ -132,14 +133,19 @@ bitflags! {
#[doc = "Specifies whether this node is in disabled state."]
static InDisabledState = 0x04,
#[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 {
pub fn new(type_id: NodeTypeId) -> NodeFlags {
match type_id {
DocumentNodeTypeId => IsInDoc,
DocumentNodeTypeId => IsInDoc | IsDirty,
// The following elements are enabled by default.
ElementNodeTypeId(HTMLButtonElementTypeId) |
ElementNodeTypeId(HTMLInputElementTypeId) |
@ -148,8 +154,8 @@ impl NodeFlags {
ElementNodeTypeId(HTMLOptGroupElementTypeId) |
ElementNodeTypeId(HTMLOptionElementTypeId) |
//ElementNodeTypeId(HTMLMenuItemElementTypeId) |
ElementNodeTypeId(HTMLFieldSetElementTypeId) => InEnabledState,
_ => NodeFlags::empty(),
ElementNodeTypeId(HTMLFieldSetElementTypeId) => InEnabledState | IsDirty,
_ => IsDirty,
}
}
}
@ -271,7 +277,7 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> {
let parent = self.parent_node().root();
parent.map(|parent| vtable_for(&*parent).child_inserted(self));
document.content_changed();
document.content_changed(self);
}
// 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);
}
document.content_changed();
document.content_changed(self);
}
//
@ -395,6 +401,9 @@ pub trait NodeHelpers<'a> {
fn is_text(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 set_hover_state(self, state: bool);
@ -404,6 +413,17 @@ pub trait NodeHelpers<'a> {
fn get_enabled_state(self) -> 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_indent(self, indent: uint);
fn debug_str(self) -> String;
@ -426,6 +446,7 @@ pub trait NodeHelpers<'a> {
fn summarize(self) -> NodeInfo;
}
impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
/// Dumps the subtree rooted at this node, for debugging.
fn dump(self) {
@ -454,7 +475,7 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
}
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.
@ -512,39 +533,98 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
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 {
self.flags.borrow().contains(InHoverState)
self.get_flag(InHoverState)
}
fn set_hover_state(self, state: bool) {
if state {
self.flags.borrow_mut().insert(InHoverState);
} else {
self.flags.borrow_mut().remove(InHoverState);
}
self.set_flag(InHoverState, state)
}
fn get_disabled_state(self) -> bool {
self.flags.borrow().contains(InDisabledState)
self.get_flag(InDisabledState)
}
fn set_disabled_state(self, state: bool) {
if state {
self.flags.borrow_mut().insert(InDisabledState);
} else {
self.flags.borrow_mut().remove(InDisabledState);
}
self.set_flag(InDisabledState, state)
}
fn get_enabled_state(self) -> bool {
self.flags.borrow().contains(InEnabledState)
self.get_flag(InEnabledState)
}
fn set_enabled_state(self, state: bool) {
if state {
self.flags.borrow_mut().insert(InEnabledState);
} else {
self.flags.borrow_mut().remove(InEnabledState);
self.set_flag(InEnabledState, state)
}
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
}
}
}
/// 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 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> {
@ -806,6 +889,25 @@ impl LayoutNodeHelpers for JS<Node> {
unsafe fn owner_doc_for_layout(&self) -> JS<Document> {
(*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 {
@ -1034,8 +1136,7 @@ impl Node {
prev_sibling: Default::default(),
owner_doc: MutNullableJS::new(doc),
child_list: Default::default(),
flags: RefCell::new(NodeFlags::new(type_id)),
flags: Cell::new(NodeFlags::new(type_id)),
layout_data: LayoutDataRef::new(),
@ -1236,11 +1337,13 @@ impl Node {
parent.add_child(*node, child);
let is_in_doc = parent.is_in_doc();
for kid in node.traverse_preorder() {
let mut flags = kid.flags.get();
if is_in_doc {
kid.flags.borrow_mut().insert(IsInDoc);
flags.insert(IsInDoc);
} else {
kid.flags.borrow_mut().remove(IsInDoc);
flags.remove(IsInDoc);
}
kid.flags.set(flags);
}
}
@ -1326,7 +1429,7 @@ impl Node {
// Step 8.
parent.remove_child(node);
node.flags.borrow_mut().remove(IsInDoc);
node.set_flag(IsInDoc, false);
// Step 9.
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
let document = self.owner_doc().root();
document.content_changed();
document.content_changed(self);
}
DoctypeNodeTypeId |
DocumentNodeTypeId => {}
@ -2120,6 +2223,12 @@ impl<'a> style::TNode<'a, JSRef<'a, Element>> for JSRef<'a, Node> {
assert!(elem.is_some());
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 {

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

@ -18,7 +18,7 @@ use dom::location::Location;
use dom::navigator::Navigator;
use dom::performance::Performance;
use dom::screen::Screen;
use layout_interface::{ReflowGoal, DocumentDamageLevel};
use layout_interface::{ReflowGoal, ReflowForDisplay};
use page::Page;
use script_task::{ExitWindowMsg, FireTimerMsg, ScriptChan, TriggerLoadMsg, TriggerFragmentMsg};
use script_traits::ScriptControlChan;
@ -366,7 +366,7 @@ impl Reflectable for Window {
}
pub trait WindowHelpers {
fn damage_and_reflow(self, damage: DocumentDamageLevel);
fn reflow(self);
fn flush_layout(self, goal: ReflowGoal);
fn wait_until_safe_to_modify_dom(self);
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) {
self.page().damage(damage);
self.page().avoided_reflows.set(self.page().avoided_reflows.get() + 1);
fn reflow(self) {
self.page().damage();
// 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) {

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

@ -14,11 +14,10 @@ use geom::point::Point2D;
use geom::rect::Rect;
use js::jsapi::JSTracer;
use libc::c_void;
use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel};
use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel, UntrustedNodeAddress};
use servo_msg::constellation_msg::WindowSizeData;
use servo_util::geometry::Au;
use std::any::{Any, AnyRefExt};
use std::cmp;
use std::comm::{channel, Receiver, Sender};
use std::owned::BoxAny;
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 ContentBoxesResponse(pub Vec<Rect<Au>>);
pub struct HitTestResponse(pub 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.
#[deriving(PartialEq)]
#[deriving(PartialEq, Show)]
pub enum ReflowGoal {
/// We're reflowing in order to send a display list to the screen.
ReflowForDisplay,
@ -137,8 +102,6 @@ pub enum ReflowGoal {
pub struct Reflow {
/// The document node.
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.
pub goal: ReflowGoal,
/// The URL of the page.
@ -190,21 +153,3 @@ impl ScriptLayoutChan for OpaqueScriptLayoutChannel {
*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::node::{Node, NodeHelpers};
use dom::window::Window;
use layout_interface::{DocumentDamage, ReflowForDisplay};
use layout_interface::{DocumentDamageLevel, HitTestResponse, MouseOverResponse};
use layout_interface::{ReflowForDisplay};
use layout_interface::{HitTestResponse, MouseOverResponse};
use layout_interface::{GetRPCMsg, LayoutChan, LayoutRPC};
use layout_interface::{Reflow, ReflowGoal, ReflowMsg};
use layout_interface::UntrustedNodeAddress;
use script_traits::ScriptControlChan;
use script_traits::{UntrustedNodeAddress, ScriptControlChan};
use geom::point::Point2D;
use js::rust::Cx;
@ -25,6 +24,7 @@ use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData};
use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_net::resource_task::ResourceTask;
use servo_util::str::DOMString;
use servo_util::smallvec::{SmallVec1, SmallVec};
use std::cell::{Cell, RefCell, Ref, RefMut};
use std::comm::{channel, Receiver, Empty, Disconnected};
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.
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.
pub window_size: Cell<WindowSizeData>,
@ -74,6 +71,9 @@ pub struct Page {
/// Pending resize event, if any.
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
pub fragment_name: RefCell<Option<String>>,
@ -86,6 +86,9 @@ pub struct Page {
// Child Pages.
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.
pub pending_reflows: Cell<int>,
@ -143,25 +146,25 @@ impl Page {
layout_chan: layout_chan,
layout_rpc: layout_rpc,
layout_join_port: RefCell::new(None),
damage: RefCell::new(None),
window_size: Cell::new(window_size),
js_info: RefCell::new(Some(js_info)),
url: RefCell::new(None),
next_subpage_id: Cell::new(SubpageId(0)),
resize_event: Cell::new(None),
pending_dirty_nodes: RefCell::new(SmallVec1::new()),
fragment_name: RefCell::new(None),
last_reflow_id: Cell::new(0),
resource_task: resource_task,
constellation_chan: constellation_chan,
children: RefCell::new(vec!()),
damaged: Cell::new(false),
pending_reflows: Cell::new(0),
avoided_reflows: Cell::new(0),
}
}
pub fn flush_layout(&self, goal: ReflowGoal) {
let damaged = self.damage.borrow().is_some();
if damaged {
if self.damaged.get() {
let frame = self.frame();
let window = frame.as_ref().unwrap().window.root();
self.reflow(goal, window.control_chan.clone(), &*window.compositor);
@ -255,35 +258,6 @@ impl Page {
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 {
self.url().as_ref().unwrap().ref0().clone()
}
@ -360,8 +334,9 @@ impl Page {
last_reflow_id.set(last_reflow_id.get() + 1);
let root: JSRef<Node> = NodeCast::from_ref(*root);
let mut damage = self.damage.borrow_mut();
let window_size = self.window_size.get();
self.damaged.set(false);
// Send new document and relevant styles to layout.
let reflow = box Reflow {
@ -372,7 +347,6 @@ impl Page {
window_size: window_size,
script_chan: script_chan,
script_join_chan: join_chan,
damage: replace(&mut *damage, None).unwrap(),
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.
pub fn find_fragment_node(&self, fragid: DOMString) -> Option<Temporary<Element>> {
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 html::hubbub_html_parser::{InputString, InputUrl, HtmlParserResult, HtmlDiscoveredScript};
use html::hubbub_html_parser;
use layout_interface::{ScriptLayoutChan, LayoutChan, MatchSelectorsDocumentDamage};
use layout_interface::{ReflowDocumentDamage, ReflowForDisplay};
use layout_interface::ContentChangedDocumentDamage;
use layout_interface::{ScriptLayoutChan, LayoutChan, ReflowForDisplay};
use layout_interface;
use page::{Page, IterablePage, Frame};
@ -52,6 +50,7 @@ use servo_msg::constellation_msg;
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask;
use servo_util::geometry::to_frac_px;
use servo_util::smallvec::{SmallVec1, SmallVec};
use servo_util::task::spawn_named_with_send_on_failure;
use geom::point::Point2D;
@ -66,6 +65,7 @@ use url::Url;
use libc::size_t;
use std::any::{Any, AnyRefExt};
use std::cell::RefCell;
use std::collections::HashSet;
use std::comm::{channel, Sender, Receiver, Select};
use std::mem::replace;
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 {
match event {
// 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");
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);
}
@ -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
}
@ -632,8 +646,7 @@ impl ScriptTask {
if page.pending_reflows.get() > 0 {
page.pending_reflows.set(0);
page.damage(MatchSelectorsDocumentDamage);
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor);
self.force_reflow(&*page);
}
}
@ -711,8 +724,7 @@ impl ScriptTask {
Some((ref loaded, needs_reflow)) if *loaded == url => {
*page.mut_url() = Some((loaded.clone(), false));
if needs_reflow {
page.damage(ContentChangedDocumentDamage);
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor);
self.force_reflow(&*page);
}
return;
},
@ -796,7 +808,11 @@ impl ScriptTask {
// Kick off the initial reflow of the page.
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);
{
@ -856,6 +872,21 @@ impl ScriptTask {
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.
///
/// TODO: Actually perform DOM event dispatch.
@ -870,8 +901,7 @@ impl ScriptTask {
let frame = page.frame();
if frame.is_some() {
page.damage(ReflowDocumentDamage);
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor)
self.force_reflow(&*page);
}
let fragment_node =
@ -906,8 +936,9 @@ impl ScriptTask {
}
// FIXME(pcwalton): This reflows the entire document and is not incremental-y.
ReflowEvent => {
ReflowEvent(to_dirty) => {
debug!("script got reflow event");
assert_eq!(to_dirty.len(), 0);
let page = get_page(&*self.page.borrow(), pipeline_id);
let frame = page.frame();
if frame.is_some() {
@ -915,8 +946,7 @@ impl ScriptTask {
if in_layout {
page.pending_reflows.set(page.pending_reflows.get() + 1);
} else {
page.damage(MatchSelectorsDocumentDamage);
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor)
self.force_reflow(&*page);
}
}
}
@ -1021,8 +1051,7 @@ impl ScriptTask {
if target_compare {
if mouse_over_targets.is_some() {
page.damage(MatchSelectorsDocumentDamage);
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor);
self.force_reflow(&*page);
}
*mouse_over_targets = Some(target_list);
}

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

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

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

@ -9,8 +9,10 @@
extern crate devtools_traits;
extern crate geom;
extern crate libc;
extern crate "msg" as servo_msg;
extern crate "net" as servo_net;
extern crate "util" as servo_util;
extern crate url;
extern crate serialize;
@ -20,11 +22,13 @@ extern crate serialize;
// that these modules won't have to depend on script.
use devtools_traits::DevtoolsControlChan;
use libc::c_void;
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, WindowSizeData};
use servo_msg::constellation_msg::SubpageId;
use servo_msg::compositor_msg::ScriptListener;
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask;
use servo_util::smallvec::SmallVec1;
use std::any::Any;
use url::Url;
@ -32,6 +36,10 @@ use geom::point::Point2D;
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 old_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
pub enum CompositorEvent {
ResizeEvent(WindowSizeData),
ReflowEvent,
ReflowEvent(SmallVec1<UntrustedNodeAddress>),
ClickEvent(uint, Point2D<f32>),
MouseDownEvent(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 match_attr(self, attr: &AttrSelector, test: |&str| -> bool) -> 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 {