servo: Merge #12838 - Fix restyling on viewport resize (from emilio:viewport); r=SimonSapin
authorEmilio Cobos Álvarez <ecoal95@gmail.com>
Tue, 16 Aug 2016 13:48:20 -0500
changeset 388481 cbf35bb355998dbfaedb916d5c57e09eb4892740
parent 388480 ee00927a1d41ac0fd34a2e3af2554a8f1468fd14
child 388482 6f3d33839f4da2a318ae2556a55ab7e827de0dd6
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersSimonSapin
servo: Merge #12838 - Fix restyling on viewport resize (from emilio:viewport); r=SimonSapin <!-- Please describe your changes on the following line: --> Fixes https://github.com/servo/servo/issues/12835 --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors <!-- Either: --> - [x] There are tests for these changes OR <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 49431be44a7bbc256829463f4ec4658801742bd9
servo/components/layout_thread/lib.rs
servo/components/style/animation.rs
servo/components/style/cascade_info.rs
servo/components/style/custom_properties.rs
servo/components/style/lib.rs
servo/components/style/matching.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/selector_matching.rs
servo/components/style/stylesheets.rs
servo/components/style/traversal.rs
servo/components/style/values/specified/mod.rs
--- a/servo/components/layout_thread/lib.rs
+++ b/servo/components/layout_thread/lib.rs
@@ -1109,16 +1109,24 @@ impl LayoutThread {
             if data.document_stylesheets.iter().any(|sheet| sheet.dirty_on_viewport_size_change) {
                 let mut iter = node.traverse_preorder();
 
                 let mut next = iter.next();
                 while let Some(node) = next {
                     if node.needs_dirty_on_viewport_size_changed() {
                         // NB: The dirty bit is propagated down the tree.
                         unsafe { node.set_dirty(true); }
+
+                        let mut current = node.parent_node();
+                        while let Some(node) = current {
+                            if node.has_dirty_descendants() { break; }
+                            unsafe { node.set_dirty_descendants(true); }
+                            current = node.parent_node();
+                        }
+
                         next = iter.next_skipping_children();
                     } else {
                         next = iter.next();
                     }
                 }
             }
         }
 
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -389,16 +389,17 @@ fn compute_style_for_animation_step(cont
                 source_order: 0,
                 specificity: ::std::u32::MAX,
             };
             let (computed, _) = properties::cascade(context.viewport_size,
                                                     &[declaration_block],
                                                     false,
                                                     Some(previous_style),
                                                     None,
+                                                    None,
                                                     context.error_reporter.clone());
             computed
         }
     }
 }
 
 pub fn maybe_start_animations(context: &SharedStyleContext,
                               new_animations_sender: &Sender<Animation>,
new file mode 100644
--- /dev/null
+++ b/servo/components/style/cascade_info.rs
@@ -0,0 +1,78 @@
+/* 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/. */
+use dom::TNode;
+use properties::{DeclaredValue, PropertyDeclaration};
+use values::HasViewportPercentage;
+
+/// A structure to collect information about the cascade.
+///
+/// This is useful to gather information about what an element is affected by,
+/// and can be used in the future to track optimisations like when a
+/// non-inherited property is explicitly inherited, in order to cut-off the
+/// traversal.
+pub struct CascadeInfo {
+    pub saw_viewport_units: bool,
+    #[cfg(debug_assertions)]
+    finished: bool,
+}
+
+impl CascadeInfo {
+    #[cfg(debug_assertions)]
+    pub fn new() -> Self {
+        CascadeInfo {
+            saw_viewport_units: false,
+            finished: false,
+        }
+    }
+
+    #[cfg(not(debug_assertions))]
+    pub fn new() -> Self {
+        CascadeInfo {
+            saw_viewport_units: false,
+        }
+    }
+
+    /// Called when a property is cascaded.
+    ///
+    /// NOTE: We can add a vast amount of information here.
+    #[inline]
+    pub fn on_cascade_property<T>(&mut self,
+                                  _property_declaration: &PropertyDeclaration,
+                                  value: &DeclaredValue<T>)
+        where T: HasViewportPercentage
+    {
+        // TODO: we can be smarter and keep a property bitfield to keep track of
+        // the last applying rule.
+        if value.has_viewport_percentage() {
+            self.saw_viewport_units = true;
+        }
+    }
+
+    #[cfg(debug_assertions)]
+    fn mark_as_finished_if_appropriate(&mut self) {
+        self.finished = true;
+    }
+
+    #[cfg(not(debug_assertions))]
+    fn mark_as_finished_if_appropriate(&mut self) {}
+
+    #[allow(unsafe_code)]
+    pub fn finish<N: TNode>(mut self, node: &N) {
+        self.mark_as_finished_if_appropriate();
+
+        if self.saw_viewport_units {
+            unsafe {
+                node.set_dirty_on_viewport_size_changed();
+            }
+        }
+    }
+}
+
+#[cfg(debug_assertions)]
+impl Drop for CascadeInfo {
+    fn drop(&mut self) {
+        debug_assert!(self.finished,
+                      "Didn't use the result of CascadeInfo, if you don't need it, consider passing None");
+    }
+}
--- a/servo/components/style/custom_properties.rs
+++ b/servo/components/style/custom_properties.rs
@@ -34,16 +34,22 @@ pub struct SpecifiedValue {
 
     first_token_type: TokenSerializationType,
     last_token_type: TokenSerializationType,
 
     /// Custom property names in var() functions.
     references: HashSet<Name>,
 }
 
+impl ::values::HasViewportPercentage for SpecifiedValue {
+    fn has_viewport_percentage(&self) -> bool {
+        panic!("has_viewport_percentage called before resolving!");
+    }
+}
+
 pub struct BorrowedSpecifiedValue<'a> {
     css: &'a str,
     first_token_type: TokenSerializationType,
     last_token_type: TokenSerializationType,
     references: Option<&'a HashSet<Name>>,
 }
 
 #[derive(Clone, Debug)]
--- a/servo/components/style/lib.rs
+++ b/servo/components/style/lib.rs
@@ -69,16 +69,17 @@ extern crate style_traits;
 extern crate time;
 extern crate url;
 extern crate util;
 
 pub mod animation;
 pub mod attr;
 pub mod bezier;
 pub mod cache;
+pub mod cascade_info;
 pub mod context;
 pub mod custom_properties;
 pub mod data;
 pub mod dom;
 pub mod element_state;
 pub mod error_reporting;
 pub mod font_face;
 #[cfg(feature = "gecko")] pub mod gecko_conversions;
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -4,16 +4,17 @@
 
 //! High-level interface to CSS selector matching.
 
 #![allow(unsafe_code)]
 
 use animation;
 use arc_ptr_eq;
 use cache::{LRUCache, SimpleHashCache};
+use cascade_info::CascadeInfo;
 use context::{StyleContext, SharedStyleContext};
 use data::PrivateStyleData;
 use dom::{TElement, TNode, TRestyleDamage};
 use properties::longhands::display::computed_value as display;
 use properties::{ComputedValues, PropertyDeclaration, cascade};
 use selector_impl::{ElementExt, TheSelectorImpl, PseudoElement};
 use selector_matching::{DeclarationBlock, Stylist};
 use selectors::bloom::BloomFilter;
