Bug 1368240: Record whether an snapshot is recording a class attribute change or id change. r?heycam draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Fri, 09 Jun 2017 17:16:29 +0200
changeset 592542 3f2c9b9eb92b2b9e4efaa78558cd994de5ca96e3
parent 592541 1ce5a4121cb2aa98ae91dbfbbb763cfd56c1f576
child 592543 84d97987e40e6b4b1b2b30e5dd3365514020b8e3
push id63430
push userbmo:emilio+bugs@crisal.io
push dateMon, 12 Jun 2017 12:30:48 +0000
reviewersheycam
bugs1368240
milestone55.0a1
Bug 1368240: Record whether an snapshot is recording a class attribute change or id change. r?heycam I'll use this information in order to get fewer dependencies out of the dependency set. MozReview-Commit-ID: 5HlmKmSNO8p
layout/base/ServoRestyleManager.cpp
layout/style/ServoElementSnapshot.cpp
layout/style/ServoElementSnapshot.h
servo/components/script/dom/document.rs
servo/components/style/gecko/snapshot.rs
servo/components/style/servo/selector_parser.rs
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -795,17 +795,17 @@ ServoRestyleManager::AttributeWillChange
 {
   MOZ_ASSERT(!mInStyleRefresh);
 
   if (!aElement->HasServoData()) {
     return;
   }
 
   ServoElementSnapshot& snapshot = SnapshotFor(aElement);
-  snapshot.AddAttrs(aElement);
+  snapshot.AddAttrs(aElement, aNameSpaceID, aAttribute);
 
   if (AttributeInfluencesOtherPseudoClassState(aElement, aAttribute)) {
     snapshot.AddOtherPseudoClassState(aElement);
   }
 
   if (Element* parent = aElement->GetFlattenedTreeParentElementForStyle()) {
     parent->NoteDirtyDescendantsForServo();
   }
--- a/layout/style/ServoElementSnapshot.cpp
+++ b/layout/style/ServoElementSnapshot.cpp
@@ -11,34 +11,51 @@
 
 namespace mozilla {
 
 ServoElementSnapshot::ServoElementSnapshot(const Element* aElement)
   : mState(0)
   , mContains(Flags(0))
   , mIsTableBorderNonzero(false)
   , mIsMozBrowserFrame(false)
+  , mClassAttributeChanged(false)
+  , mIdAttributeChanged(false)
+  , mOtherAttributeChanged(false)
 {
   MOZ_COUNT_CTOR(ServoElementSnapshot);
   mIsHTMLElementInHTMLDocument =
     aElement->IsHTMLElement() && aElement->IsInHTMLDocument();
   mIsInChromeDocument = nsContentUtils::IsChromeDoc(aElement->OwnerDoc());
   mSupportsLangAttr = aElement->SupportsLangAttr();
 }
 
 ServoElementSnapshot::~ServoElementSnapshot()
 {
   MOZ_COUNT_DTOR(ServoElementSnapshot);
 }
 
 void
-ServoElementSnapshot::AddAttrs(Element* aElement)
+ServoElementSnapshot::AddAttrs(Element* aElement,
+                               int32_t aNameSpaceID,
+                               nsIAtom* aAttribute)
 {
   MOZ_ASSERT(aElement);
 
+  if (aNameSpaceID == kNameSpaceID_None) {
+    if (aAttribute == nsGkAtoms::_class) {
+      mClassAttributeChanged = true;
+    } else if (aAttribute == nsGkAtoms::id) {
+      mIdAttributeChanged = true;
+    } else {
+      mOtherAttributeChanged = true;
+    }
+  } else {
+    mOtherAttributeChanged = true;
+  }
+
   if (HasAttrs()) {
     return;
   }
 
   uint32_t attrCount = aElement->GetAttrCount();
   const nsAttrName* attrName;
   for (uint32_t i = 0; i < attrCount; ++i) {
     attrName = aElement->GetAttrNameAt(i);
--- a/layout/style/ServoElementSnapshot.h
+++ b/layout/style/ServoElementSnapshot.h
@@ -89,18 +89,23 @@ public:
     if (!HasAny(Flags::State)) {
       mState = aState.ServoValue();
       mContains |= Flags::State;
     }
   }
 
   /**
    * Captures the given element attributes (if not previously captured).
+   *
+   * The attribute name and namespace are used to note which kind of attribute
+   * has changed.
    */
-  void AddAttrs(Element* aElement);
+  void AddAttrs(Element* aElement,
+                int32_t aNameSpaceID,
+                nsIAtom* aChangedAttribute);
 
   /**
    * Captures some other pseudo-class matching state not included in
    * EventStates.
    */
   void AddOtherPseudoClassState(Element* aElement);
 
   /**
@@ -170,13 +175,16 @@ private:
   nsTArray<ServoAttrSnapshot> mAttrs;
   ServoStateType mState;
   Flags mContains;
   bool mIsHTMLElementInHTMLDocument : 1;
   bool mIsInChromeDocument : 1;
   bool mSupportsLangAttr : 1;
   bool mIsTableBorderNonzero : 1;
   bool mIsMozBrowserFrame : 1;
+  bool mClassAttributeChanged : 1;
+  bool mIdAttributeChanged : 1;
+  bool mOtherAttributeChanged : 1;
 };
 
 } // namespace mozilla
 
 #endif
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -2382,16 +2382,23 @@ impl Document {
         // FIXME(emilio): This should become something like
         // element.is_attribute_mapped(attr.local_name()).
         if attr.local_name() == &local_name!("width") ||
            attr.local_name() == &local_name!("height") {
             entry.hint.insert(RestyleHint::for_self());
         }
 
         let mut snapshot = entry.snapshot.as_mut().unwrap();
+        if attr.local_name() == &local_name!("id") {
+            snapshot.id_changed = true;
+        } else if attr.local_name() == &local_name!("class") {
+            snapshot.class_changed = true;
+        } else {
+            snapshot.other_attributes_changed = true;
+        }
         if snapshot.attrs.is_none() {
             let attrs = el.attrs()
                           .iter()
                           .map(|attr| (attr.identifier().clone(), attr.value().clone()))
                           .collect();
             snapshot.attrs = Some(attrs);
         }
     }
--- a/servo/components/style/gecko/snapshot.rs
+++ b/servo/components/style/gecko/snapshot.rs
@@ -57,16 +57,35 @@ impl GeckoElementSnapshot {
 
     /// Returns true if the snapshot has stored state for pseudo-classes
     /// that depend on things other than `ElementState`.
     #[inline]
     pub fn has_other_pseudo_class_state(&self) -> bool {
         self.has_any(Flags::OtherPseudoClassState)
     }
 
+    /// Returns true if the snapshot recorded an id change.
+    #[inline]
+    pub fn id_changed(&self) -> bool {
+        self.mIdAttributeChanged()
+    }
+
+    /// Returns true if the snapshot recorded a class attribute change.
+    #[inline]
+    pub fn class_changed(&self) -> bool {
+        self.mClassAttributeChanged()
+    }
+
+    /// Returns true if the snapshot recorded an attribute change which isn't a
+    /// class or id change.
+    #[inline]
+    pub fn other_attr_changed(&self) -> bool {
+        self.mOtherAttributeChanged()
+    }
+
     /// selectors::Element::attr_matches
     pub fn attr_matches(&self,
                         ns: &NamespaceConstraint<&Namespace>,
                         local_name: &Atom,
                         operation: &AttrSelectorOperation<&Atom>)
                         -> bool {
         unsafe {
             match *operation {
--- a/servo/components/style/servo/selector_parser.rs
+++ b/servo/components/style/servo/selector_parser.rs
@@ -540,28 +540,52 @@ impl DerefMut for SnapshotMap {
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct ServoElementSnapshot {
     /// The stored state of the element.
     pub state: Option<ElementState>,
     /// The set of stored attributes and its values.
     pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>,
     /// Whether this element is an HTML element in an HTML document.
     pub is_html_element_in_html_document: bool,
+    /// Whether the class attribute changed or not.
+    pub class_changed: bool,
+    /// Whether the id attribute changed or not.
+    pub id_changed: bool,
+    /// Whether other attributes other than id or class changed or not.
+    pub other_attributes_changed: bool,
 }
 
 impl ServoElementSnapshot {
     /// Create an empty element snapshot.
     pub fn new(is_html_element_in_html_document: bool) -> Self {
         ServoElementSnapshot {
             state: None,
             attrs: None,
             is_html_element_in_html_document: is_html_element_in_html_document,
+            class_changed: false,
+            id_changed: false,
+            other_attributes_changed: false,
         }
     }
 
+    /// Returns whether the id attribute changed or not.
+    pub fn id_changed(&self) -> bool {
+        self.id_changed
+    }
+
+    /// Returns whether the class attribute changed or not.
+    pub fn class_changed(&self) -> bool {
+        self.class_changed
+    }
+
+    /// Returns whether other attributes other than id or class changed or not.
+    pub fn other_attr_changed(&self) -> bool {
+        self.other_attributes_changed
+    }
+
     fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
         self.attrs.as_ref().unwrap().iter()
             .find(|&&(ref ident, _)| ident.local_name == *name &&
                                      ident.namespace == *namespace)
             .map(|&(_, ref v)| v)
     }
 
     fn any_attr_ignore_ns<F>(&self, name: &LocalName, mut f: F) -> bool