From 513a75d86a164f9c92bc31050305fbc7df6c8dfe Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 31 Dec 2015 13:38:51 -0800 Subject: [PATCH] Hoist the rest of css/matching.rs into style/. --- components/layout/animation.rs | 66 +----- components/layout/css/matching.rs | 381 ------------------------------ components/layout/lib.rs | 4 - components/layout/traversal.rs | 3 +- components/servo/Cargo.lock | 1 + components/style/Cargo.toml | 1 + components/style/animation.rs | 64 ++++- components/style/lib.rs | 1 + components/style/matching.rs | 375 ++++++++++++++++++++++++++++- ports/cef/Cargo.lock | 1 + ports/gonk/Cargo.lock | 1 + 11 files changed, 440 insertions(+), 458 deletions(-) delete mode 100644 components/layout/css/matching.rs diff --git a/components/layout/animation.rs b/components/layout/animation.rs index bf705465709..8728977dc12 100644 --- a/components/layout/animation.rs +++ b/components/layout/animation.rs @@ -12,49 +12,10 @@ 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::dom::TRestyleDamage; -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>, - 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, @@ -140,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, - 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 -} diff --git a/components/layout/css/matching.rs b/components/layout/css/matching.rs deleted file mode 100644 index 958241d0978..00000000000 --- a/components/layout/css/matching.rs +++ /dev/null @@ -1,381 +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 msg::ParseErrorReporter; -use script::layout_interface::Animation; -use selectors::bloom::BloomFilter; -use selectors::parser::PseudoElement; -use selectors::{Element}; -use std::sync::mpsc::Sender; -use std::sync::{Arc, Mutex}; -use style::context::SharedStyleContext; -use style::data::PrivateStyleData; -use style::dom::{TElement, TNode, TRestyleDamage}; -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, ConcreteRestyleDamage), -} - -trait PrivateMatchMethods<'ln>: TNode<'ln> { - fn cascade_node_pseudo_element(&self, - context: &SharedStyleContext, - parent_style: Option<&Arc>, - applicable_declarations: &[DeclarationBlock], - style: &mut Option>, - applicable_declarations_cache: - &mut ApplicableDeclarationsCache, - new_animations_sender: &Mutex>, - 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>) - -> 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::(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, - candidate: &StyleSharingCandidate) - -> Option> { - 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) - -> StyleSharingResult<>::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 = <>::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|); - - - // 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, - applicable_declarations: &ApplicableDeclarations, - applicable_declarations_cache: &mut ApplicableDeclarationsCache, - new_animations_sender: &Mutex>) { - // 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 {} diff --git a/components/layout/lib.rs b/components/layout/lib.rs index e1d2f77f32b..2b17e3b677d 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -96,7 +96,3 @@ mod table_wrapper; mod text; mod traversal; mod wrapper; - -mod css { - pub mod matching; -} diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index ded5d81118c..b740e7dbcf3 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -6,7 +6,6 @@ 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; @@ -17,7 +16,7 @@ use std::cell::RefCell; use std::mem; use style::context::StyleContext; use style::dom::{TRestyleDamage, UnsafeNode}; -use style::matching::ApplicableDeclarations; +use style::matching::{ApplicableDeclarations, ElementMatchMethods, MatchMethods, StyleSharingResult}; use util::opts; use util::tid::tid; use wrapper::{LayoutNode, ThreadSafeLayoutNode}; diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 3d6d7e60012..264fbbdc470 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -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", ] diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index b39bc4d759a..608df090869 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -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" diff --git a/components/style/animation.rs b/components/style/animation.rs index 374b5396bee..10f96b19b1c 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -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 GetMod for Vec { &(*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>, + 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(animation: &Animation, + style: &mut Arc, + 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 +} diff --git a/components/style/lib.rs b/components/style/lib.rs index 33e6d515994..e010a92f0b2 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -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; diff --git a/components/style/matching.rs b/components/style/matching.rs index 3e91425cec9..b8f555edc02 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -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 { + /// 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>, + applicable_declarations: &[DeclarationBlock], + style: &mut Option>, + applicable_declarations_cache: + &mut ApplicableDeclarationsCache, + new_animations_sender: &Mutex>, + 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>) + -> 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::(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, + candidate: &StyleSharingCandidate) + -> Option> { + 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) + -> StyleSharingResult<>::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 = <>::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|); + + + // 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, + applicable_declarations: &ApplicableDeclarations, + applicable_declarations_cache: &mut ApplicableDeclarationsCache, + new_animations_sender: &Mutex>) { + // 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 {} diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 2289d09cbc4..3d008ce8a9b 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -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", ] diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index 2a8ae352aa6..71e3966c3bd 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -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", ]