mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
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:
commit
f3075d1319
20 changed files with 762 additions and 865 deletions
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -96,7 +96,3 @@ mod table_wrapper;
|
||||||
mod text;
|
mod text;
|
||||||
mod traversal;
|
mod traversal;
|
||||||
mod wrapper;
|
mod wrapper;
|
||||||
|
|
||||||
mod css {
|
|
||||||
pub mod matching;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
1
components/servo/Cargo.lock
generated
1
components/servo/Cargo.lock
generated
|
@ -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",
|
||||||
]
|
]
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
1
ports/cef/Cargo.lock
generated
|
@ -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
1
ports/gonk/Cargo.lock
generated
|
@ -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",
|
||||||
]
|
]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue