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 gfx::display_list::OpaqueNode;
|
||||
use incremental::{self, RestyleDamage};
|
||||
use incremental::RestyleDamage;
|
||||
use msg::constellation_msg::{AnimationState, ConstellationChan, PipelineId};
|
||||
use script::layout_interface::Animation;
|
||||
use script_traits::LayoutMsg as ConstellationMsg;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::sync::mpsc::{Sender, Receiver};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use style::animation::{GetMod, PropertyAnimation};
|
||||
use style::properties::ComputedValues;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use style::animation::update_style_for_animation;
|
||||
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.
|
||||
/// Also expire any old animations that have completed, inserting them into `expired_animations`.
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 gfx::display_list::{ClippingRegion, DisplayList};
|
||||
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 model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
|
||||
use msg::compositor_msg::LayerType;
|
||||
|
@ -48,6 +48,7 @@ use std::sync::Arc;
|
|||
use std::sync::atomic::Ordering;
|
||||
use std::{fmt, mem, raw};
|
||||
use style::computed_values::{clear, display, empty_cells, float, position, text_align};
|
||||
use style::dom::TRestyleDamage;
|
||||
use style::properties::{self, ComputedValues};
|
||||
use style::values::computed::LengthOrPercentageOrAuto;
|
||||
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, TableFlow};
|
||||
|
@ -1046,7 +1047,7 @@ impl BaseFlow {
|
|||
}
|
||||
|
||||
// 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);
|
||||
|
||||
BaseFlow {
|
||||
|
|
|
@ -18,7 +18,7 @@ use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode};
|
|||
use gfx::text::glyph::CharIndex;
|
||||
use gfx::text::text_run::{TextRun, TextRunSlice};
|
||||
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::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT};
|
||||
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::{overflow_x, position, text_decoration, transform_style};
|
||||
use style::computed_values::{white_space, word_break, z_index};
|
||||
use style::dom::TRestyleDamage;
|
||||
use style::properties::ComputedValues;
|
||||
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
|
||||
use style::values::computed::{LengthOrPercentageOrNone};
|
||||
|
@ -833,7 +834,7 @@ impl Fragment {
|
|||
self.border_box.start,
|
||||
size);
|
||||
|
||||
let mut restyle_damage = incremental::rebuild_and_reflow();
|
||||
let mut restyle_damage = RestyleDamage::rebuild_and_reflow();
|
||||
restyle_damage.remove(RECONSTRUCT_FLOW);
|
||||
|
||||
Fragment {
|
||||
|
|
|
@ -13,12 +13,13 @@ use flow::{InorderFlowTraversal};
|
|||
use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, ImmutableFlowUtils};
|
||||
use fragment::{Fragment, GeneratedContentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
|
||||
use gfx::display_list::OpaqueNode;
|
||||
use incremental::{self, RESOLVE_GENERATED_CONTENT};
|
||||
use incremental::{RESOLVE_GENERATED_CONTENT, RestyleDamage};
|
||||
use smallvec::SmallVec;
|
||||
use std::collections::{HashMap, LinkedList};
|
||||
use std::sync::Arc;
|
||||
use style::computed_values::content::ContentItem;
|
||||
use style::computed_values::{display, list_style_type};
|
||||
use style::dom::TRestyleDamage;
|
||||
use style::properties::ComputedValues;
|
||||
use text::TextRunScanner;
|
||||
use wrapper::PseudoElementType;
|
||||
|
@ -431,7 +432,7 @@ fn render_text(layout_context: &LayoutContext,
|
|||
fragments.push_back(Fragment::from_opaque_node_and_style(node,
|
||||
pseudo,
|
||||
style,
|
||||
incremental::rebuild_and_reflow(),
|
||||
RestyleDamage::rebuild_and_reflow(),
|
||||
info));
|
||||
// FIXME(pcwalton): This should properly handle multiple marker fragments. This could happen
|
||||
// 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::sync::Arc;
|
||||
use style::computed_values::float;
|
||||
use style::dom::TRestyleDamage;
|
||||
use style::properties::ComputedValues;
|
||||
|
||||
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 {
|
||||
/// 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 {
|
||||
let old: &ComputedValues =
|
||||
match old.as_ref() {
|
||||
None => return rebuild_and_reflow(),
|
||||
None => return RestyleDamage::rebuild_and_reflow(),
|
||||
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
|
||||
// of the transform, then reflow is required to rebuild the layers.
|
||||
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
|
||||
|
@ -256,7 +261,7 @@ impl<'a> LayoutDamageComputation for &'a mut Flow {
|
|||
|
||||
fn reflow_entire_document(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);
|
||||
for kid in self_base.children.iter_mut() {
|
||||
kid.reflow_entire_document();
|
||||
|
|
|
@ -69,6 +69,7 @@ use style::dom::{TDocument, TElement, TNode};
|
|||
use style::media_queries::{Device, MediaType};
|
||||
use style::selector_matching::{Stylist, USER_OR_USER_AGENT_STYLESHEETS};
|
||||
use style::stylesheets::{CSSRuleIteratorExt, Stylesheet};
|
||||
use traversal::RecalcStyleAndConstructFlows;
|
||||
use url::Url;
|
||||
use util::geometry::MAX_RECT;
|
||||
use util::ipc::OptionalIpcSender;
|
||||
|
@ -1024,10 +1025,12 @@ impl LayoutTask {
|
|||
// Perform CSS selector matching and flow construction.
|
||||
match self.parallel_traversal {
|
||||
None => {
|
||||
sequential::traverse_dom_preorder(node, &shared_layout_context);
|
||||
sequential::traverse_dom_preorder::<ServoLayoutNode, RecalcStyleAndConstructFlows>(
|
||||
node, &shared_layout_context);
|
||||
}
|
||||
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 traversal;
|
||||
mod wrapper;
|
||||
|
||||
mod css {
|
||||
pub mod matching;
|
||||
}
|
||||
|
|
|
@ -19,8 +19,7 @@ use style::dom::UnsafeNode;
|
|||
use traversal::PostorderNodeMutTraversal;
|
||||
use traversal::{AssignBSizesAndStoreOverflow, AssignISizes, BubbleISizes};
|
||||
use traversal::{BuildDisplayList, ComputeAbsolutePositions};
|
||||
use traversal::{ConstructFlows, RecalcStyleForNode};
|
||||
use traversal::{PostorderDomTraversal, PreorderDomTraversal};
|
||||
use traversal::{DomTraversal, DomTraversalContext};
|
||||
use util::opts;
|
||||
use util::workqueue::{WorkQueue, WorkUnit, WorkerProxy};
|
||||
use wrapper::LayoutNode;
|
||||
|
@ -73,105 +72,6 @@ pub type ChunkedFlowTraversalFunction =
|
|||
|
||||
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.
|
||||
pub struct FlowParallelInfo {
|
||||
/// 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, 'ln, ConcreteLayoutNode> ParallelPostorderDomTraversal<'ln, ConcreteLayoutNode>
|
||||
for ConstructFlows<'a>
|
||||
where ConcreteLayoutNode: LayoutNode<'ln> {
|
||||
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,
|
||||
/// A parallel top-down DOM traversal.
|
||||
#[inline(always)]
|
||||
fn top_down_dom<'ln, N, T>(unsafe_nodes: 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 layout_context = LayoutContext::new(shared_layout_context);
|
||||
let recalc_style_for_node_traversal = RecalcStyleForNode {
|
||||
let traversal_context = DomTraversalContext {
|
||||
layout_context: &layout_context,
|
||||
root: unsafe_nodes.1,
|
||||
};
|
||||
|
||||
// The UFCS is necessary here to select the proper set of generic routines which
|
||||
// will eventually cast the UnsafeNode to a concrete LayoutNode implementation.
|
||||
<RecalcStyleForNode as ParallelPreorderDomTraversal<'ln, ConcreteLayoutNode>>
|
||||
::run_parallel(&recalc_style_for_node_traversal, unsafe_nodes, proxy)
|
||||
let mut discovered_child_nodes = Vec::new();
|
||||
for unsafe_node in *unsafe_nodes.0 {
|
||||
// Get a real layout node.
|
||||
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>>(
|
||||
root: OpaqueNode,
|
||||
// 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_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,
|
||||
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 layout_context = LayoutContext::new(shared_layout_context);
|
||||
let construct_flows_traversal = ConstructFlows {
|
||||
let traversal_context = DomTraversalContext {
|
||||
layout_context: &layout_context,
|
||||
root: root,
|
||||
};
|
||||
|
||||
// The UFCS is necessary here to select the proper set of generic routines which
|
||||
// will eventually cast the UnsafeNode to a concrete LayoutNode implementation.
|
||||
<ConstructFlows as ParallelPostorderDomTraversal<'ln, ConcreteLayoutNode>>
|
||||
::run_parallel(&construct_flows_traversal, unsafe_node)
|
||||
// Get a real layout node.
|
||||
let mut node = unsafe { N::from_unsafe(&unsafe_node) };
|
||||
loop {
|
||||
// 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,
|
||||
|
@ -441,12 +384,14 @@ fn run_queue_with_custom_work_data_type<To, F>(
|
|||
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,
|
||||
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| {
|
||||
queue.push(WorkUnit {
|
||||
fun: recalc_style::<'ln, ConcreteLayoutNode>,
|
||||
fun: top_down_dom::<N, T>,
|
||||
data: (box vec![root.to_unsafe()], root.opaque()),
|
||||
});
|
||||
}, shared_layout_context);
|
||||
|
|
|
@ -14,39 +14,33 @@ use fragment::FragmentBorderBoxIterator;
|
|||
use generated_content::ResolveGeneratedContent;
|
||||
use traversal::PostorderNodeMutTraversal;
|
||||
use traversal::{AssignBSizesAndStoreOverflow, AssignISizes};
|
||||
use traversal::{BubbleISizes, ConstructFlows, RecalcStyleForNode};
|
||||
use traversal::{BuildDisplayList, ComputeAbsolutePositions};
|
||||
use traversal::{PostorderDomTraversal, PreorderDomTraversal};
|
||||
use traversal::{BubbleISizes, BuildDisplayList, ComputeAbsolutePositions};
|
||||
use traversal::{DomTraversal, DomTraversalContext};
|
||||
use util::opts;
|
||||
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)
|
||||
where N: LayoutNode<'le> {
|
||||
fn doit<'le, N>(node: N,
|
||||
recalc_style: RecalcStyleForNode,
|
||||
construct_flows: ConstructFlows)
|
||||
where N: LayoutNode<'le> {
|
||||
recalc_style.process(node);
|
||||
where N: LayoutNode<'ln>,
|
||||
T: DomTraversal<'ln, N> {
|
||||
fn doit<'a, 'ln, N, T>(context: &'a DomTraversalContext<'a>, node: N)
|
||||
where N: LayoutNode<'ln>, T: DomTraversal<'ln, N> {
|
||||
T::process_preorder(context, node);
|
||||
|
||||
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 recalc_style = RecalcStyleForNode {
|
||||
layout_context: &layout_context,
|
||||
root: root.opaque(),
|
||||
};
|
||||
let construct_flows = ConstructFlows {
|
||||
let traversal_context = DomTraversalContext {
|
||||
layout_context: &layout_context,
|
||||
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) {
|
||||
|
|
|
@ -6,18 +6,17 @@
|
|||
|
||||
use construct::FlowConstructor;
|
||||
use context::LayoutContext;
|
||||
use css::matching::{ElementMatchMethods, MatchMethods, StyleSharingResult};
|
||||
use flow::{PostorderFlowTraversal, PreorderFlowTraversal};
|
||||
use flow::{self, Flow};
|
||||
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 selectors::bloom::BloomFilter;
|
||||
use std::cell::RefCell;
|
||||
use std::mem;
|
||||
use style::context::StyleContext;
|
||||
use style::dom::UnsafeNode;
|
||||
use style::matching::ApplicableDeclarations;
|
||||
use style::dom::{TRestyleDamage, UnsafeNode};
|
||||
use style::matching::{ApplicableDeclarations, ElementMatchMethods, MatchMethods, StyleSharingResult};
|
||||
use util::opts;
|
||||
use util::tid::tid;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/// A top-down traversal.
|
||||
pub trait PreorderDomTraversal<'ln, ConcreteLayoutNode: LayoutNode<'ln>> {
|
||||
/// The operation to perform. Return true to continue or false to stop.
|
||||
fn process(&self, node: ConcreteLayoutNode);
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct DomTraversalContext<'a> {
|
||||
pub layout_context: &'a LayoutContext<'a>,
|
||||
pub root: OpaqueNode,
|
||||
}
|
||||
|
||||
/// A bottom-up traversal, with a optional in-order pass.
|
||||
pub trait PostorderDomTraversal<'ln, ConcreteLayoutNode: LayoutNode<'ln>> {
|
||||
/// The operation to perform. Return true to continue or false to stop.
|
||||
fn process(&self, node: ConcreteLayoutNode);
|
||||
pub trait DomTraversal<'ln, N: LayoutNode<'ln>> {
|
||||
fn process_preorder<'a>(context: &'a DomTraversalContext<'a>, node: N);
|
||||
fn process_postorder<'a>(context: &'a DomTraversalContext<'a>, node: N);
|
||||
}
|
||||
|
||||
/// 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.
|
||||
|
@ -139,18 +151,9 @@ pub trait PostorderNodeMutTraversal<'ln, ConcreteThreadSafeLayoutNode: ThreadSaf
|
|||
|
||||
/// The recalc-style-for-node traversal, which styles each node and must run before
|
||||
/// 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]
|
||||
#[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.
|
||||
//
|
||||
// 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();
|
||||
|
||||
// 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.
|
||||
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;
|
||||
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.
|
||||
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() {
|
||||
Some(element) => {
|
||||
|
@ -194,7 +197,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode>
|
|||
let shareable_element = match node.as_element() {
|
||||
Some(element) => {
|
||||
// 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,
|
||||
Some(&*bf),
|
||||
&mut applicable_declarations) {
|
||||
|
@ -205,8 +208,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode>
|
|||
},
|
||||
None => {
|
||||
if node.has_changed() {
|
||||
node.to_threadsafe().set_restyle_damage(
|
||||
incremental::rebuild_and_reflow())
|
||||
node.set_restyle_damage(N::ConcreteRestyleDamage::rebuild_and_reflow())
|
||||
}
|
||||
None
|
||||
},
|
||||
|
@ -214,11 +216,11 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode>
|
|||
|
||||
// Perform the CSS cascade.
|
||||
unsafe {
|
||||
node.cascade_node(self.layout_context.shared,
|
||||
node.cascade_node(&context.layout_context.shared.style_context,
|
||||
parent_opt,
|
||||
&applicable_declarations,
|
||||
&mut self.layout_context.applicable_declarations_cache(),
|
||||
&self.layout_context.shared_context().new_animations_sender);
|
||||
&mut context.layout_context.applicable_declarations_cache(),
|
||||
&context.layout_context.shared_context().new_animations_sender);
|
||||
}
|
||||
|
||||
// Add ourselves to the LRU cache.
|
||||
|
@ -228,7 +230,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode>
|
|||
}
|
||||
StyleSharingResult::StyleWasShared(index, damage) => {
|
||||
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);
|
||||
|
||||
// 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.
|
||||
#[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]
|
||||
#[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.
|
||||
{
|
||||
let tnode = node.to_threadsafe();
|
||||
|
@ -265,7 +257,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PostorderDomTraversal<'ln, ConcreteLayoutNode>
|
|||
// Always reconstruct if incremental layout is turned off.
|
||||
let nonincremental_layout = opts::get().nonincremental_layout;
|
||||
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) {
|
||||
flow_constructor.process(&tnode);
|
||||
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_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 => {
|
||||
debug!("[{}] - {:X}, and deleting BF.", tid(), unsafe_layout_node.0);
|
||||
// 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.
|
||||
node.remove_from_bloom_filter(&mut *bf);
|
||||
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
|
||||
/// 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> {
|
||||
type ConcreteElement = ServoLayoutElement<'ln>;
|
||||
type ConcreteDocument = ServoLayoutDocument<'ln>;
|
||||
type ConcreteRestyleDamage = RestyleDamage;
|
||||
|
||||
fn to_unsafe(&self) -> UnsafeNode {
|
||||
unsafe {
|
||||
|
@ -237,6 +238,14 @@ impl<'ln> TNode<'ln> for ServoLayoutNode<'ln> {
|
|||
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>> {
|
||||
unsafe {
|
||||
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;
|
||||
|
||||
/// Get the description of how to account for recent style changes.
|
||||
/// This is a simple bitfield and fine to copy by value.
|
||||
fn restyle_damage(self) -> RestyleDamage {
|
||||
self.borrow_layout_data().unwrap().restyle_damage
|
||||
}
|
||||
fn restyle_damage(self) -> RestyleDamage;
|
||||
|
||||
/// Set the restyle damage field.
|
||||
fn set_restyle_damage(self, damage: RestyleDamage) {
|
||||
self.mutate_layout_data().unwrap().restyle_damage = damage;
|
||||
}
|
||||
fn set_restyle_damage(self, damage: RestyleDamage);
|
||||
|
||||
/// Returns the layout data flags for this node.
|
||||
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 {
|
||||
unsafe {
|
||||
(*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)",
|
||||
"string_cache 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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)",
|
||||
"util 0.0.1",
|
||||
]
|
||||
|
|
|
@ -38,5 +38,6 @@ string_cache = "0.2"
|
|||
euclid = {version = "0.4", features = ["plugins"]}
|
||||
serde = "0.6"
|
||||
serde_macros = "0.6"
|
||||
time = "0.1"
|
||||
url = "0.5.2"
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use app_units::Au;
|
||||
use cssparser::{Color, RGBA};
|
||||
use dom::OpaqueNode;
|
||||
use dom::{OpaqueNode, TRestyleDamage};
|
||||
use euclid::point::Point2D;
|
||||
use properties::ComputedValues;
|
||||
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 std::cmp::Ordering;
|
||||
use std::iter::repeat;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use time;
|
||||
use util::bezier::Bezier;
|
||||
use values::CSSFloat;
|
||||
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
|
||||
|
@ -922,3 +925,62 @@ impl<T> GetMod for Vec<T> {
|
|||
&(*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)]
|
||||
|
||||
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 selectors::matching::DeclarationBlock;
|
||||
use selectors::states::ElementState;
|
||||
use smallvec::VecLike;
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::BitOr;
|
||||
use std::sync::Arc;
|
||||
use string_cache::{Atom, Namespace};
|
||||
|
||||
/// 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 {
|
||||
type ConcreteElement: TElement<'ln, ConcreteNode = Self, ConcreteDocument = Self::ConcreteDocument>;
|
||||
type ConcreteDocument: TDocument<'ln, ConcreteNode = Self, ConcreteElement = Self::ConcreteElement>;
|
||||
type ConcreteRestyleDamage: TRestyleDamage;
|
||||
|
||||
fn to_unsafe(&self) -> UnsafeNode;
|
||||
unsafe fn from_unsafe(n: &UnsafeNode) -> Self;
|
||||
|
@ -134,6 +141,12 @@ pub trait TNode<'ln> : Sized + Copy + Clone {
|
|||
#[inline(always)]
|
||||
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 first_child(&self) -> Option<Self>;
|
||||
|
|
|
@ -40,6 +40,7 @@ extern crate smallvec;
|
|||
#[macro_use(atom, ns)] extern crate string_cache;
|
||||
#[macro_use]
|
||||
extern crate style_traits;
|
||||
extern crate time;
|
||||
extern crate url;
|
||||
extern crate util;
|
||||
|
||||
|
|
|
@ -2,22 +2,31 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use dom::{TElement, TNode};
|
||||
use properties::{ComputedValues, PropertyDeclaration};
|
||||
use selector_matching::DeclarationBlock;
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
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::{common_style_affecting_attributes, rare_style_affecting_attributes};
|
||||
use selectors::parser::PseudoElement;
|
||||
use smallvec::SmallVec;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::slice::Iter;
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use string_cache::{Atom, Namespace};
|
||||
use util::arc_ptr_eq;
|
||||
use util::cache::{LRUCache, SimpleHashCache};
|
||||
use util::opts;
|
||||
use util::vec::ForgetfulSink;
|
||||
|
||||
/// Pieces of layout/css/matching.rs, which will eventually be merged
|
||||
/// into this file.
|
||||
/// High-level interface to CSS selector matching.
|
||||
|
||||
fn create_common_style_affecting_attributes_from_element<'le, E: TElement<'le>>(element: &E)
|
||||
-> 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)",
|
||||
"string_cache 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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)",
|
||||
"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)",
|
||||
"string_cache 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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)",
|
||||
"util 0.0.1",
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue