Bug 1203766 - Part 6: Cache resolved style contexts on nsComputedDOMStyle to avoid re-resolving if styles haven't changed. r=bzbarsky
authorCameron McCormack <cam@mcc.id.au>
Thu, 17 Sep 2015 12:08:20 +1000
changeset 295559 b66ac6fdf07c544472200ced740a6ffa157aa445
parent 295558 b8537b5ef9485ba43675f7c23d2342fc1d0b4b31
child 295560 856b829f6b0ad0edb85b1ff3ebbb1cb9cb006f20
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1203766
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1203766 - Part 6: Cache resolved style contexts on nsComputedDOMStyle to avoid re-resolving if styles haven't changed. r=bzbarsky
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -217,17 +217,19 @@ nsComputedStyleMap::Update()
 
 nsComputedDOMStyle::nsComputedDOMStyle(dom::Element* aElement,
                                        const nsAString& aPseudoElt,
                                        nsIPresShell* aPresShell,
                                        StyleType aStyleType)
   : mDocumentWeak(nullptr), mOuterFrame(nullptr),
     mInnerFrame(nullptr), mPresShell(nullptr),
     mStyleType(aStyleType),
-    mExposeVisitedStyle(false)
+    mStyleContextGeneration(0),
+    mExposeVisitedStyle(false),
+    mResolvedStyleContext(false)
 {
   MOZ_ASSERT(aElement && aPresShell);
 
   mDocumentWeak = do_GetWeakReference(aPresShell->GetDocument());
 
   mContent = aElement;
 
   if (!DOMStringIsNull(aPseudoElt) && !aPseudoElt.IsEmpty() &&
@@ -567,20 +569,20 @@ nsComputedDOMStyle::GetCSSParsingEnviron
   NS_RUNTIMEABORT("called nsComputedDOMStyle::GetCSSParsingEnvironment");
   // Just in case NS_RUNTIMEABORT ever stops killing us for some reason
   aCSSParseEnv.mPrincipal = nullptr;
 }
 
 void
 nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush)
 {
-  MOZ_ASSERT(!mStyleContext);
-
   nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocumentWeak);
   if (!document) {
+    mResolvedStyleContext = false;
+    mStyleContext = nullptr;
     return;
   }
 
   document->FlushPendingLinkUpdates();
 
   // Flush _before_ getting the presshell, since that could create a new
   // presshell.  Also note that we want to flush the style on the document
   // we're computing style in, not on the document mContent is in -- the two
@@ -588,19 +590,34 @@ nsComputedDOMStyle::UpdateCurrentStyleSo
   document->FlushPendingNotifications(
     aNeedsLayoutFlush ? Flush_Layout : Flush_Style);
 #ifdef DEBUG
   mFlushedPendingReflows = aNeedsLayoutFlush;
 #endif
 
   mPresShell = document->GetShell();
   if (!mPresShell || !mPresShell->GetPresContext()) {
+    mResolvedStyleContext = false;
+    mStyleContext = nullptr;
     return;
   }
 
+  uint64_t currentGeneration =
+    mPresShell->GetPresContext()->GetRestyleGeneration();
+
+  if (mStyleContext) {
+    if (mStyleContextGeneration == currentGeneration) {
+      // Our cached style context is still valid.
+      return;
+    }
+    // We've processed some restyles, so the cached style context might
+    // be out of date.
+    mStyleContext = nullptr;
+  }
+
   // XXX the !mContent->IsHTMLElement(nsGkAtoms::area)
   // check is needed due to bug 135040 (to avoid using
   // mPrimaryFrame). Remove it once that's fixed.
   if (!mPseudo && mStyleType == eAll &&
       !mContent->IsHTMLElement(nsGkAtoms::area)) {
     mOuterFrame = mContent->GetPrimaryFrame();
     mInnerFrame = mOuterFrame;
     if (mOuterFrame) {
@@ -611,16 +628,17 @@ nsComputedDOMStyle::UpdateCurrentStyleSo
         mInnerFrame = mOuterFrame->GetFirstPrincipalChild();
         NS_ASSERTION(mInnerFrame, "Outer table must have an inner");
         NS_ASSERTION(!mInnerFrame->GetNextSibling(),
                      "Outer table frames should have just one child, "
                      "the inner table");
       }
 
       mStyleContext = mInnerFrame->StyleContext();
+      mResolvedStyleContext = false;
       NS_ASSERTION(mStyleContext, "Frame without style context?");
     }
   }
 
   if (!mStyleContext || mStyleContext->HasPseudoElementData()) {
 #ifdef DEBUG
     if (mStyleContext) {
       // We want to check that going through this path because of
@@ -644,19 +662,29 @@ nsComputedDOMStyle::UpdateCurrentStyleSo
 #endif
     // Need to resolve a style context
     mStyleContext =
       nsComputedDOMStyle::GetStyleContextForElement(mContent->AsElement(),
                                                     mPseudo,
                                                     mPresShell,
                                                     mStyleType);
     if (!mStyleContext) {
+      mResolvedStyleContext = false;
       return;
     }
 
+    // No need to re-get the generation, even though GetStyleContextForElement
+    // will flush, since we flushed style at the top of this function.
+    NS_ASSERTION(mPresShell &&
+                 currentGeneration ==
+                   mPresShell->GetPresContext()->GetRestyleGeneration(),
+                 "why should we have flushed style again?");
+
+    mResolvedStyleContext = true;
+    mStyleContextGeneration = currentGeneration;
     NS_ASSERTION(mPseudo || !mStyleContext->HasPseudoElementData(),
                  "should not have pseudo-element data");
   }
 
   // mExposeVisitedStyle is set to true only by testing APIs that
   // require chrome privilege.
   MOZ_ASSERT(!mExposeVisitedStyle || nsContentUtils::IsCallerChrome(),
              "mExposeVisitedStyle set incorrectly");
@@ -670,19 +698,22 @@ nsComputedDOMStyle::UpdateCurrentStyleSo
 
 void
 nsComputedDOMStyle::ClearCurrentStyleSources()
 {
   mOuterFrame = nullptr;
   mInnerFrame = nullptr;
   mPresShell = nullptr;
 
-  // Release the current style context for it should be re-resolved
-  // whenever a frame is not available.
-  mStyleContext = nullptr;
+  // Release the current style context if we got it off the frame.
+  // For a style context we resolved, keep it around so that we
+  // can re-use it next time this object is queried.
+  if (!mResolvedStyleContext) {
+    mStyleContext = nullptr;
+  }
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::GetPropertyCSSValue(const nsAString& aPropertyName, ErrorResult& aRv)
 {
   nsCSSProperty prop = nsCSSProps::LookupProperty(aPropertyName,
                                                   nsCSSProps::eEnabledForAllContent);
 
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* DOM object returned from element.getComputedStyle() */
 
 #ifndef nsComputedDOMStyle_h__
 #define nsComputedDOMStyle_h__
 
 #include "nsAutoPtr.h"
+#include "mozilla/ArenaRefPtr.h"
+#include "mozilla/ArenaRefPtrInlines.h"
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
 #include "nscore.h"
 #include "nsCSSProps.h"
 #include "nsDOMCSSDeclaration.h"
 #include "nsStyleContext.h"
 #include "nsIWeakReferenceUtils.h"
 #include "mozilla/gfx/Types.h"
@@ -590,22 +592,33 @@ private:
   static nsComputedStyleMap* GetComputedStyleMap();
 
   // We don't really have a good immutable representation of "presentation".
   // Given the way GetComputedStyle is currently used, we should just grab the
   // 0th presshell, if any, from the document.
   nsWeakPtr mDocumentWeak;
   nsCOMPtr<nsIContent> mContent;
 
-  /*
-   * Strong reference to the style context while we're accessing the data from
-   * it.  This can be either a style context we resolved ourselves or a style
-   * context we got from our frame.
+  /**
+   * Strong reference to the style context we access data from.  This can be
+   * either a style context we resolved ourselves or a style context we got
+   * from our frame.
+   *
+   * If we got the style context from the frame, we clear out mStyleContext
+   * in ClearCurrentStyleSources.  If we resolved one ourselves, then
+   * ClearCurrentStyleSources leaves it in mStyleContext for use the next
+   * time this nsComputedDOMStyle object is queried.  UpdateCurrentStyleSources
+   * in this case will check that the style context is still valid to be used,
+   * by checking whether flush styles results in any restyles having been
+   * processed.
+   *
+   * Since an ArenaRefPtr is used to hold the style context, it will be cleared
+   * if the pres arena from which it was allocated goes away.
    */
-  nsRefPtr<nsStyleContext> mStyleContext;
+  mozilla::ArenaRefPtr<nsStyleContext> mStyleContext;
   nsCOMPtr<nsIAtom> mPseudo;
 
   /*
    * While computing style data, the primary frame for mContent --- named "outer"
    * because we should use it to compute positioning data.  Null
    * otherwise.
    */
   nsIFrame* mOuterFrame;
@@ -621,18 +634,30 @@ private:
    */
   nsIPresShell* mPresShell;
 
   /*
    * The kind of styles we should be returning.
    */
   StyleType mStyleType;
 
+  /**
+   * The nsComputedDOMStyle generation at the time we last resolved a style
+   * context and stored it in mStyleContext.
+   */
+  uint64_t mStyleContextGeneration;
+
   bool mExposeVisitedStyle;
 
+  /**
+   * Whether we resolved a style context last time we called
+   * UpdateCurrentStyleSources.  Initially false.
+   */
+  bool mResolvedStyleContext;
+
 #ifdef DEBUG
   bool mFlushedPendingReflows;
 #endif
 };
 
 already_AddRefed<nsComputedDOMStyle>
 NS_NewComputedDOMStyle(mozilla::dom::Element* aElement,
                        const nsAString& aPseudoElt,