Auto merge of #9110 - bholley:hoist_matching_and_animation, r=SimonSapin

Hoist the rest of css/matching.rs (and its dependencies) into style/.

Slowly but surely.

This goes on top of #9103.

Fixes #9103.

<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/9110)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-01-04 23:34:35 +05:30
commit f3075d1319
20 changed files with 762 additions and 865 deletions

View file

@ -6,54 +6,16 @@
use flow::{self, Flow}; use flow::{self, Flow};
use gfx::display_list::OpaqueNode; use gfx::display_list::OpaqueNode;
use incremental::{self, RestyleDamage}; use incremental::RestyleDamage;
use msg::constellation_msg::{AnimationState, ConstellationChan, PipelineId}; use msg::constellation_msg::{AnimationState, ConstellationChan, PipelineId};
use script::layout_interface::Animation; use script::layout_interface::Animation;
use script_traits::LayoutMsg as ConstellationMsg; use script_traits::LayoutMsg as ConstellationMsg;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::sync::mpsc::{Sender, Receiver}; use std::sync::mpsc::Receiver;
use std::sync::{Arc, Mutex}; use style::animation::update_style_for_animation;
use style::animation::{GetMod, PropertyAnimation};
use style::properties::ComputedValues;
use time; use time;
/// Inserts transitions into the queue of running animations as applicable for the given style
/// difference. This is called from the layout worker threads. Returns true if any animations were
/// kicked off and false otherwise.
pub fn start_transitions_if_applicable(new_animations_sender: &Mutex<Sender<Animation>>,
node: OpaqueNode,
old_style: &ComputedValues,
new_style: &mut ComputedValues)
-> bool {
let mut had_animations = false;
for i in 0..new_style.get_animation().transition_property.0.len() {
// Create any property animations, if applicable.
let property_animations = PropertyAnimation::from_transition(i, old_style, new_style);
for property_animation in property_animations {
// Set the property to the initial value.
property_animation.update(new_style, 0.0);
// Kick off the animation.
let now = time::precise_time_s();
let animation_style = new_style.get_animation();
let start_time =
now + (animation_style.transition_delay.0.get_mod(i).seconds() as f64);
new_animations_sender.lock().unwrap().send(Animation {
node: node,
property_animation: property_animation,
start_time: start_time,
end_time: start_time +
(animation_style.transition_duration.0.get_mod(i).seconds() as f64),
}).unwrap();
had_animations = true
}
}
had_animations
}
/// Processes any new animations that were discovered after style recalculation. /// Processes any new animations that were discovered after style recalculation.
/// Also expire any old animations that have completed, inserting them into `expired_animations`. /// Also expire any old animations that have completed, inserting them into `expired_animations`.
pub fn update_animation_state(constellation_chan: &ConstellationChan<ConstellationMsg>, pub fn update_animation_state(constellation_chan: &ConstellationChan<ConstellationMsg>,
@ -139,26 +101,3 @@ pub fn recalc_style_for_animations(flow: &mut Flow,
recalc_style_for_animations(kid, animations) recalc_style_for_animations(kid, animations)
} }
} }
/// Updates a single animation and associated style based on the current time. If `damage` is
/// provided, inserts the appropriate restyle damage.
pub fn update_style_for_animation(animation: &Animation,
style: &mut Arc<ComputedValues>,
damage: Option<&mut RestyleDamage>) {
let now = time::precise_time_s();
let mut progress = (now - animation.start_time) / animation.duration();
if progress > 1.0 {
progress = 1.0
}
if progress <= 0.0 {
return
}
let mut new_style = (*style).clone();
animation.property_animation.update(&mut *Arc::make_mut(&mut new_style), progress);
if let Some(damage) = damage {
damage.insert(incremental::compute_damage(&Some((*style).clone()), &new_style));
}
*style = new_style
}

View file

@ -1,432 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
//! High-level interface to CSS selector matching.
#![allow(unsafe_code)]
use animation;
use context::SharedLayoutContext;
use data::PrivateLayoutData;
use incremental::{self, RestyleDamage};
use msg::ParseErrorReporter;
use script::layout_interface::Animation;
use selectors::bloom::BloomFilter;
use selectors::parser::PseudoElement;
use selectors::{Element};
use std::mem::transmute;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use style::data::PrivateStyleData;
use style::dom::{TElement, TNode};
use style::matching::{ApplicableDeclarations, ApplicableDeclarationsCache};
use style::matching::{StyleSharingCandidate, StyleSharingCandidateCache};
use style::properties::{ComputedValues, cascade};
use style::selector_matching::{DeclarationBlock, Stylist};
use util::arc_ptr_eq;
use util::opts;
/// The results of attempting to share a style.
pub enum StyleSharingResult {
/// We didn't find anybody to share the style with.
CannotShare,
/// The node's style can be shared. The integer specifies the index in the LRU cache that was
/// hit and the damage that was done.
StyleWasShared(usize, RestyleDamage),
}
pub trait ElementMatchMethods<'le, ConcreteElement: TElement<'le>> {
fn match_element(&self,
stylist: &Stylist,
parent_bf: Option<&BloomFilter>,
applicable_declarations: &mut ApplicableDeclarations)
-> bool;
/// Attempts to share a style with another node. This method is unsafe because it depends on
/// the `style_sharing_candidate_cache` having only live nodes in it, and we have no way to
/// guarantee that at the type system level yet.
unsafe fn share_style_if_possible(&self,
style_sharing_candidate_cache:
&mut StyleSharingCandidateCache,
parent: Option<ConcreteElement::ConcreteNode>)
-> StyleSharingResult;
}
pub trait MatchMethods<'ln, ConcreteNode: TNode<'ln>> {
/// Inserts and removes the matching `Descendant` selectors from a bloom
/// filter. This is used to speed up CSS selector matching to remove
/// unnecessary tree climbs for `Descendant` queries.
///
/// A bloom filter of the local names, namespaces, IDs, and classes is kept.
/// Therefore, each node must have its matching selectors inserted _after_
/// its own selector matching and _before_ its children start.
fn insert_into_bloom_filter(&self, bf: &mut BloomFilter);
/// After all the children are done css selector matching, this must be
/// called to reset the bloom filter after an `insert`.
fn remove_from_bloom_filter(&self, bf: &mut BloomFilter);
unsafe fn cascade_node(&self,
layout_context: &SharedLayoutContext,
parent: Option<ConcreteNode>,
applicable_declarations: &ApplicableDeclarations,
applicable_declarations_cache: &mut ApplicableDeclarationsCache,
new_animations_sender: &Mutex<Sender<Animation>>);
}
trait PrivateMatchMethods {
fn cascade_node_pseudo_element(&self,
layout_context: &SharedLayoutContext,
parent_style: Option<&Arc<ComputedValues>>,
applicable_declarations: &[DeclarationBlock],
style: &mut Option<Arc<ComputedValues>>,
applicable_declarations_cache:
&mut ApplicableDeclarationsCache,
new_animations_sender: &Mutex<Sender<Animation>>,
shareable: bool,
animate_properties: bool)
-> RestyleDamage;
fn update_animations_for_cascade(&self,
layout_context: &SharedLayoutContext,
style: &mut Option<Arc<ComputedValues>>)
-> bool;
}
trait PrivateElementMatchMethods<'le, ConcreteElement: TElement<'le>> {
fn share_style_with_candidate_if_possible(&self,
parent_node: Option<ConcreteElement::ConcreteNode>,
candidate: &StyleSharingCandidate)
-> Option<Arc<ComputedValues>>;
}
impl<'ln, ConcreteNode> PrivateMatchMethods for ConcreteNode
where ConcreteNode: TNode<'ln> {
fn cascade_node_pseudo_element(&self,
layout_context: &SharedLayoutContext,
parent_style: Option<&Arc<ComputedValues>>,
applicable_declarations: &[DeclarationBlock],
style: &mut Option<Arc<ComputedValues>>,
applicable_declarations_cache:
&mut ApplicableDeclarationsCache,
new_animations_sender: &Mutex<Sender<Animation>>,
shareable: bool,
animate_properties: bool)
-> RestyleDamage {
let mut cacheable = true;
if animate_properties {
cacheable = !self.update_animations_for_cascade(layout_context, style) && cacheable;
}
let mut this_style;
match parent_style {
Some(ref parent_style) => {
let cache_entry = applicable_declarations_cache.find(applicable_declarations);
let cached_computed_values = match cache_entry {
None => None,
Some(ref style) => Some(&**style),
};
let (the_style, is_cacheable) = cascade(layout_context.style_context.viewport_size,
applicable_declarations,
shareable,
Some(&***parent_style),
cached_computed_values,
layout_context.style_context.error_reporter.clone());
cacheable = cacheable && is_cacheable;
this_style = the_style
}
None => {
let (the_style, is_cacheable) = cascade(layout_context.style_context.viewport_size,
applicable_declarations,
shareable,
None,
None,
layout_context.style_context.error_reporter.clone());
cacheable = cacheable && is_cacheable;
this_style = the_style
}
};
// Trigger transitions if necessary. This will reset `this_style` back to its old value if
// it did trigger a transition.
if animate_properties {
if let Some(ref style) = *style {
let animations_started =
animation::start_transitions_if_applicable(new_animations_sender,
self.opaque(),
&**style,
&mut this_style);
cacheable = cacheable && !animations_started
}
}
// Calculate style difference.
let this_style = Arc::new(this_style);
let damage = incremental::compute_damage(style, &*this_style);
// Cache the resolved style if it was cacheable.
if cacheable {
applicable_declarations_cache.insert(applicable_declarations.to_vec(),
this_style.clone());
}
// Write in the final style and return the damage done to our caller.
*style = Some(this_style);
damage
}
fn update_animations_for_cascade(&self,
layout_context: &SharedLayoutContext,
style: &mut Option<Arc<ComputedValues>>)
-> bool {
let style = match *style {
None => return false,
Some(ref mut style) => style,
};
// Finish any expired transitions.
let this_opaque = self.opaque();
let had_animations_to_expire;
{
let all_expired_animations = layout_context.style_context.expired_animations.read().unwrap();
let animations_to_expire = all_expired_animations.get(&this_opaque);
had_animations_to_expire = animations_to_expire.is_some();
if let Some(ref animations) = animations_to_expire {
for animation in *animations {
animation.property_animation.update(&mut *Arc::make_mut(style), 1.0);
}
}
}
if had_animations_to_expire {
layout_context.style_context.expired_animations.write().unwrap().remove(&this_opaque);
}
// Merge any running transitions into the current style, and cancel them.
let had_running_animations = layout_context.style_context
.running_animations
.read()
.unwrap()
.get(&this_opaque)
.is_some();
if had_running_animations {
let mut all_running_animations = layout_context.style_context.running_animations.write().unwrap();
for running_animation in all_running_animations.get(&this_opaque).unwrap() {
animation::update_style_for_animation(running_animation, style, None);
}
all_running_animations.remove(&this_opaque);
}
had_animations_to_expire || had_running_animations
}
}
impl<'le, ConcreteElement> PrivateElementMatchMethods<'le, ConcreteElement>
for ConcreteElement
where ConcreteElement: TElement<'le> {
fn share_style_with_candidate_if_possible(&self,
parent_node: Option<ConcreteElement::ConcreteNode>,
candidate: &StyleSharingCandidate)
-> Option<Arc<ComputedValues>> {
let parent_node = match parent_node {
Some(ref parent_node) if parent_node.as_element().is_some() => parent_node,
Some(_) | None => return None,
};
let parent_data: Option<&PrivateStyleData> = unsafe {
parent_node.borrow_data_unchecked().map(|d| &*d)
};
if let Some(parent_data_ref) = parent_data {
// Check parent style.
let parent_style = (*parent_data_ref).style.as_ref().unwrap();
if !arc_ptr_eq(parent_style, &candidate.parent_style) {
return None
}
// Check tag names, classes, etc.
if !candidate.can_share_style_with(self) {
return None
}
return Some(candidate.style.clone())
}
None
}
}
impl<'le, ConcreteElement> ElementMatchMethods<'le, ConcreteElement>
for ConcreteElement
where ConcreteElement: TElement<'le> {
fn match_element(&self,
stylist: &Stylist,
parent_bf: Option<&BloomFilter>,
applicable_declarations: &mut ApplicableDeclarations)
-> bool {
let style_attribute = self.style_attribute().as_ref();
applicable_declarations.normal_shareable =
stylist.push_applicable_declarations(self,
parent_bf,
style_attribute,
None,
&mut applicable_declarations.normal);
stylist.push_applicable_declarations(self,
parent_bf,
None,
Some(PseudoElement::Before),
&mut applicable_declarations.before);
stylist.push_applicable_declarations(self,
parent_bf,
None,
Some(PseudoElement::After),
&mut applicable_declarations.after);
applicable_declarations.normal_shareable &&
applicable_declarations.before.is_empty() &&
applicable_declarations.after.is_empty()
}
unsafe fn share_style_if_possible(&self,
style_sharing_candidate_cache:
&mut StyleSharingCandidateCache,
parent: Option<ConcreteElement::ConcreteNode>)
-> StyleSharingResult {
if opts::get().disable_share_style_cache {
return StyleSharingResult::CannotShare
}
if self.style_attribute().is_some() {
return StyleSharingResult::CannotShare
}
if self.get_attr(&ns!(), &atom!("id")).is_some() {
return StyleSharingResult::CannotShare
}
for (i, &(ref candidate, ())) in style_sharing_candidate_cache.iter().enumerate() {
match self.share_style_with_candidate_if_possible(parent.clone(), candidate) {
Some(shared_style) => {
// Yay, cache hit. Share the style.
let node = self.as_node();
let style = &mut node.mutate_data().unwrap().style;
let damage = incremental::compute_damage(style, &*shared_style);
*style = Some(shared_style);
return StyleSharingResult::StyleWasShared(i, damage)
}
None => {}
}
}
StyleSharingResult::CannotShare
}
}
impl<'ln, ConcreteNode> MatchMethods<'ln, ConcreteNode>
for ConcreteNode
where ConcreteNode: TNode<'ln> {
// The below two functions are copy+paste because I can't figure out how to
// write a function which takes a generic function. I don't think it can
// be done.
//
// Ideally, I'd want something like:
//
// > fn with_really_simple_selectors(&self, f: <H: Hash>|&H|);
// In terms of `SimpleSelector`s, these two functions will insert and remove:
// - `SimpleSelector::LocalName`
// - `SimpleSelector::Namepace`
// - `SimpleSelector::ID`
// - `SimpleSelector::Class`
fn insert_into_bloom_filter(&self, bf: &mut BloomFilter) {
// Only elements are interesting.
if let Some(element) = self.as_element() {
bf.insert(element.get_local_name());
bf.insert(element.get_namespace());
element.get_id().map(|id| bf.insert(&id));
// TODO: case-sensitivity depends on the document type and quirks mode
element.each_class(|class| bf.insert(class));
}
}
fn remove_from_bloom_filter(&self, bf: &mut BloomFilter) {
// Only elements are interesting.
if let Some(element) = self.as_element() {
bf.remove(element.get_local_name());
bf.remove(element.get_namespace());
element.get_id().map(|id| bf.remove(&id));
// TODO: case-sensitivity depends on the document type and quirks mode
element.each_class(|class| bf.remove(class));
}
}
unsafe fn cascade_node(&self,
layout_context: &SharedLayoutContext,
parent: Option<ConcreteNode>,
applicable_declarations: &ApplicableDeclarations,
applicable_declarations_cache: &mut ApplicableDeclarationsCache,
new_animations_sender: &Mutex<Sender<Animation>>) {
// Get our parent's style. This must be unsafe so that we don't touch the parent's
// borrow flags.
//
// FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow
// enforced safe, race-free access to the parent style.
let parent_style = match parent {
None => None,
Some(parent_node) => {
let parent_style = (*parent_node.borrow_data_unchecked().unwrap()).style.as_ref().unwrap();
Some(parent_style)
}
};
let mut data_ref = self.mutate_data().unwrap();
let mut data = &mut *data_ref;
if self.is_text_node() {
// Text nodes get a copy of the parent style. This ensures
// that during fragment construction any non-inherited
// CSS properties (such as vertical-align) are correctly
// set on the fragment(s).
let cloned_parent_style = parent_style.unwrap().clone();
data.style = Some(cloned_parent_style);
} else {
let mut damage = self.cascade_node_pseudo_element(
layout_context,
parent_style,
&applicable_declarations.normal,
&mut data.style,
applicable_declarations_cache,
new_animations_sender,
applicable_declarations.normal_shareable,
true);
if !applicable_declarations.before.is_empty() {
damage = damage | self.cascade_node_pseudo_element(
layout_context,
Some(data.style.as_ref().unwrap()),
&*applicable_declarations.before,
&mut data.before_style,
applicable_declarations_cache,
new_animations_sender,
false,
false);
}
if !applicable_declarations.after.is_empty() {
damage = damage | self.cascade_node_pseudo_element(
layout_context,
Some(data.style.as_ref().unwrap()),
&*applicable_declarations.after,
&mut data.after_style,
applicable_declarations_cache,
new_animations_sender,
false,
false);
}
// FIXME(bholley): This is the only dependency in this file on non-style
// stuff.
let layout_data: &mut PrivateLayoutData = transmute(data);
layout_data.restyle_damage = damage;
}
}
}

View file