@@ -438,40 +439,44 @@ trait PrivateMatchMethods: TNode {
     {
         let mut cacheable = true;
         let shared_context = context.shared_context();
         if animate_properties {
             cacheable = !self.update_animations_for_cascade(shared_context,
                                                             &mut old_style) && cacheable;
         }
 
+        let mut cascade_info = CascadeInfo::new();
         let (this_style, is_cacheable) = match parent_style {
             Some(ref parent_style) => {
                 let cache_entry = applicable_declarations_cache.find(applicable_declarations);
                 let cached_computed_values = match cache_entry {
                     Some(ref style) => Some(&**style),
                     None => None,
                 };
 
                 cascade(shared_context.viewport_size,
                         applicable_declarations,
                         shareable,
                         Some(&***parent_style),
                         cached_computed_values,
+                        Some(&mut cascade_info),
                         shared_context.error_reporter.clone())
             }
             None => {
                 cascade(shared_context.viewport_size,
                         applicable_declarations,
                         shareable,
                         None,
                         None,
+                        Some(&mut cascade_info),
                         shared_context.error_reporter.clone())
             }
         };
+        cascade_info.finish(self);
 
         cacheable = cacheable && is_cacheable;
 
         let mut this_style = Arc::new(this_style);
 
         if animate_properties {
             let new_animations_sender = &context.local_context().new_animations_sender;
             let this_opaque = self.opaque();
@@ -492,17 +497,16 @@ trait PrivateMatchMethods: TNode {
                         &**style,
                         &mut this_style,
                         &shared_context.timer);
             }
 
             cacheable = cacheable && !animations_started
         }
 
-
         // Cache the resolved style if it was cacheable.
         if cacheable {
             applicable_declarations_cache.insert(applicable_declarations.to_vec(),
                                                  this_style.clone());
         }
 
         this_style
     }
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -162,16 +162,17 @@
     %>
     pub mod ${property.ident} {
         #![allow(unused_imports)]
         % if not property.derived_from:
             use cssparser::Parser;
             use parser::{ParserContext, ParserContextExtraData};
             use properties::{CSSWideKeyword, DeclaredValue, Shorthand};
         % endif
+        use cascade_info::CascadeInfo;
         use error_reporting::ParseErrorReporter;
         use properties::longhands;
         use properties::property_bit_field::PropertyBitField;
         use properties::{ComputedValues, PropertyDeclaration};
         use properties::style_structs;
         use std::boxed::Box as StdBox;
         use std::collections::HashMap;
         use std::sync::Arc;
@@ -180,32 +181,39 @@
         use string_cache::Atom;
         ${caller.body()}
         #[allow(unused_variables)]
         pub fn cascade_property(declaration: &PropertyDeclaration,
                                 inherited_style: &ComputedValues,
                                 context: &mut computed::Context,
                                 seen: &mut PropertyBitField,
                                 cacheable: &mut bool,
+                                cascade_info: &mut Option<<&mut CascadeInfo>,
                                 error_reporter: &mut StdBox<ParseErrorReporter + Send>) {
             let declared_value = match *declaration {
                 PropertyDeclaration::${property.camel_case}(ref declared_value) => {
                     declared_value
                 }
                 _ => panic!("entered the wrong cascade_property() implementation"),
             };
             % if not property.derived_from:
                 if seen.get_${property.ident}() {
                     return
                 }
                 seen.set_${property.ident}();
                 {
                     let custom_props = context.style().custom_properties();
                     ::properties::substitute_variables_${property.ident}(
-                        declared_value, &custom_props, |value| match *value {
+                        declared_value, &custom_props,
+                    |value| {
+                        if let Some(ref mut cascade_info) = *cascade_info {
+                            cascade_info.on_cascade_property(&declaration,
+                                                             &value);
+                        }
+                        match *value {
                             DeclaredValue::Value(ref specified_value) => {
                                 let computed = specified_value.to_computed_value(context);
                                 context.mutate_style().mutate_${data.current_style_struct.name_lower}()
                                                       .set_${property.ident}(computed);
                             }
                             DeclaredValue::WithVariables { .. } => unreachable!(),
                             DeclaredValue::Initial => {
                                 // We assume that it's faster to use copy_*_from rather than
@@ -221,18 +229,18 @@
                                 //
                                 // FIXME: is it still?
                                 *cacheable = false;
                                 let inherited_struct =
                                     inherited_style.get_${data.current_style_struct.name_lower}();
                                 context.mutate_style().mutate_${data.current_style_struct.name_lower}()
                                        .copy_${property.ident}_from(inherited_struct);
                             }
-                        }, error_reporter
-                    );
+                        }
+                    }, error_reporter);
                 }
 
                 % if property.custom_cascade:
                     cascade_property_custom(declaration,
                                             inherited_style,
                                             context,
                                             seen,
                                             cacheable,
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -29,16 +29,17 @@ use computed_values;
 #[cfg(feature = "servo")] use logical_geometry::{LogicalMargin, PhysicalSide};
 use logical_geometry::WritingMode;
 use parser::{ParserContext, ParserContextExtraData, log_css_error};
 use selectors::matching::DeclarationBlock;
 use stylesheets::Origin;
 use values::LocalToCss;
 use values::HasViewportPercentage;
 use values::computed::{self, ToComputedValue};
+use cascade_info::CascadeInfo;
 #[cfg(feature = "servo")] use values::specified::BorderStyle;
 
 use self::property_bit_field::PropertyBitField;
 
 <%!
     from data import Method, Keyword, to_rust_ident
 %>
 
@@ -768,16 +769,29 @@ pub enum DeclaredValue<T> {
     },
     Initial,
     Inherit,
     // There is no Unset variant here.
     // The 'unset' keyword is represented as either Initial or Inherit,
     // depending on whether the property is inherited.
 }
 
+impl<T: HasViewportPercentage> HasViewportPercentage for DeclaredValue<T> {
+    fn has_viewport_percentage(&self) -> bool {
+        match *self {
+            DeclaredValue::Value(ref v)
+                => v.has_viewport_percentage(),
+            DeclaredValue::WithVariables { .. }
+                => panic!("DeclaredValue::has_viewport_percentage without resolving variables!"),
+            DeclaredValue::Initial |
+            DeclaredValue::Inherit => false,
+        }
+    }
+}
+
 impl<T: ToCss> ToCss for DeclaredValue<T> {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             DeclaredValue::Value(ref inner) => inner.to_css(dest),
             DeclaredValue::WithVariables { ref css, from_shorthand: None, .. } => {
                 dest.write_str(css)
             }
             // https://drafts.csswg.org/css-variables/#variables-in-shorthands
@@ -792,16 +806,30 @@ impl<T: ToCss> ToCss for DeclaredValue<T
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum PropertyDeclaration {
     % for property in data.longhands:
         ${property.camel_case}(DeclaredValue<longhands::${property.ident}::SpecifiedValue>),
     % endfor
     Custom(::custom_properties::Name, DeclaredValue<::custom_properties::SpecifiedValue>),
 }
 
+impl HasViewportPercentage for PropertyDeclaration {
+    fn has_viewport_percentage(&self) -> bool {
+        match *self {
+            % for property in data.longhands:
+                PropertyDeclaration::${property.camel_case}(ref val) => {
+                    val.has_viewport_percentage()
+                },
+            % endfor
+            PropertyDeclaration::Custom(_, ref val) => {
+                val.has_viewport_percentage()
+            }
+        }
+    }
+}
 
 #[derive(Eq, PartialEq, Copy, Clone)]
 pub enum PropertyDeclarationParseResult {
     UnknownProperty,
     ExperimentalProperty,
     InvalidValue,
     ValidOrIgnoredDeclaration,
 }
@@ -849,29 +877,16 @@ impl ToCss for PropertyDeclaration {
             PropertyDeclaration::Custom(_, ref value) => value.to_css(dest),
             % if any(property.derived_from for property in data.longhands):
                 _ => Err(fmt::Error),
             % endif
         }
     }
 }
 
