Bug 1519861 - Improve mozilla::GetTabSizes - r=smaug
authorTarek Ziadé <tarek@mozilla.com>
Mon, 25 Feb 2019 16:39:46 +0000
changeset 518918 1e169e0e9d17d376d31e491db36530a4ca3e18f9
parent 518917 65870fec6eea2103719af73c7369d5242250ecdb
child 518919 63242317442cedd9c5407b6fe5734c61036cb452
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1519861
milestone67.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 1519861 - Improve mozilla::GetTabSizes - r=smaug Introduces a generation number in dom::base::Document as well as a cached version of TabSizes we can Get and Set. When the document is changed, the cache is invalidated. This change improves about:performance speed by avoiding a heavy recursive call on big DOM trees via GetTabSizes() calls. If the page does not change, the cached values are returned. Differential Revision: https://phabricator.services.mozilla.com/D20526
dom/base/Document.cpp
dom/base/Document.h
dom/base/nsNodeUtils.cpp
toolkit/components/perfmonitoring/PerformanceUtils.cpp
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -1336,17 +1336,19 @@ Document::Document(const char* aContentT
       mBoxObjectTable(nullptr),
       mCurrentOrientationAngle(0),
       mCurrentOrientationType(OrientationType::Portrait_primary),
       mServoRestyleRootDirtyBits(0),
       mThrowOnDynamicMarkupInsertionCounter(0),
       mIgnoreOpensDuringUnloadCounter(0),
       mDocLWTheme(Doc_Theme_Uninitialized),
       mSavedResolution(1.0f),
-      mPendingInitialTranslation(false) {
+      mPendingInitialTranslation(false),
+      mGeneration(0),
+      mCachedTabSizeGeneration(0) {
   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
 
   SetIsInDocument();
   SetIsConnected(true);
 
   if (StaticPrefs::layout_css_use_counters_enabled()) {
     mStyleUseCounters.reset(Servo_UseCounters_Create());
   }
@@ -12544,16 +12546,34 @@ void Document::ReportShadowDOMUsage() {
   mHasReportedShadowDOMUsage = true;
 }
 
 bool Document::StorageAccessSandboxed() const {
   return StaticPrefs::dom_storage_access_enabled() &&
          (GetSandboxFlags() & SANDBOXED_STORAGE_ACCESS) != 0;
 }
 
+bool Document::GetCachedSizes(nsTabSizes* aSizes) {
+  if (mCachedTabSizeGeneration == 0 ||
+      GetGeneration() != mCachedTabSizeGeneration) {
+    return false;
+  }
+  aSizes->mDom += mCachedTabSizes.mDom;
+  aSizes->mStyle += mCachedTabSizes.mStyle;
+  aSizes->mOther += mCachedTabSizes.mOther;
+  return true;
+}
+
+void Document::SetCachedSizes(nsTabSizes* aSizes) {
+  mCachedTabSizes.mDom = aSizes->mDom;
+  mCachedTabSizes.mStyle = aSizes->mStyle;
+  mCachedTabSizes.mOther = aSizes->mOther;
+  mCachedTabSizeGeneration = GetGeneration();
+}
+
 already_AddRefed<nsAtom> Document::GetContentLanguageAsAtomForStyle() const {
   nsAutoString contentLang;
   GetContentLanguage(contentLang);
   contentLang.StripWhitespace();
 
   // Content-Language may be a comma-separated list of language codes,
   // in which case the HTML5 spec says to treat it as unknown
   if (!contentLang.IsEmpty() && !contentLang.Contains(char16_t(','))) {
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -1485,16 +1485,31 @@ class Document : public nsINode,
   // canceled by the URL classifier (Safebrowsing).
   //
   already_AddRefed<nsSimpleContentList> BlockedNodesByClassifier() const;
 
   // Helper method that returns true if the document has storage-access sandbox
   // flag.
   bool StorageAccessSandboxed() const;
 
+  // Increments the document generation.
+  inline void Changed() { ++mGeneration; }
+
+  // Returns the current generation.
+  inline int32_t GetGeneration() const { return mGeneration; }
+
+  // Adds cached sizes values to aSizes if there's any
+  // cached value and if the document generation hasn't
+  // changed since the cache was created.
+  // Returns true if sizes were added.
+  bool GetCachedSizes(nsTabSizes* aSizes);
+
+  // Sets the cache sizes for the current generation.
+  void SetCachedSizes(nsTabSizes* aSizes);
+
  protected:
   friend class nsUnblockOnloadEvent;
 
   nsresult InitCSP(nsIChannel* aChannel);
 
   nsresult InitFeaturePolicy(nsIChannel* aChannel);
 
   void PostUnblockOnloadEvent();
@@ -4636,16 +4651,23 @@ class Document : public nsINode,
   // :-moz-lwtheme-brighttext and :-moz-lwtheme-darktext
   DocumentTheme mDocLWTheme;
 
   // Pres shell resolution saved before entering fullscreen mode.
   float mSavedResolution;
 
   bool mPendingInitialTranslation;
 
+  // Document generation. Gets incremented everytime it changes.
+  int32_t mGeneration;
+
+  // Cached TabSizes values for the document.
+  int32_t mCachedTabSizeGeneration;
+  nsTabSizes mCachedTabSizes;
+
  public:
   // Needs to be public because the bindings code pokes at it.
   js::ExpandoAndGeneration mExpandoAndGeneration;
 
   bool HasPendingInitialTranslation() { return mPendingInitialTranslation; }
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(Document, NS_IDOCUMENT_IID)
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -131,32 +131,34 @@ void nsNodeUtils::CharacterDataWillChang
   Document* doc = aContent->OwnerDoc();
   IMPL_MUTATION_NOTIFICATION(CharacterDataWillChange, aContent,
                              (aContent, aInfo), IsRemoveNotification::No);
 }
 
 void nsNodeUtils::CharacterDataChanged(nsIContent* aContent,
                                        const CharacterDataChangeInfo& aInfo) {
   Document* doc = aContent->OwnerDoc();
+  doc->Changed();
   IMPL_MUTATION_NOTIFICATION(CharacterDataChanged, aContent, (aContent, aInfo),
                              IsRemoveNotification::No);
 }
 
 void nsNodeUtils::AttributeWillChange(Element* aElement, int32_t aNameSpaceID,
                                       nsAtom* aAttribute, int32_t aModType) {
   Document* doc = aElement->OwnerDoc();
   IMPL_MUTATION_NOTIFICATION(AttributeWillChange, aElement,
                              (aElement, aNameSpaceID, aAttribute, aModType),
                              IsRemoveNotification::No);
 }
 
 void nsNodeUtils::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
                                    nsAtom* aAttribute, int32_t aModType,
                                    const nsAttrValue* aOldValue) {
   Document* doc = aElement->OwnerDoc();
+  doc->Changed();
   IMPL_MUTATION_NOTIFICATION(
       AttributeChanged, aElement,
       (aElement, aNameSpaceID, aAttribute, aModType, aOldValue),
       IsRemoveNotification::No);
 }
 
 void nsNodeUtils::AttributeSetToCurrentValue(Element* aElement,
                                              int32_t aNameSpaceID,
@@ -165,17 +167,17 @@ void nsNodeUtils::AttributeSetToCurrentV
   IMPL_MUTATION_NOTIFICATION(AttributeSetToCurrentValue, aElement,
                              (aElement, aNameSpaceID, aAttribute),
                              IsRemoveNotification::No);
 }
 
 void nsNodeUtils::ContentAppended(nsIContent* aContainer,
                                   nsIContent* aFirstNewContent) {
   Document* doc = aContainer->OwnerDoc();
-
+  doc->Changed();
   IMPL_MUTATION_NOTIFICATION(ContentAppended, aContainer, (aFirstNewContent),
                              IsRemoveNotification::No);
 }
 
 void nsNodeUtils::NativeAnonymousChildListChange(nsIContent* aContent,
                                                  bool aIsRemove) {
   Document* doc = aContent->OwnerDoc();
   auto isRemove =
@@ -183,25 +185,27 @@ void nsNodeUtils::NativeAnonymousChildLi
   IMPL_MUTATION_NOTIFICATION(NativeAnonymousChildListChange, aContent,
                              (aContent, aIsRemove), isRemove);
 }
 
 void nsNodeUtils::ContentInserted(nsINode* aContainer, nsIContent* aChild) {
   MOZ_ASSERT(aContainer->IsContent() || aContainer->IsDocument(),
              "container must be an nsIContent or an Document");
   Document* doc = aContainer->OwnerDoc();
+  doc->Changed();
   IMPL_MUTATION_NOTIFICATION(ContentInserted, aContainer, (aChild),
                              IsRemoveNotification::No);
 }
 
 void nsNodeUtils::ContentRemoved(nsINode* aContainer, nsIContent* aChild,
                                  nsIContent* aPreviousSibling) {
   MOZ_ASSERT(aContainer->IsContent() || aContainer->IsDocument(),
              "container must be an nsIContent or an Document");
   Document* doc = aContainer->OwnerDoc();
+  doc->Changed();
   MOZ_ASSERT(aChild->GetParentNode() == aContainer,
              "We expect the parent link to be still around at this point");
   IMPL_MUTATION_NOTIFICATION(ContentRemoved, aContainer,
                              (aChild, aPreviousSibling),
                              IsRemoveNotification::Yes);
 }
 
 Maybe<NonOwningAnimationTarget> nsNodeUtils::GetTargetForAnimation(
--- a/toolkit/components/perfmonitoring/PerformanceUtils.cpp
+++ b/toolkit/components/perfmonitoring/PerformanceUtils.cpp
@@ -55,32 +55,51 @@ nsTArray<RefPtr<PerformanceInfoPromise>>
     }
     for (DocGroup* docGroup : docGroups) {
       promises.AppendElement(docGroup->ReportPerformanceInfo());
     }
   }
   return promises;
 }
 
-nsresult GetTabSizes(nsGlobalWindowOuter* aWindow, nsTabSizes* aSizes) {
+void AddWindowTabSizes(nsGlobalWindowOuter* aWindow, nsTabSizes* aSizes) {
+  Document* document = aWindow->GetDocument();
+  if (document && document->GetCachedSizes(aSizes)) {
+    // We got a cached version
+    return;
+  }
+  // We measure the sizes on a fresh nsTabSizes instance
+  // because we want to cache the value and aSizes might
+  // already have some values from other windows.
+  nsTabSizes sizes;
+
   // Measure the window.
   SizeOfState state(moz_malloc_size_of);
   nsWindowSizes windowSizes(state);
   aWindow->AddSizeOfIncludingThis(windowSizes);
-
   // Measure the inner window, if there is one.
   nsGlobalWindowInner* inner = aWindow->GetCurrentInnerWindowInternal();
   if (inner != nullptr) {
     inner->AddSizeOfIncludingThis(windowSizes);
   }
+  windowSizes.addToTabSizes(&sizes);
+  if (document) {
+    document->SetCachedSizes(&sizes);
+  }
+  aSizes->mDom += sizes.mDom;
+  aSizes->mStyle += sizes.mStyle;
+  aSizes->mOther += sizes.mOther;
+}
 
-  windowSizes.addToTabSizes(aSizes);
+nsresult GetTabSizes(nsGlobalWindowOuter* aWindow, nsTabSizes* aSizes) {
+  // Add the window (and inner window) sizes. Might be cached.
+  AddWindowTabSizes(aWindow, aSizes);
+
   nsDOMWindowList* frames = aWindow->GetFrames();
   uint32_t length = frames->GetLength();
-
   // Measure this window's descendents.
   for (uint32_t i = 0; i < length; i++) {
     nsCOMPtr<nsPIDOMWindowOuter> child = frames->IndexedGetter(i);
     NS_ENSURE_STATE(child);
     nsGlobalWindowOuter* childWin = nsGlobalWindowOuter::Cast(child);
     nsresult rv = GetTabSizes(childWin, aSizes);
     NS_ENSURE_SUCCESS(rv, rv);
   }