@ -35,7 +35,7 @@ use flow_ref::{self, FlowRef, WeakFlowRef};
use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
use gfx::display_list::{ClippingRegion, DisplayList}; use gfx::display_list::{ClippingRegion, DisplayList};
use gfx_traits::LayerId; use gfx_traits::LayerId;
use incremental::{self, RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage}; use incremental::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage};
use inline::InlineFlow; use inline::InlineFlow;
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo}; use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
use msg::compositor_msg::LayerType; use msg::compositor_msg::LayerType;
@ -48,6 +48,7 @@ use std::sync::Arc;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::{fmt, mem, raw}; use std::{fmt, mem, raw};
use style::computed_values::{clear, display, empty_cells, float, position, text_align}; use style::computed_values::{clear, display, empty_cells, float, position, text_align};
use style::dom::TRestyleDamage;
use style::properties::{self, ComputedValues}; use style::properties::{self, ComputedValues};
use style::values::computed::LengthOrPercentageOrAuto; use style::values::computed::LengthOrPercentageOrAuto;
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, TableFlow}; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, TableFlow};
@ -1046,7 +1047,7 @@ impl BaseFlow {
} }
// New flows start out as fully damaged. // New flows start out as fully damaged.
let mut damage = incremental::rebuild_and_reflow(); let mut damage = RestyleDamage::rebuild_and_reflow();
damage.remove(RECONSTRUCT_FLOW); damage.remove(RECONSTRUCT_FLOW);
BaseFlow { BaseFlow {

View file

@ -18,7 +18,7 @@ use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode};
use gfx::text::glyph::CharIndex; use gfx::text::glyph::CharIndex;
use gfx::text::text_run::{TextRun, TextRunSlice}; use gfx::text::text_run::{TextRun, TextRunSlice};
use gfx_traits::LayerId; use gfx_traits::LayerId;
use incremental::{self, RECONSTRUCT_FLOW, RestyleDamage}; use incremental::{RECONSTRUCT_FLOW, RestyleDamage};
use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFragmentContext, InlineFragmentNodeInfo}; use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFragmentContext, InlineFragmentNodeInfo};
use inline::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT}; use inline::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT};
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
@ -40,6 +40,7 @@ use style::computed_values::content::ContentItem;
use style::computed_values::{border_collapse, clear, display, mix_blend_mode, overflow_wrap}; use style::computed_values::{border_collapse, clear, display, mix_blend_mode, overflow_wrap};
use style::computed_values::{overflow_x, position, text_decoration, transform_style}; use style::computed_values::{overflow_x, position, text_decoration, transform_style};
use style::computed_values::{white_space, word_break, z_index}; use style::computed_values::{white_space, word_break, z_index};
use style::dom::TRestyleDamage;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
use style::values::computed::{LengthOrPercentageOrNone}; use style::values::computed::{LengthOrPercentageOrNone};
@ -833,7 +834,7 @@ impl Fragment {
self.border_box.start, self.border_box.start,
size); size);
let mut restyle_damage = incremental::rebuild_and_reflow(); let mut restyle_damage = RestyleDamage::rebuild_and_reflow();
restyle_damage.remove(RECONSTRUCT_FLOW); restyle_damage.remove(RECONSTRUCT_FLOW);
Fragment { Fragment {

View file

@ -13,12 +13,13 @@ use flow::{InorderFlowTraversal};
use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, ImmutableFlowUtils}; use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, ImmutableFlowUtils};
use fragment::{Fragment, GeneratedContentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo}; use fragment::{Fragment, GeneratedContentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
use gfx::display_list::OpaqueNode; use gfx::display_list::OpaqueNode;
use incremental::{self, RESOLVE_GENERATED_CONTENT}; use incremental::{RESOLVE_GENERATED_CONTENT, RestyleDamage};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::collections::{HashMap, LinkedList}; use std::collections::{HashMap, LinkedList};
use std::sync::Arc; use std::sync::Arc;
use style::computed_values::content::ContentItem; use style::computed_values::content::ContentItem;
use style::computed_values::{display, list_style_type}; use style::computed_values::{display, list_style_type};
use style::dom::TRestyleDamage;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use text::TextRunScanner; use text::TextRunScanner;
use wrapper::PseudoElementType; use wrapper::PseudoElementType;
@ -431,7 +432,7 @@ fn render_text(layout_context: &LayoutContext,
fragments.push_back(Fragment::from_opaque_node_and_style(node, fragments.push_back(Fragment::from_opaque_node_and_style(node,
pseudo, pseudo,
style, style,
incremental::rebuild_and_reflow(), RestyleDamage::rebuild_and_reflow(),
info)); info));
// FIXME(pcwalton): This should properly handle multiple marker fragments. This could happen // FIXME(pcwalton): This should properly handle multiple marker fragments. This could happen
// due to text run splitting. // due to text run splitting.

View file

@ -6,6 +6,7 @@ use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, IS_ABSO
use std::fmt; use std::fmt;
use std::sync::Arc; use std::sync::Arc;
use style::computed_values::float; use style::computed_values::float;
use style::dom::TRestyleDamage;
use style::properties::ComputedValues; use style::properties::ComputedValues;
bitflags! { bitflags! {
@ -47,6 +48,18 @@ bitflags! {
} }
} }
impl TRestyleDamage for RestyleDamage {
fn compute(old: &Option<Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage { compute_damage(old, new) }
/// Returns a bitmask that represents a flow that needs to be rebuilt and reflowed.
///
/// Use this instead of `RestyleDamage::all()` because `RestyleDamage::all()` will result in
/// unnecessary sequential resolution of generated content.
fn rebuild_and_reflow() -> RestyleDamage {
REPAINT | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW
}
}
impl RestyleDamage { impl RestyleDamage {
/// Supposing a flow has the given `position` property and this damage, returns the damage that /// Supposing a flow has the given `position` property and this damage, returns the damage that
@ -130,18 +143,10 @@ macro_rules! add_if_not_equal(
}) })
); );
/// Returns a bitmask that represents a flow that needs to be rebuilt and reflowed.
///
/// Use this instead of `RestyleDamage::all()` because `RestyleDamage::all()` will result in
/// unnecessary sequential resolution of generated content.
pub fn rebuild_and_reflow() -> RestyleDamage {
REPAINT | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW
}
pub fn compute_damage(old: &Option<Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage { pub fn compute_damage(old: &Option<Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage {
let old: &ComputedValues = let old: &ComputedValues =
match old.as_ref() { match old.as_ref() {
None => return rebuild_and_reflow(), None => return RestyleDamage::rebuild_and_reflow(),
Some(cv) => &**cv, Some(cv) => &**cv,
}; };
@ -195,7 +200,7 @@ pub fn compute_damage(old: &Option<Arc<ComputedValues>>, new: &ComputedValues) -
// If the layer requirements of this flow have changed due to the value // If the layer requirements of this flow have changed due to the value
// of the transform, then reflow is required to rebuild the layers. // of the transform, then reflow is required to rebuild the layers.
if old.transform_requires_layer() != new.transform_requires_layer() { if old.transform_requires_layer() != new.transform_requires_layer() {
damage.insert(rebuild_and_reflow()); damage.insert(RestyleDamage::rebuild_and_reflow());
} }
// FIXME: test somehow that we checked every CSS property // FIXME: test somehow that we checked every CSS property
@ -256,7 +261,7 @@ impl<'a> LayoutDamageComputation for &'a mut Flow {
fn reflow_entire_document(self) { fn reflow_entire_document(self) {
let self_base = flow::mut_base(self); let self_base = flow::mut_base(self);
self_base.restyle_damage.insert(rebuild_and_reflow()); self_base.restyle_damage.insert(RestyleDamage::rebuild_and_reflow());
self_base.restyle_damage.remove(RECONSTRUCT_FLOW); self_base.restyle_damage.remove(RECONSTRUCT_FLOW);
for kid in self_base.children.iter_mut() { for kid in self_base.children.iter_mut() {
kid.reflow_entire_document(); kid.reflow_entire_document();

View file

@ -69,6 +69,7 @@ use style::dom::{TDocument, TElement, TNode};
use style::media_queries::{Device, MediaType}; use style::media_queries::{Device, MediaType};
use style::selector_matching::{Stylist, USER_OR_USER_AGENT_STYLESHEETS}; use style::selector_matching::{Stylist, USER_OR_USER_AGENT_STYLESHEETS};
use style::stylesheets::{CSSRuleIteratorExt, Stylesheet}; use style::stylesheets::{CSSRuleIteratorExt, Stylesheet};
use traversal::RecalcStyleAndConstructFlows;
use url::Url; use url::Url;
use util::geometry::MAX_RECT; use util::geometry::MAX_RECT;
use util::ipc::OptionalIpcSender; use util::ipc::OptionalIpcSender;
@ -1024,10 +1025,12 @@ impl LayoutTask {
// Perform CSS selector matching and flow construction. // Perform CSS selector matching and flow construction.
match self.parallel_traversal { match self.parallel_traversal {
None => { None => {
sequential::traverse_dom_preorder(node, &shared_layout_context); sequential::traverse_dom_preorder::<ServoLayoutNode, RecalcStyleAndConstructFlows>(
node, &shared_layout_context);
} }
Some(ref mut traversal) => { Some(ref mut traversal) => {
parallel::traverse_dom_preorder(node, &shared_layout_context, traversal); parallel::traverse_dom_preorder::<ServoLayoutNode, RecalcStyleAndConstructFlows>(
node, &shared_layout_context, traversal);
} }
} }
}); });

View file

@ -96,7 +96,3 @@ mod table_wrapper;
mod text; mod text;
mod traversal; mod traversal;
mod wrapper; mod wrapper;
mod css {
pub mod matching;
}

View file

@ -19,8 +19,7 @@ use style::dom::UnsafeNode;
use traversal::PostorderNodeMutTraversal; use traversal::PostorderNodeMutTraversal;
use traversal::{AssignBSizesAndStoreOverflow, AssignISizes, BubbleISizes}; use traversal::{AssignBSizesAndStoreOverflow, AssignISizes, BubbleISizes};
use traversal::{BuildDisplayList, ComputeAbsolutePositions}; use traversal::{BuildDisplayList, ComputeAbsolutePositions};
use traversal::{ConstructFlows, RecalcStyleForNode}; use traversal::{DomTraversal, DomTraversalContext};
use traversal::{PostorderDomTraversal, PreorderDomTraversal};
use util::opts; use util::opts;
use util::workqueue::{WorkQueue, WorkUnit, WorkerProxy}; use util::workqueue::{WorkQueue, WorkUnit, WorkerProxy};
use wrapper::LayoutNode; use wrapper::LayoutNode;
@ -73,105 +72,6 @@ pub type ChunkedFlowTraversalFunction =
pub type FlowTraversalFunction = extern "Rust" fn(UnsafeFlow, &SharedLayoutContext); pub type FlowTraversalFunction = extern "Rust" fn(UnsafeFlow, &SharedLayoutContext);
/// A parallel top-down DOM traversal.
pub trait ParallelPreorderDomTraversal<'ln, ConcreteLayoutNode>
: PreorderDomTraversal<'ln, ConcreteLayoutNode>
where ConcreteLayoutNode: LayoutNode<'ln> {
fn run_parallel(&self,
nodes: UnsafeNodeList,
proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>);
#[inline(always)]
fn run_parallel_helper(
&self,
unsafe_nodes: UnsafeNodeList,
proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>,
top_down_func: ChunkedDomTraversalFunction,
bottom_up_func: DomTraversalFunction) {
let mut discovered_child_nodes = Vec::new();
for unsafe_node in *unsafe_nodes.0 {
// Get a real layout node.
let node = unsafe { ConcreteLayoutNode::from_unsafe(&unsafe_node) };
// Perform the appropriate traversal.
self.process(node);
let child_count = node.children_count();
// Reset the count of children.
{
let data = node.mutate_data().unwrap();
data.parallel.children_count.store(child_count as isize,
Ordering::Relaxed);
}
// Possibly enqueue the children.
if child_count != 0 {
for kid in node.children() {
discovered_child_nodes.push(kid.to_unsafe())
}
} else {
// If there were no more children, start walking back up.
bottom_up_func(unsafe_nodes.1, unsafe_node, proxy)
}
}
for chunk in discovered_child_nodes.chunks(CHUNK_SIZE) {
proxy.push(WorkUnit {
fun: top_down_func,
data: (box chunk.iter().cloned().collect(), unsafe_nodes.1),
});
}
}
}
/// A parallel bottom-up DOM traversal.
trait ParallelPostorderDomTraversal<'ln, ConcreteLayoutNode>
: PostorderDomTraversal<'ln, ConcreteLayoutNode>
where ConcreteLayoutNode: LayoutNode<'ln> {
fn root(&self) -> OpaqueNode;
/// Process current node and potentially traverse its ancestors.
///
/// If we are the last child that finished processing, recursively process
/// our parent. Else, stop. Also, stop at the root.
///
/// Thus, if we start with all the leaves of a tree, we end up traversing
/// the whole tree bottom-up because each parent will be processed exactly
/// once (by the last child that finishes processing).
///
/// The only communication between siblings is that they both
/// fetch-and-subtract the parent's children count.
fn run_parallel(&self, unsafe_node: UnsafeNode) {
// Get a real layout node.
let mut node = unsafe { ConcreteLayoutNode::from_unsafe(&unsafe_node) };
loop {
// Perform the appropriate operation.
self.process(node);
let parent = match node.layout_parent_node(self.root()) {
None => break,
Some(parent) => parent,
};
let parent_data = unsafe {
&*parent.borrow_data_unchecked().unwrap()
};
if parent_data
.parallel
.children_count
.fetch_sub(1, Ordering::Relaxed) != 1 {
// Get out of here and find another node to work on.
break
}
// We were the last child of our parent. Construct flows for our parent.
node = parent;
}
}
}
/// Information that we need stored in each flow. /// Information that we need stored in each flow.
pub struct FlowParallelInfo { pub struct FlowParallelInfo {
/// The number of children that still need work done. /// The number of children that still need work done.
@ -332,59 +232,102 @@ impl<'a> ParallelPreorderFlowTraversal for ComputeAbsolutePositions<'a> {
impl<'a> ParallelPostorderFlowTraversal for BuildDisplayList<'a> {} impl<'a> ParallelPostorderFlowTraversal for BuildDisplayList<'a> {}
impl<'a, 'ln, ConcreteLayoutNode> ParallelPostorderDomTraversal<'ln, ConcreteLayoutNode> /// A parallel top-down DOM traversal.
for ConstructFlows<'a> #[inline(always)]
where ConcreteLayoutNode: LayoutNode<'ln> { fn top_down_dom<'ln, N, T>(unsafe_nodes: UnsafeNodeList,
fn root(&self) -> OpaqueNode {
self.root
}
}
impl<'a, 'ln, ConcreteLayoutNode> ParallelPreorderDomTraversal<'ln, ConcreteLayoutNode>
for RecalcStyleForNode<'a>
where ConcreteLayoutNode: LayoutNode<'ln> {
fn run_parallel(&self,
unsafe_nodes: UnsafeNodeList,
proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>) {
// Not exactly sure why we need UFCS here, but we seem to.
<RecalcStyleForNode<'a> as ParallelPreorderDomTraversal<'ln, ConcreteLayoutNode>>
::run_parallel_helper(self, unsafe_nodes, proxy,
recalc_style::<'ln, ConcreteLayoutNode>,
construct_flows::<'ln, ConcreteLayoutNode>)
}
}
fn recalc_style<'ln, ConcreteLayoutNode>(unsafe_nodes: UnsafeNodeList,
proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>) proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>)
where ConcreteLayoutNode: LayoutNode<'ln> { where N: LayoutNode<'ln>, T: DomTraversal<'ln, N> {
let shared_layout_context = proxy.user_data(); let shared_layout_context = proxy.user_data();
let layout_context = LayoutContext::new(shared_layout_context); let layout_context = LayoutContext::new(shared_layout_context);
let recalc_style_for_node_traversal = RecalcStyleForNode { let traversal_context = DomTraversalContext {
layout_context: &layout_context, layout_context: &layout_context,
root: unsafe_nodes.1, root: unsafe_nodes.1,
}; };
// The UFCS is necessary here to select the proper set of generic routines which let mut discovered_child_nodes = Vec::new();
// will eventually cast the UnsafeNode to a concrete LayoutNode implementation. for unsafe_node in *unsafe_nodes.0 {
<RecalcStyleForNode as ParallelPreorderDomTraversal<'ln, ConcreteLayoutNode>> // Get a real layout node.
::run_parallel(&recalc_style_for_node_traversal, unsafe_nodes, proxy) let node = unsafe { N::from_unsafe(&unsafe_node) };
// Perform the appropriate traversal.
T::process_preorder(&traversal_context, node);
let child_count = node.children_count();
// Reset the count of children.
{
let data = node.mutate_data().unwrap();
data.parallel.children_count.store(child_count as isize,
Ordering::Relaxed);
} }
fn construct_flows<'ln, ConcreteLayoutNode: LayoutNode<'ln>>( // Possibly enqueue the children.
root: OpaqueNode, if child_count != 0 {
for kid in node.children() {
discovered_child_nodes.push(kid.to_unsafe())
}
} else {
// If there were no more children, start walking back up.
bottom_up_dom::<N, T>(unsafe_nodes.1, unsafe_node, proxy)
}
}
for chunk in discovered_child_nodes.chunks(CHUNK_SIZE) {
proxy.push(WorkUnit {
fun: top_down_dom::<N, T>,
data: (box chunk.iter().cloned().collect(), unsafe_nodes.1),
});
}
}
/// Process current node and potentially traverse its ancestors.
///
/// If we are the last child that finished processing, recursively process
/// our parent. Else, stop. Also, stop at the root.
///
/// Thus, if we start with all the leaves of a tree, we end up traversing
/// the whole tree bottom-up because each parent will be processed exactly
/// once (by the last child that finishes processing).
///
/// The only communication between siblings is that they both
/// fetch-and-subtract the parent's children count.
fn bottom_up_dom<'ln, N, T>(root: OpaqueNode,
unsafe_node: UnsafeNode, unsafe_node: UnsafeNode,
proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>) { proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>)
where N: LayoutNode<'ln>, T: DomTraversal<'ln, N> {
let shared_layout_context = proxy.user_data(); let shared_layout_context = proxy.user_data();
let layout_context = LayoutContext::new(shared_layout_context); let layout_context = LayoutContext::new(shared_layout_context);
let construct_flows_traversal = ConstructFlows { let traversal_context = DomTraversalContext {
layout_context: &layout_context, layout_context: &layout_context,
root: root, root: root,
}; };
// The UFCS is necessary here to select the proper set of generic routines which // Get a real layout node.
// will eventually cast the UnsafeNode to a concrete LayoutNode implementation. let mut node = unsafe { N::from_unsafe(&unsafe_node) };
<ConstructFlows as ParallelPostorderDomTraversal<'ln, ConcreteLayoutNode>> loop {
::run_parallel(&construct_flows_traversal, unsafe_node) // Perform the appropriate operation.
T::process_postorder(&traversal_context, node);
let parent = match node.layout_parent_node(traversal_context.root) {
None => break,
Some(parent) => parent,
};
let parent_data = unsafe {
&*parent.borrow_data_unchecked().unwrap()
};
if parent_data
.parallel
.children_count
.fetch_sub(1, Ordering::Relaxed) != 1 {
// Get out of here and find another node to work on.
break
}
// We were the last child of our parent. Construct flows for our parent.
node = parent;
}
} }
fn assign_inline_sizes(unsafe_flows: UnsafeFlowList, fn assign_inline_sizes(unsafe_flows: UnsafeFlowList,
@ -441,12 +384,14 @@ fn run_queue_with_custom_work_data_type<To, F>(
queue.run(shared_layout_context); queue.run(shared_layout_context);
} }
pub fn traverse_dom_preorder<'ln, ConcreteLayoutNode: LayoutNode<'ln>>(root: ConcreteLayoutNode, pub fn traverse_dom_preorder<'ln, N, T>(
root: N,
shared_layout_context: &SharedLayoutContext, shared_layout_context: &SharedLayoutContext,
queue: &mut WorkQueue<SharedLayoutContext, WorkQueueData>) { queue: &mut WorkQueue<SharedLayoutContext, WorkQueueData>)
where N: LayoutNode<'ln>, T: DomTraversal<'ln, N> {
run_queue_with_custom_work_data_type(queue, |queue| { run_queue_with_custom_work_data_type(queue, |queue| {
queue.push(WorkUnit { queue.push(WorkUnit {
fun: recalc_style::<'ln, ConcreteLayoutNode>, fun: top_down_dom::<N, T>,
data: (box vec![root.to_unsafe()], root.opaque()), data: (box vec![root.to_unsafe()], root.opaque()),
}); });
}, shared_layout_context); }, shared_layout_context);

View file

@ -14,39 +14,33 @@ use fragment::FragmentBorderBoxIterator;
use generated_content::ResolveGeneratedContent; use generated_content::ResolveGeneratedContent;
use traversal::PostorderNodeMutTraversal; use traversal::PostorderNodeMutTraversal;
use traversal::{AssignBSizesAndStoreOverflow, AssignISizes}; use traversal::{AssignBSizesAndStoreOverflow, AssignISizes};
use traversal::{BubbleISizes, ConstructFlows, RecalcStyleForNode}; use traversal::{BubbleISizes, BuildDisplayList, ComputeAbsolutePositions};
use traversal::{BuildDisplayList, ComputeAbsolutePositions}; use traversal::{DomTraversal, DomTraversalContext};
use traversal::{PostorderDomTraversal, PreorderDomTraversal};
use util::opts; use util::opts;
use wrapper::LayoutNode; use wrapper::LayoutNode;
pub fn traverse_dom_preorder<'le, N>(root: N, pub fn traverse_dom_preorder<'ln, N, T>(root: N,
shared_layout_context: &SharedLayoutContext) shared_layout_context: &SharedLayoutContext)
where N: LayoutNode<'le> { where N: LayoutNode<'ln>,
fn doit<'le, N>(node: N, T: DomTraversal<'ln, N> {
recalc_style: RecalcStyleForNode, fn doit<'a, 'ln, N, T>(context: &'a DomTraversalContext<'a>, node: N)
construct_flows: ConstructFlows) where N: LayoutNode<'ln>, T: DomTraversal<'ln, N> {
where N: LayoutNode<'le> { T::process_preorder(context, node);
recalc_style.process(node);
for kid in node.children() { for kid in node.children() {
doit(kid, recalc_style, construct_flows); doit::<N, T>(context, kid);
} }
construct_flows.process(node); T::process_postorder(context, node);
} }
let layout_context = LayoutContext::new(shared_layout_context); let layout_context = LayoutContext::new(shared_layout_context);
let recalc_style = RecalcStyleForNode { let traversal_context = DomTraversalContext {
layout_context: &layout_context,
root: root.opaque(),
};
let construct_flows = ConstructFlows {
layout_context: &layout_context, layout_context: &layout_context,
root: root.opaque(), root: root.opaque(),
}; };
doit::<'le, N>(root, recalc_style, construct_flows); doit::<N, T>(&traversal_context, root);
} }
pub fn resolve_generated_content(root: &mut FlowRef, shared_layout_context: &SharedLayoutContext) { pub fn resolve_generated_content(root: &mut FlowRef, shared_layout_context: &SharedLayoutContext) {

View file

@ -6,18 +6,17 @@
use construct::FlowConstructor; use construct::FlowConstructor;
use context::LayoutContext; use context::LayoutContext;
use css::matching::{ElementMatchMethods, MatchMethods, StyleSharingResult};
use flow::{PostorderFlowTraversal, PreorderFlowTraversal}; use flow::{PostorderFlowTraversal, PreorderFlowTraversal};
use flow::{self, Flow}; use flow::{self, Flow};
use gfx::display_list::OpaqueNode; use gfx::display_list::OpaqueNode;
use incremental::{self, BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage}; use incremental::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage};
use script::layout_interface::ReflowGoal; use script::layout_interface::ReflowGoal;
use selectors::bloom::BloomFilter; use selectors::bloom::BloomFilter;
use std::cell::RefCell; use std::cell::RefCell;
use std::mem; use std::mem;
use style::context::StyleContext; use style::context::StyleContext;
use style::dom::UnsafeNode; use style::dom::{TRestyleDamage, UnsafeNode};
use style::matching::ApplicableDeclarations; use style::matching::{ApplicableDeclarations, ElementMatchMethods, MatchMethods, StyleSharingResult};
use util::opts; use util::opts;
use util::tid::tid; use util::tid::tid;
use wrapper::{LayoutNode, ThreadSafeLayoutNode}; use wrapper::{LayoutNode, ThreadSafeLayoutNode};
@ -118,17 +117,30 @@ fn insert_ancestors_into_bloom_filter<'ln, N>(bf: &mut Box<BloomFilter>,
debug!("[{}] Inserted {} ancestors.", tid(), ancestors); debug!("[{}] Inserted {} ancestors.", tid(), ancestors);
} }
#[derive(Copy, Clone)]
/// A top-down traversal. pub struct DomTraversalContext<'a> {
pub trait PreorderDomTraversal<'ln, ConcreteLayoutNode: LayoutNode<'ln>> { pub layout_context: &'a LayoutContext<'a>,
/// The operation to perform. Return true to continue or false to stop. pub root: OpaqueNode,
fn process(&self, node: ConcreteLayoutNode);
} }
/// A bottom-up traversal, with a optional in-order pass. pub trait DomTraversal<'ln, N: LayoutNode<'ln>> {
pub trait PostorderDomTraversal<'ln, ConcreteLayoutNode: LayoutNode<'ln>> { fn process_preorder<'a>(context: &'a DomTraversalContext<'a>, node: N);
/// The operation to perform. Return true to continue or false to stop. fn process_postorder<'a>(context: &'a DomTraversalContext<'a>, node: N);
fn process(&self, node: ConcreteLayoutNode); }
/// FIXME(bholley): I added this now to demonstrate the usefulness of the new design.
/// This is currently unused, but will be used shortly.
#[allow(dead_code)]
pub struct RecalcStyleOnly;
impl<'ln, N: LayoutNode<'ln>> DomTraversal<'ln, N> for RecalcStyleOnly {
fn process_preorder<'a>(context: &'a DomTraversalContext<'a>, node: N) { recalc_style_at(context, node); }
fn process_postorder<'a>(_: &'a DomTraversalContext<'a>, _: N) {}
}
pub struct RecalcStyleAndConstructFlows;
impl<'ln, N: LayoutNode<'ln>> DomTraversal<'ln, N> for RecalcStyleAndConstructFlows {
fn process_preorder<'a>(context: &'a DomTraversalContext<'a>, node: N) { recalc_style_at(context, node); }
fn process_postorder<'a>(context: &'a DomTraversalContext<'a>, node: N) { construct_flows_at(context, node); }
} }
/// A bottom-up, parallelizable traversal. /// A bottom-up, parallelizable traversal.
@ -139,18 +151,9 @@ pub trait PostorderNodeMutTraversal<'ln, ConcreteThreadSafeLayoutNode: ThreadSaf
/// The recalc-style-for-node traversal, which styles each node and must run before /// The recalc-style-for-node traversal, which styles each node and must run before
/// layout computation. This computes the styles applied to each node. /// layout computation. This computes the styles applied to each node.
#[derive(Copy, Clone)]
pub struct RecalcStyleForNode<'a> {
pub layout_context: &'a LayoutContext<'a>,
pub root: OpaqueNode,
}
impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode>
for RecalcStyleForNode<'a>
where ConcreteLayoutNode: LayoutNode<'ln> {
#[inline] #[inline]
#[allow(unsafe_code)] #[allow(unsafe_code)]
fn process(&self, node: ConcreteLayoutNode) { fn recalc_style_at<'a, 'ln, N: LayoutNode<'ln>> (context: &'a DomTraversalContext<'a>, node: N) {
// Initialize layout data. // Initialize layout data.
// //
// FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML // FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML
@ -158,10 +161,10 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode>
node.initialize_data(); node.initialize_data();
// Get the parent node. // Get the parent node.
let parent_opt = node.layout_parent_node(self.root); let parent_opt = node.layout_parent_node(context.root);
// Get the style bloom filter. // Get the style bloom filter.
let mut bf = take_task_local_bloom_filter(parent_opt, self.root, self.layout_context); let mut bf = take_task_local_bloom_filter(parent_opt, context.root, context.layout_context);
let nonincremental_layout = opts::get().nonincremental_layout; let nonincremental_layout = opts::get().nonincremental_layout;
if nonincremental_layout || node.is_dirty() { if nonincremental_layout || node.is_dirty() {
@ -174,7 +177,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode>
// Check to see whether we can share a style with someone. // Check to see whether we can share a style with someone.
let style_sharing_candidate_cache = let style_sharing_candidate_cache =
&mut self.layout_context.style_sharing_candidate_cache(); &mut context.layout_context.style_sharing_candidate_cache();
let sharing_result = match node.as_element() { let sharing_result = match node.as_element() {
Some(element) => { Some(element) => {
@ -194,7 +197,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode>
let shareable_element = match node.as_element() { let shareable_element = match node.as_element() {
Some(element) => { Some(element) => {
// Perform the CSS selector matching. // Perform the CSS selector matching.
let stylist = unsafe { &*self.layout_context.shared_context().stylist.0 }; let stylist = unsafe { &*context.layout_context.shared_context().stylist.0 };
if element.match_element(stylist, if element.match_element(stylist,
Some(&*bf), Some(&*bf),
&mut applicable_declarations) { &mut applicable_declarations) {
@ -205,8 +208,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode>
}, },
None => { None => {
if node.has_changed() { if node.has_changed() {
node.to_threadsafe().set_restyle_damage( node.set_restyle_damage(N::ConcreteRestyleDamage::rebuild_and_reflow())
incremental::rebuild_and_reflow())
} }
None None
}, },
@ -214,11 +216,11 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode>
// Perform the CSS cascade. // Perform the CSS cascade.
unsafe { unsafe {
node.cascade_node(self.layout_context.shared, node.cascade_node(&context.layout_context.shared.style_context,
parent_opt, parent_opt,
&applicable_declarations, &applicable_declarations,
&mut self.layout_context.applicable_declarations_cache(), &mut context.layout_context.applicable_declarations_cache(),
&self.layout_context.shared_context().new_animations_sender); &context.layout_context.shared_context().new_animations_sender);
} }
// Add ourselves to the LRU cache. // Add ourselves to the LRU cache.
@ -228,7 +230,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode>
} }
StyleSharingResult::StyleWasShared(index, damage) => { StyleSharingResult::StyleWasShared(index, damage) => {
style_sharing_candidate_cache.touch(index); style_sharing_candidate_cache.touch(index);
node.to_threadsafe().set_restyle_damage(damage); node.set_restyle_damage(damage);
} }
} }
} }
@ -241,23 +243,13 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode>
node.insert_into_bloom_filter(&mut *bf); node.insert_into_bloom_filter(&mut *bf);
// NB: flow construction updates the bloom filter on the way up. // NB: flow construction updates the bloom filter on the way up.
put_task_local_bloom_filter(bf, &unsafe_layout_node, self.layout_context); put_task_local_bloom_filter(bf, &unsafe_layout_node, context.layout_context);
}
} }
/// The flow construction traversal, which builds flows for styled nodes. /// The flow construction traversal, which builds flows for styled nodes.
#[derive(Copy, Clone)]
pub struct ConstructFlows<'a> {
pub layout_context: &'a LayoutContext<'a>,
pub root: OpaqueNode,
}
impl<'a, 'ln, ConcreteLayoutNode> PostorderDomTraversal<'ln, ConcreteLayoutNode>
for ConstructFlows<'a>
where ConcreteLayoutNode: LayoutNode<'ln> {
#[inline] #[inline]
#[allow(unsafe_code)] #[allow(unsafe_code)]
fn process(&self, node: ConcreteLayoutNode) { fn construct_flows_at<'a, 'ln, N: LayoutNode<'ln>>(context: &'a DomTraversalContext<'a>, node: N) {
// Construct flows for this node. // Construct flows for this node.
{ {
let tnode = node.to_threadsafe(); let tnode = node.to_threadsafe();
@ -265,7 +257,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PostorderDomTraversal<'ln, ConcreteLayoutNode>
// Always reconstruct if incremental layout is turned off. // Always reconstruct if incremental layout is turned off.
let nonincremental_layout = opts::get().nonincremental_layout; let nonincremental_layout = opts::get().nonincremental_layout;
if nonincremental_layout || node.has_dirty_descendants() { if nonincremental_layout || node.has_dirty_descendants() {
let mut flow_constructor = FlowConstructor::new(self.layout_context); let mut flow_constructor = FlowConstructor::new(context.layout_context);
if nonincremental_layout || !flow_constructor.repair_if_possible(&tnode) { if nonincremental_layout || !flow_constructor.repair_if_possible(&tnode) {
flow_constructor.process(&tnode); flow_constructor.process(&tnode);
debug!("Constructed flow for {:x}: {:x}", debug!("Constructed flow for {:x}: {:x}",
@ -294,9 +286,9 @@ impl<'a, 'ln, ConcreteLayoutNode> PostorderDomTraversal<'ln, ConcreteLayoutNode>
}); });
assert_eq!(old_node, unsafe_layout_node); assert_eq!(old_node, unsafe_layout_node);
assert_eq!(old_generation, self.layout_context.shared_context().generation); assert_eq!(old_generation, context.layout_context.shared_context().generation);
match node.layout_parent_node(self.root) { match node.layout_parent_node(context.root) {
None => { None => {
debug!("[{}] - {:X}, and deleting BF.", tid(), unsafe_layout_node.0); debug!("[{}] - {:X}, and deleting BF.", tid(), unsafe_layout_node.0);
// If this is the reflow root, eat the task-local bloom filter. // If this is the reflow root, eat the task-local bloom filter.
@ -305,11 +297,10 @@ impl<'a, 'ln, ConcreteLayoutNode> PostorderDomTraversal<'ln, ConcreteLayoutNode>
// Otherwise, put it back, but remove this node. // Otherwise, put it back, but remove this node.
node.remove_from_bloom_filter(&mut *bf); node.remove_from_bloom_filter(&mut *bf);
let unsafe_parent = parent.to_unsafe(); let unsafe_parent = parent.to_unsafe();
put_task_local_bloom_filter(bf, &unsafe_parent, self.layout_context); put_task_local_bloom_filter(bf, &unsafe_parent, context.layout_context);
}, },
}; };
} }
}
/// The bubble-inline-sizes traversal, the first part of layout computation. This computes /// The bubble-inline-sizes traversal, the first part of layout computation. This computes
/// preferred and intrinsic inline-sizes and bubbles them up the tree. /// preferred and intrinsic inline-sizes and bubbles them up the tree.

View file

@ -132,6 +132,7 @@ impl<'ln> ServoLayoutNode<'ln> {
impl<'ln> TNode<'ln> for ServoLayoutNode<'ln> { impl<'ln> TNode<'ln> for ServoLayoutNode<'ln> {
type ConcreteElement = ServoLayoutElement<'ln>; type ConcreteElement = ServoLayoutElement<'ln>;
type ConcreteDocument = ServoLayoutDocument<'ln>; type ConcreteDocument = ServoLayoutDocument<'ln>;
type ConcreteRestyleDamage = RestyleDamage;
fn to_unsafe(&self) -> UnsafeNode { fn to_unsafe(&self) -> UnsafeNode {
unsafe { unsafe {
@ -237,6 +238,14 @@ impl<'ln> TNode<'ln> for ServoLayoutNode<'ln> {
unsafe { self.mutate_layout_data().map(|d| transmute(d)) } unsafe { self.mutate_layout_data().map(|d| transmute(d)) }
} }
fn restyle_damage(self) -> RestyleDamage {
self.borrow_layout_data().unwrap().restyle_damage
}
fn set_restyle_damage(self, damage: RestyleDamage) {
self.mutate_layout_data().unwrap().restyle_damage = damage;
}
fn parent_node(&self) -> Option<ServoLayoutNode<'ln>> { fn parent_node(&self) -> Option<ServoLayoutNode<'ln>> {
unsafe { unsafe {
self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node)) self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node))
@ -714,16 +723,9 @@ pub trait ThreadSafeLayoutNode<'ln> : Clone + Copy + Sized {
fn is_ignorable_whitespace(&self) -> bool; fn is_ignorable_whitespace(&self) -> bool;
/// Get the description of how to account for recent style changes. fn restyle_damage(self) -> RestyleDamage;
/// This is a simple bitfield and fine to copy by value.
fn restyle_damage(self) -> RestyleDamage {
self.borrow_layout_data().unwrap().restyle_damage
}
/// Set the restyle damage field. fn set_restyle_damage(self, damage: RestyleDamage);
fn set_restyle_damage(self, damage: RestyleDamage) {
self.mutate_layout_data().unwrap().restyle_damage = damage;
}
/// Returns the layout data flags for this node. /// Returns the layout data flags for this node.
fn flags(self) -> LayoutDataFlags; fn flags(self) -> LayoutDataFlags;
@ -909,6 +911,14 @@ impl<'ln> ThreadSafeLayoutNode<'ln> for ServoThreadSafeLayoutNode<'ln> {
} }
} }
fn restyle_damage(self) -> RestyleDamage {
self.node.restyle_damage()
}
fn set_restyle_damage(self, damage: RestyleDamage) {
self.node.set_restyle_damage(damage)
}
fn flags(self) -> LayoutDataFlags { fn flags(self) -> LayoutDataFlags {
unsafe { unsafe {
(*self.node.borrow_layout_data_unchecked().unwrap()).flags (*self.node.borrow_layout_data_unchecked().unwrap()).flags

View file

@ -1790,6 +1790,7 @@ dependencies = [
"smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"string_cache 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"style_traits 0.0.1", "style_traits 0.0.1",
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"util 0.0.1", "util 0.0.1",
] ]

View file

@ -38,5 +38,6 @@ string_cache = "0.2"
euclid = {version = "0.4", features = ["plugins"]} euclid = {version = "0.4", features = ["plugins"]}
serde = "0.6" serde = "0.6"
serde_macros = "0.6" serde_macros = "0.6"
time = "0.1"
url = "0.5.2" url = "0.5.2"

View file

@ -4,7 +4,7 @@
use app_units::Au; use app_units::Au;
use cssparser::{Color, RGBA}; use cssparser::{Color, RGBA};
use dom::OpaqueNode; use dom::{OpaqueNode, TRestyleDamage};
use euclid::point::Point2D; use euclid::point::Point2D;
use properties::ComputedValues; use properties::ComputedValues;
use properties::longhands::background_position::computed_value::T as BackgroundPosition; use properties::longhands::background_position::computed_value::T as BackgroundPosition;
@ -26,6 +26,9 @@ use properties::longhands::visibility::computed_value::T as Visibility;
use properties::longhands::z_index::computed_value::T as ZIndex; use properties::longhands::z_index::computed_value::T as ZIndex;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::iter::repeat; use std::iter::repeat;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use time;
use util::bezier::Bezier; use util::bezier::Bezier;
use values::CSSFloat; use values::CSSFloat;
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
@ -922,3 +925,62 @@ impl<T> GetMod for Vec<T> {
&(*self)[i % self.len()] &(*self)[i % self.len()]
} }
} }
/// Inserts transitions into the queue of running animations as applicable for the given style
/// difference. This is called from the layout worker threads. Returns true if any animations were
/// kicked off and false otherwise.
pub fn start_transitions_if_applicable(new_animations_sender: &Mutex<Sender<Animation>>,
node: OpaqueNode,
old_style: &ComputedValues,
new_style: &mut ComputedValues)
-> bool {
let mut had_animations = false;
for i in 0..new_style.get_animation().transition_property.0.len() {
// Create any property animations, if applicable.
let property_animations = PropertyAnimation::from_transition(i, old_style, new_style);
for property_animation in property_animations {
// Set the property to the initial value.
property_animation.update(new_style, 0.0);
// Kick off the animation.
let now = time::precise_time_s();
let animation_style = new_style.get_animation();
let start_time =
now + (animation_style.transition_delay.0.get_mod(i).seconds() as f64);
new_animations_sender.lock().unwrap().send(Animation {
node: node,
property_animation: property_animation,
start_time: start_time,
end_time: start_time +
(animation_style.transition_duration.0.get_mod(i).seconds() as f64),
}).unwrap();
had_animations = true
}
}
had_animations
}
/// Updates a single animation and associated style based on the current time. If `damage` is
/// provided, inserts the appropriate restyle damage.
pub fn update_style_for_animation<ConcreteRestyleDamage: TRestyleDamage>(animation: &Animation,
style: &mut Arc<ComputedValues>,
damage: Option<&mut ConcreteRestyleDamage>) {
let now = time::precise_time_s();
let mut progress = (now - animation.start_time) / animation.duration();
if progress > 1.0 {
progress = 1.0
}
if progress <= 0.0 {
return
}
let mut new_style = (*style).clone();
animation.property_animation.update(&mut *Arc::make_mut(&mut new_style), progress);
if let Some(damage) = damage {
*damage = *damage | ConcreteRestyleDamage::compute(&Some((*style).clone()), &new_style);
}
*style = new_style
}

View file

@ -5,13 +5,15 @@
#![allow(unsafe_code)] #![allow(unsafe_code)]
use data::PrivateStyleData; use data::PrivateStyleData;
use properties::{PropertyDeclaration, PropertyDeclarationBlock}; use properties::{ComputedValues, PropertyDeclaration, PropertyDeclarationBlock};
use restyle_hints::{ElementSnapshot, RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint}; use restyle_hints::{ElementSnapshot, RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
use selectors::matching::DeclarationBlock; use selectors::matching::DeclarationBlock;
use selectors::states::ElementState; use selectors::states::ElementState;
use smallvec::VecLike; use smallvec::VecLike;
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefMut};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::BitOr;
use std::sync::Arc;
use string_cache::{Atom, Namespace}; use string_cache::{Atom, Namespace};
/// Opaque type stored in type-unsafe work queues for parallel layout. /// Opaque type stored in type-unsafe work queues for parallel layout.
@ -39,10 +41,15 @@ impl OpaqueNode {
} }
} }
pub trait TRestyleDamage : BitOr<Output=Self> + Copy {
fn compute(old: &Option<Arc<ComputedValues>>, new: &ComputedValues) -> Self;
fn rebuild_and_reflow() -> Self;
}
pub trait TNode<'ln> : Sized + Copy + Clone { pub trait TNode<'ln> : Sized + Copy + Clone {
type ConcreteElement: TElement<'ln, ConcreteNode = Self, ConcreteDocument = Self::ConcreteDocument>; type ConcreteElement: TElement<'ln, ConcreteNode = Self, ConcreteDocument = Self::ConcreteDocument>;
type ConcreteDocument: TDocument<'ln, ConcreteNode = Self, ConcreteElement = Self::ConcreteElement>; type ConcreteDocument: TDocument<'ln, ConcreteNode = Self, ConcreteElement = Self::ConcreteElement>;
type ConcreteRestyleDamage: TRestyleDamage;
fn to_unsafe(&self) -> UnsafeNode; fn to_unsafe(&self) -> UnsafeNode;
unsafe fn from_unsafe(n: &UnsafeNode) -> Self; unsafe fn from_unsafe(n: &UnsafeNode) -> Self;
@ -134,6 +141,12 @@ pub trait TNode<'ln> : Sized + Copy + Clone {
#[inline(always)] #[inline(always)]
fn mutate_data(&self) -> Option<RefMut<PrivateStyleData>>; fn mutate_data(&self) -> Option<RefMut<PrivateStyleData>>;
/// Get the description of how to account for recent style changes.
fn restyle_damage(self) -> Self::ConcreteRestyleDamage;
/// Set the restyle damage field.
fn set_restyle_damage(self, damage: Self::ConcreteRestyleDamage);
fn parent_node(&self) -> Option<Self>; fn parent_node(&self) -> Option<Self>;
fn first_child(&self) -> Option<Self>; fn first_child(&self) -> Option<Self>;

View file

@ -40,6 +40,7 @@ extern crate smallvec;
#[macro_use(atom, ns)] extern crate string_cache; #[macro_use(atom, ns)] extern crate string_cache;
#[macro_use] #[macro_use]
extern crate style_traits; extern crate style_traits;
extern crate time;
extern crate url; extern crate url;
extern crate util; extern crate util;

View file

@ -2,22 +2,31 @@
* 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 dom::{TElement, TNode}; #![allow(unsafe_code)]
use properties::{ComputedValues, PropertyDeclaration};
use selector_matching::DeclarationBlock; use animation::{self, Animation};
use context::SharedStyleContext;
use data::PrivateStyleData;
use dom::{TElement, TNode, TRestyleDamage};
use properties::{ComputedValues, PropertyDeclaration, cascade};
use selector_matching::{DeclarationBlock, Stylist};
use selectors::Element;
use selectors::bloom::BloomFilter;
use selectors::matching::{CommonStyleAffectingAttributeMode, CommonStyleAffectingAttributes}; use selectors::matching::{CommonStyleAffectingAttributeMode, CommonStyleAffectingAttributes};
use selectors::matching::{common_style_affecting_attributes, rare_style_affecting_attributes}; use selectors::matching::{common_style_affecting_attributes, rare_style_affecting_attributes};
use selectors::parser::PseudoElement;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::slice::Iter; use std::slice::Iter;
use std::sync::Arc; use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use string_cache::{Atom, Namespace}; use string_cache::{Atom, Namespace};
use util::arc_ptr_eq; use util::arc_ptr_eq;
use util::cache::{LRUCache, SimpleHashCache}; use util::cache::{LRUCache, SimpleHashCache};
use util::opts;
use util::vec::ForgetfulSink; use util::vec::ForgetfulSink;
/// Pieces of layout/css/matching.rs, which will eventually be merged /// High-level interface to CSS selector matching.
/// into this file.
fn create_common_style_affecting_attributes_from_element<'le, E: TElement<'le>>(element: &E) fn create_common_style_affecting_attributes_from_element<'le, E: TElement<'le>>(element: &E)
-> CommonStyleAffectingAttributes { -> CommonStyleAffectingAttributes {
@ -341,4 +350,358 @@ impl StyleSharingCandidateCache {
} }
} }
/// The results of attempting to share a style.
pub enum StyleSharingResult<ConcreteRestyleDamage: TRestyleDamage> {
/// We didn't find anybody to share the style with.
CannotShare,
/// The node's style can be shared. The integer specifies the index in the LRU cache that was
/// hit and the damage that was done.
StyleWasShared(usize, ConcreteRestyleDamage),
}
trait PrivateMatchMethods<'ln>: TNode<'ln> {
fn cascade_node_pseudo_element(&self,
context: &SharedStyleContext,
parent_style: Option<&Arc<ComputedValues>>,
applicable_declarations: &[DeclarationBlock],
style: &mut Option<Arc<ComputedValues>>,
applicable_declarations_cache:
&mut ApplicableDeclarationsCache,
new_animations_sender: &Mutex<Sender<Animation>>,
shareable: bool,
animate_properties: bool)
-> Self::ConcreteRestyleDamage {
let mut cacheable = true;
if animate_properties {
cacheable = !self.update_animations_for_cascade(context, style) && cacheable;
}
let mut this_style;
match parent_style {
Some(ref parent_style) => {
let cache_entry = applicable_declarations_cache.find(applicable_declarations);
let cached_computed_values = match cache_entry {
None => None,
Some(ref style) => Some(&**style),
};
let (the_style, is_cacheable) = cascade(context.viewport_size,
applicable_declarations,
shareable,
Some(&***parent_style),
cached_computed_values,
context.error_reporter.clone());
cacheable = cacheable && is_cacheable;
this_style = the_style
}
None => {
let (the_style, is_cacheable) = cascade(context.viewport_size,
applicable_declarations,
shareable,
None,
None,
context.error_reporter.clone());
cacheable = cacheable && is_cacheable;
this_style = the_style
}
};
// Trigger transitions if necessary. This will reset `this_style` back to its old value if
// it did trigger a transition.
if animate_properties {
if let Some(ref style) = *style {
let animations_started =
animation::start_transitions_if_applicable(new_animations_sender,
self.opaque(),
&**style,
&mut this_style);
cacheable = cacheable && !animations_started
}
}
// Calculate style difference.
let this_style = Arc::new(this_style);
let damage = Self::ConcreteRestyleDamage::compute(style, &*this_style);
// Cache the resolved style if it was cacheable.
if cacheable {
applicable_declarations_cache.insert(applicable_declarations.to_vec(),
this_style.clone());
}
// Write in the final style and return the damage done to our caller.
*style = Some(this_style);
damage
}
fn update_animations_for_cascade(&self,
context: &SharedStyleContext,
style: &mut Option<Arc<ComputedValues>>)
-> bool {
let style = match *style {
None => return false,
Some(ref mut style) => style,
};
// Finish any expired transitions.
let this_opaque = self.opaque();
let had_animations_to_expire;
{
let all_expired_animations = context.expired_animations.read().unwrap();
let animations_to_expire = all_expired_animations.get(&this_opaque);
had_animations_to_expire = animations_to_expire.is_some();
if let Some(ref animations) = animations_to_expire {
for animation in *animations {
animation.property_animation.update(&mut *Arc::make_mut(style), 1.0);
}
}
}
if had_animations_to_expire {
context.expired_animations.write().unwrap().remove(&this_opaque);
}
// Merge any running transitions into the current style, and cancel them.
let had_running_animations = context.running_animations
.read()
.unwrap()
.get(&this_opaque)
.is_some();
if had_running_animations {
let mut all_running_animations = context.running_animations.write().unwrap();
for running_animation in all_running_animations.get(&this_opaque).unwrap() {
animation::update_style_for_animation::<Self::ConcreteRestyleDamage>(running_animation, style, None);
}
all_running_animations.remove(&this_opaque);
}
had_animations_to_expire || had_running_animations
}
}
impl<'ln, N: TNode<'ln>> PrivateMatchMethods<'ln> for N {}
trait PrivateElementMatchMethods<'le>: TElement<'le> {
fn share_style_with_candidate_if_possible(&self,
parent_node: Option<Self::ConcreteNode>,
candidate: &StyleSharingCandidate)
-> Option<Arc<ComputedValues>> {
let parent_node = match parent_node {
Some(ref parent_node) if parent_node.as_element().is_some() => parent_node,
Some(_) | None => return None,
};
let parent_data: Option<&PrivateStyleData> = unsafe {
parent_node.borrow_data_unchecked().map(|d| &*d)
};
if let Some(parent_data_ref) = parent_data {
// Check parent style.
let parent_style = (*parent_data_ref).style.as_ref().unwrap();
if !arc_ptr_eq(parent_style, &candidate.parent_style) {
return None
}
// Check tag names, classes, etc.
if !candidate.can_share_style_with(self) {
return None
}
return Some(candidate.style.clone())
}
None
}
}
impl<'le, E: TElement<'le>> PrivateElementMatchMethods<'le> for E {}
pub trait ElementMatchMethods<'le> : TElement<'le> {
fn match_element(&self,
stylist: &Stylist,
parent_bf: Option<&BloomFilter>,
applicable_declarations: &mut ApplicableDeclarations)
-> bool {
let style_attribute = self.style_attribute().as_ref();
applicable_declarations.normal_shareable =
stylist.push_applicable_declarations(self,
parent_bf,
style_attribute,
None,
&mut applicable_declarations.normal);
stylist.push_applicable_declarations(self,
parent_bf,
None,
Some(PseudoElement::Before),
&mut applicable_declarations.before);
stylist.push_applicable_declarations(self,
parent_bf,
None,
Some(PseudoElement::After),
&mut applicable_declarations.after);
applicable_declarations.normal_shareable &&
applicable_declarations.before.is_empty() &&
applicable_declarations.after.is_empty()
}
/// Attempts to share a style with another node. This method is unsafe because it depends on
/// the `style_sharing_candidate_cache` having only live nodes in it, and we have no way to
/// guarantee that at the type system level yet.
unsafe fn share_style_if_possible(&self,
style_sharing_candidate_cache:
&mut StyleSharingCandidateCache,
parent: Option<Self::ConcreteNode>)
-> StyleSharingResult<<Self::ConcreteNode as TNode<'le>>::ConcreteRestyleDamage> {
if opts::get().disable_share_style_cache {
return StyleSharingResult::CannotShare
}
if self.style_attribute().is_some() {
return StyleSharingResult::CannotShare
}
if self.get_attr(&ns!(), &atom!("id")).is_some() {
return StyleSharingResult::CannotShare
}
for (i, &(ref candidate, ())) in style_sharing_candidate_cache.iter().enumerate() {
match self.share_style_with_candidate_if_possible(parent.clone(), candidate) {
Some(shared_style) => {
// Yay, cache hit. Share the style.
let node = self.as_node();
let style = &mut node.mutate_data().unwrap().style;
let damage = <<Self as TElement<'le>>::ConcreteNode as TNode<'le>>
::ConcreteRestyleDamage::compute(style, &*shared_style);
*style = Some(shared_style);
return StyleSharingResult::StyleWasShared(i, damage)
}
None => {}
}
}
StyleSharingResult::CannotShare
}
}
impl<'le, E: TElement<'le>> ElementMatchMethods<'le> for E {}
pub trait MatchMethods<'ln> : TNode<'ln> {
// The below two functions are copy+paste because I can't figure out how to
// write a function which takes a generic function. I don't think it can
// be done.
//
// Ideally, I'd want something like:
//
// > fn with_really_simple_selectors(&self, f: <H: Hash>|&H|);
// In terms of `SimpleSelector`s, these two functions will insert and remove:
// - `SimpleSelector::LocalName`
// - `SimpleSelector::Namepace`
// - `SimpleSelector::ID`
// - `SimpleSelector::Class`
/// Inserts and removes the matching `Descendant` selectors from a bloom
/// filter. This is used to speed up CSS selector matching to remove
/// unnecessary tree climbs for `Descendant` queries.
///
/// A bloom filter of the local names, namespaces, IDs, and classes is kept.
/// Therefore, each node must have its matching selectors inserted _after_
/// its own selector matching and _before_ its children start.
fn insert_into_bloom_filter(&self, bf: &mut BloomFilter) {
// Only elements are interesting.
if let Some(element) = self.as_element() {
bf.insert(element.get_local_name());
bf.insert(element.get_namespace());
element.get_id().map(|id| bf.insert(&id));
// TODO: case-sensitivity depends on the document type and quirks mode
element.each_class(|class| bf.insert(class));
}
}
/// After all the children are done css selector matching, this must be
/// called to reset the bloom filter after an `insert`.
fn remove_from_bloom_filter(&self, bf: &mut BloomFilter) {
// Only elements are interesting.
if let Some(element) = self.as_element() {
bf.remove(element.get_local_name());
bf.remove(element.get_namespace());
element.get_id().map(|id| bf.remove(&id));
// TODO: case-sensitivity depends on the document type and quirks mode
element.each_class(|class| bf.remove(class));
}
}
unsafe fn cascade_node(&self,
context: &SharedStyleContext,
parent: Option<Self>,
applicable_declarations: &ApplicableDeclarations,
applicable_declarations_cache: &mut ApplicableDeclarationsCache,
new_animations_sender: &Mutex<Sender<Animation>>) {
// Get our parent's style. This must be unsafe so that we don't touch the parent's
// borrow flags.
//
// FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow
// enforced safe, race-free access to the parent style.
let parent_style = match parent {
None => None,
Some(parent_node) => {
let parent_style = (*parent_node.borrow_data_unchecked().unwrap()).style.as_ref().unwrap();
Some(parent_style)
}
};
if self.is_text_node() {
// Text nodes get a copy of the parent style. This ensures
// that during fragment construction any non-inherited
// CSS properties (such as vertical-align) are correctly
// set on the fragment(s).
let mut data_ref = self.mutate_data().unwrap();
let mut data = &mut *data_ref;
let cloned_parent_style = parent_style.unwrap().clone();
data.style = Some(cloned_parent_style);
} else {
let mut damage;
{
let mut data_ref = self.mutate_data().unwrap();
let mut data = &mut *data_ref;
damage = self.cascade_node_pseudo_element(
context,
parent_style,
&applicable_declarations.normal,
&mut data.style,
applicable_declarations_cache,
new_animations_sender,
applicable_declarations.normal_shareable,
true);
if !applicable_declarations.before.is_empty() {
damage = damage | self.cascade_node_pseudo_element(
context,
Some(data.style.as_ref().unwrap()),
&*applicable_declarations.before,
&mut data.before_style,
applicable_declarations_cache,
new_animations_sender,
false,
false);
}
if !applicable_declarations.after.is_empty() {
damage = damage | self.cascade_node_pseudo_element(
context,
Some(data.style.as_ref().unwrap()),
&*applicable_declarations.after,
&mut data.after_style,
applicable_declarations_cache,
new_animations_sender,
false,
false);
}
}
// This method needs to borrow the data as mutable, so make sure data_ref goes out of
// scope first.
self.set_restyle_damage(damage);
}
}
}
impl<'ln, N: TNode<'ln>> MatchMethods<'ln> for N {}

1
ports/cef/Cargo.lock generated
View file

@ -1742,6 +1742,7 @@ dependencies = [
"smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"string_cache 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"style_traits 0.0.1", "style_traits 0.0.1",
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"util 0.0.1", "util 0.0.1",
] ]

1
ports/gonk/Cargo.lock generated
View file

@ -1708,6 +1708,7 @@ dependencies = [
"smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"string_cache 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"style_traits 0.0.1", "style_traits 0.0.1",
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"util 0.0.1", "util 0.0.1",
] ]