-impl HasViewportPercentage for PropertyDeclaration {
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            % for property in data.longhands:
-                PropertyDeclaration::${property.camel_case}(DeclaredValue::Value(ref val)) => {
-                    val.has_viewport_percentage()
-                },
-            % endfor
-            _ => false
-        }
-    }
-}
-
 impl PropertyDeclaration {
     pub fn name(&self) -> PropertyDeclarationName {
         match *self {
             % for property in data.longhands:
                 PropertyDeclaration::${property.camel_case}(..) =>
                 % if not property.derived_from:
                     PropertyDeclarationName::Longhand("${property.name}"),
                 % else:
@@ -1617,16 +1632,17 @@ mod lazy_static_module {
 #[allow(unused_mut, unused_imports)]
 fn cascade_with_cached_declarations(
         viewport_size: Size2D<Au>,
         applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclaration>>],
         shareable: bool,
         parent_style: &ComputedValues,
         cached_style: &ComputedValues,
         custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
+        mut cascade_info: Option<<&mut CascadeInfo>,
         mut error_reporter: StdBox<ParseErrorReporter + Send>)
         -> ComputedValues {
     let mut context = computed::Context {
         is_root_element: false,
         viewport_size: viewport_size,
         inherited_style: parent_style,
         style: ComputedValues::new(
             custom_properties,
@@ -1659,17 +1675,22 @@ fn cascade_with_cached_declarations(
                                 % if style_struct.inherited:
                                     if seen.get_${property.ident}() {
                                         continue
                                     }
                                     seen.set_${property.ident}();
                                     let custom_props = context.style().custom_properties();
                                     substitute_variables_${property.ident}(
                                         declared_value, &custom_props,
-                                        |value| match *value {
+                                    |value| {
+                                        if let Some(ref mut cascade_info) = cascade_info {
+                                            cascade_info.on_cascade_property(&declaration,
+                                                                             &value);
+                                        }
+                                        match *value {
                                             DeclaredValue::Value(ref specified_value)
                                             => {
                                                 let computed = specified_value.to_computed_value(&context);
                                                 context.mutate_style().mutate_${style_struct.name_lower}()
                                                        .set_${property.ident}(computed);
                                             },
                                             DeclaredValue::Initial
                                             => {
@@ -1683,18 +1704,18 @@ fn cascade_with_cached_declarations(
                                                 // matter.
                                                 //
                                                 // FIXME: is it still?
                                                 let inherited_struct = parent_style.get_${style_struct.ident}();
                                                 context.mutate_style().mutate_${style_struct.name_lower}()
                                                        .copy_${property.ident}_from(inherited_struct);
                                             }
                                             DeclaredValue::WithVariables { .. } => unreachable!()
-                                        }, &mut error_reporter
-                                    );
+                                        }
+                                    }, &mut error_reporter);
                                 % endif
 
                                 % if property.name in data.derived_longhands:
                                     % for derived in data.derived_longhands[property.name]:
                                             longhands::${derived.ident}
                                                      ::derive_from_${property.ident}(&mut context);
                                     % endfor
                                 % endif
@@ -1723,16 +1744,17 @@ fn cascade_with_cached_declarations(
 }
 
 pub type CascadePropertyFn =
     extern "Rust" fn(declaration: &PropertyDeclaration,
                      inherited_style: &ComputedValues,
                      context: &mut computed::Context,
                      seen: &mut PropertyBitField,
                      cacheable: &mut bool,
+                     cascade_info: &mut Option<<&mut CascadeInfo>,
                      error_reporter: &mut StdBox<ParseErrorReporter + Send>);
 
 #[cfg(feature = "servo")]
 static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
     % for property in data.longhands:
         longhands::${property.ident}::cascade_property,
     % endfor
 ];
@@ -1755,16 +1777,17 @@ static CASCADE_PROPERTY: [CascadePropert
 ///     this is ignored.
 ///
 /// Returns the computed values and a boolean indicating whether the result is cacheable.
 pub fn cascade(viewport_size: Size2D<Au>,
                applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclaration>>],
                shareable: bool,
                parent_style: Option<<&ComputedValues>,
                cached_style: Option<<&ComputedValues>,
+               mut cascade_info: Option<<&mut CascadeInfo>,
                mut error_reporter: StdBox<ParseErrorReporter + Send>)
                -> (ComputedValues, bool) {
     let initial_values = ComputedValues::initial_values();
     let (is_root_element, inherited_style) = match parent_style {
         Some(parent_style) => (false, parent_style),
         None => (true, initial_values),
     };
 
@@ -1789,16 +1812,17 @@ pub fn cascade(viewport_size: Size2D<Au>
 
     if let (Some(cached_style), Some(parent_style)) = (cached_style, parent_style) {
         let style = cascade_with_cached_declarations(viewport_size,
                                                      applicable_declarations,
                                                      shareable,
                                                      parent_style,
                                                      cached_style,
                                                      custom_properties,
+                                                     cascade_info,
                                                      error_reporter);
         return (style, false)
     }
 
     let mut context = computed::Context {
         is_root_element: is_root_element,
         viewport_size: viewport_size,
         inherited_style: inherited_style,
@@ -1858,16 +1882,17 @@ pub fn cascade(viewport_size: Size2D<Au>
                         continue
                     }
                     let discriminant = declaration.discriminant_value();
                     (cascade_property[discriminant])(declaration,
                                                      inherited_style,
                                                      &mut context,
                                                      &mut seen,
                                                      &mut cacheable,
+                                                     &mut cascade_info,
                                                      &mut error_reporter);
                 }
             }
         % endfor
     });
 
     let mut style = context.style;
 
--- a/servo/components/style/selector_matching.rs
+++ b/servo/components/style/selector_matching.rs
@@ -225,30 +225,32 @@ impl Stylist {
                                          -> Option<Arc<ComputedValues>> {
         debug_assert!(TheSelectorImpl::pseudo_element_cascade_type(pseudo).is_precomputed());
         if let Some(declarations) = self.precomputed_pseudo_element_decls.get(pseudo) {
             let (computed, _) =
                 properties::cascade(self.device.au_viewport_size(),
                                     &declarations, false,
                                     parent.map(|p| &**p),
                                     None,
+                                    None,
                                     Box::new(StdoutErrorReporter));
             Some(Arc::new(computed))
         } else {
             parent.map(|p| p.clone())
         }
     }
 
     pub fn lazily_compute_pseudo_element_style<E>(&self,
                                                   element: &E,
                                                   pseudo: &PseudoElement,
                                                   parent: &Arc<ComputedValues>)
                                                   -> Option<Arc<ComputedValues>>
-                                                  where E: Element<Impl=TheSelectorImpl> +
-                                                        PresentationalHintsSynthetizer {
+        where E: Element<Impl=TheSelectorImpl> +
+              PresentationalHintsSynthetizer
+    {
         debug_assert!(TheSelectorImpl::pseudo_element_cascade_type(pseudo).is_lazy());
         if self.pseudos_map.get(pseudo).is_none() {
             return None;
         }
 
         let mut declarations = vec![];
 
         // NB: This being cached could be worth it, maybe allow an optional
@@ -257,18 +259,20 @@ impl Stylist {
                                           None,
                                           None,
                                           Some(pseudo),
                                           &mut declarations);
 
         let (computed, _) =
             properties::cascade(self.device.au_viewport_size(),
                                 &declarations, false,
-                                Some(&**parent), None,
+                                Some(&**parent), None, None,
                                 Box::new(StdoutErrorReporter));
+
+
         Some(Arc::new(computed))
     }
 
     pub fn set_device(&mut self, mut device: Device, stylesheets: &[Arc<Stylesheet>]) {
         let cascaded_rule = stylesheets.iter()
             .flat_map(|s| s.effective_rules(&self.device).viewport())
             .cascade();
 
--- a/servo/components/style/stylesheets.rs
+++ b/servo/components/style/stylesheets.rs
@@ -164,17 +164,18 @@ impl Stylesheet {
                 }
             }
         }
 
         Stylesheet {
             origin: origin,
             rules: rules,
             media: None,
-            dirty_on_viewport_size_change: input.seen_viewport_percentages(),
+            dirty_on_viewport_size_change:
+                input.seen_viewport_percentages(),
         }
     }
 
     /// Set the MediaQueryList associated with the style-sheet.
     pub fn set_media(&mut self, media: Option<MediaQueryList>) {
         self.media = media;
     }
 
--- a/servo/components/style/traversal.rs
+++ b/servo/components/style/traversal.rs
@@ -365,28 +365,14 @@ pub fn recalc_style_at<'a, N, C>(context
     // Before running the children, we need to insert our nodes into the bloom
     // filter.
     debug!("[{}] + {:X}", tid(), unsafe_layout_node.0);
     node.insert_into_bloom_filter(&mut *bf);
 
     // NB: flow construction updates the bloom filter on the way up.
     put_thread_local_bloom_filter(bf, &unsafe_layout_node, context.shared_context());
 
-    // Mark the node as DIRTY_ON_VIEWPORT_SIZE_CHANGE is it uses viewport
-    // percentage units.
-    if !node.needs_dirty_on_viewport_size_changed() {
-        if let Some(element) = node.as_element() {
-            if let Some(ref property_declaration_block) = *element.style_attribute() {
-                if property_declaration_block.declarations().any(|d| d.0.has_viewport_percentage()) {
-                    unsafe {
-                        node.set_dirty_on_viewport_size_changed();
-                    }
-                }
-            }
-        }
-    }
-
     if nonincremental_layout {
         RestyleResult::Continue
     } else {
         restyle_result
     }
 }
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -192,28 +192,29 @@ pub enum Length {
 
     Calc(CalcLengthOrPercentage),
 }
 
 impl HasViewportPercentage for Length {
     fn has_viewport_percentage(&self) -> bool {
         match *self {
             Length::ViewportPercentage(_) => true,
+            Length::Calc(ref calc) => calc.has_viewport_percentage(),
             _ => false
         }
     }
 }
 
 impl ToCss for Length {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             Length::Absolute(length) => write!(dest, "{}px", length.to_f32_px()),
             Length::FontRelative(length) => length.to_css(dest),
             Length::ViewportPercentage(length) => length.to_css(dest),
-            Length::Calc(calc) => calc.to_css(dest),
+            Length::Calc(ref calc) => calc.to_css(dest),
             Length::ServoCharacterWidth(_)
             => panic!("internal CSS values should never be serialized"),
         }
     }
 }
 
 impl Mul<CSSFloat> for Length {
     type Output = Length;
@@ -462,16 +463,17 @@ pub struct CalcLengthOrPercentage {
     pub vmin: Option<ViewportPercentageLength>,
     pub vmax: Option<ViewportPercentageLength>,
     pub em: Option<FontRelativeLength>,
     pub ex: Option<FontRelativeLength>,
     pub ch: Option<FontRelativeLength>,
     pub rem: Option<FontRelativeLength>,
     pub percentage: Option<Percentage>,
 }
+
 impl CalcLengthOrPercentage {
     fn parse_sum(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcSumNode, ()> {
         let mut products = Vec::new();
         products.push(try!(CalcLengthOrPercentage::parse_product(input, expected_unit)));
 
         while let Ok(token) = input.next() {
             match token {
                 Token::Delim('+') => {
@@ -746,16 +748,23 @@ impl CalcLengthOrPercentage {
         match (angle, number) {
             (Some(angle), None) => Ok(Angle(angle)),
             (None, Some(value)) if value == 0. => Ok(Angle(0.)),
             _ => Err(())
         }
     }
 }
 
+impl HasViewportPercentage for CalcLengthOrPercentage {
+    fn has_viewport_percentage(&self) -> bool {
+        self.vw.is_some() || self.vh.is_some() ||
+            self.vmin.is_some() || self.vmax.is_some()
+    }
+}
+
 impl ToCss for CalcLengthOrPercentage {
     #[allow(unused_assignments)]
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         macro_rules! count {
             ( $( $val:ident ),* ) => {
                 {
                     let mut count = 0;
                     $(
@@ -818,17 +827,18 @@ pub enum LengthOrPercentage {
     Length(Length),
     Percentage(Percentage),
     Calc(CalcLengthOrPercentage),
 }
 
 impl HasViewportPercentage for LengthOrPercentage {
     fn has_viewport_percentage(&self) -> bool {
         match *self {
-            LengthOrPercentage::Length(length) => length.has_viewport_percentage(),
+            LengthOrPercentage::Length(ref length) => length.has_viewport_percentage(),
+            LengthOrPercentage::Calc(ref calc) => calc.has_viewport_percentage(),
             _ => false
         }
     }
 }
 
 impl ToCss for LengthOrPercentage {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
@@ -877,17 +887,18 @@ pub enum LengthOrPercentageOrAuto {
     Percentage(Percentage),
     Auto,
     Calc(CalcLengthOrPercentage),
 }
 
 impl HasViewportPercentage for LengthOrPercentageOrAuto {
     fn has_viewport_percentage(&self) -> bool {
         match *self {
-            LengthOrPercentageOrAuto::Length(length) => length.has_viewport_percentage(),
+            LengthOrPercentageOrAuto::Length(ref length) => length.has_viewport_percentage(),
+            LengthOrPercentageOrAuto::Calc(ref calc) => calc.has_viewport_percentage(),
             _ => false
         }
     }
 }
 
 impl ToCss for LengthOrPercentageOrAuto {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
@@ -936,28 +947,29 @@ pub enum LengthOrPercentageOrNone {
     Percentage(Percentage),
     Calc(CalcLengthOrPercentage),
     None,
 }
 
 impl HasViewportPercentage for LengthOrPercentageOrNone {
     fn has_viewport_percentage(&self) -> bool {
         match *self {
-            LengthOrPercentageOrNone::Length(length) => length.has_viewport_percentage(),
+            LengthOrPercentageOrNone::Length(ref length) => length.has_viewport_percentage(),
+            LengthOrPercentageOrNone::Calc(ref calc) => calc.has_viewport_percentage(),
             _ => false
         }
     }
 }
 
 impl ToCss for LengthOrPercentageOrNone {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
-            LengthOrPercentageOrNone::Length(length) => length.to_css(dest),
-            LengthOrPercentageOrNone::Percentage(percentage) => percentage.to_css(dest),
-            LengthOrPercentageOrNone::Calc(calc) => calc.to_css(dest),
+            LengthOrPercentageOrNone::Length(ref length) => length.to_css(dest),
+            LengthOrPercentageOrNone::Percentage(ref percentage) => percentage.to_css(dest),
+            LengthOrPercentageOrNone::Calc(ref calc) => calc.to_css(dest),
             LengthOrPercentageOrNone::None => dest.write_str("none"),
         }
     }
 }
 impl LengthOrPercentageOrNone {
     fn parse_internal(input: &mut Parser, context: &AllowedNumericType)
                       -> Result<LengthOrPercentageOrNone, ()>
     {
@@ -992,17 +1004,17 @@ impl LengthOrPercentageOrNone {
 pub enum LengthOrNone {
     Length(Length),
     None,
 }
 
 impl HasViewportPercentage for LengthOrNone {
     fn has_viewport_percentage(&self) -> bool {
         match *self {
-            LengthOrNone::Length(length) => length.has_viewport_percentage(),
+            LengthOrNone::Length(ref length) => length.has_viewport_percentage(),
             _ => false
         }
     }
 }
 
 impl ToCss for LengthOrNone {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
@@ -1046,16 +1058,17 @@ pub enum LengthOrPercentageOrAutoOrConte
     Auto,
     Content
 }
 
 impl HasViewportPercentage for LengthOrPercentageOrAutoOrContent {
     fn has_viewport_percentage(&self) -> bool {
         match *self {
             LengthOrPercentageOrAutoOrContent::Length(length) => length.has_viewport_percentage(),
+            LengthOrPercentageOrAutoOrContent::Calc(ref calc) => calc.has_viewport_percentage(),
             _ => false
         }
     }
 }
 
 impl ToCss for LengthOrPercentageOrAutoOrContent {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {