Bug 1358495 - Part 2: Implement window.getComputedStyleWithoutFlushing. r?heycam draft
authorWei-Cheng Pan <wpan@mozilla.com>
Fri, 28 Apr 2017 19:01:58 +0800
changeset 570141 39c4d9cdb6c00be72c1ad7d997ff7b2d15c57c27
parent 570140 9ff680041df726a3bd6025ac3d55862a4587e094
child 626411 3ef0696010ba9a52d42889fd87ff784a16076b03
push id56403
push userbmo:wpan@mozilla.com
push dateFri, 28 Apr 2017 11:33:40 +0000
reviewersheycam
bugs1358495
milestone55.0a1
Bug 1358495 - Part 2: Implement window.getComputedStyleWithoutFlushing. r?heycam Added FlushingFlag into mozilla::layout instead of nsComputedDOMStyle, because including nsComputedDOMStyle.h in nsGlobalWindow.h will cause problems in unified build. MozReview-Commit-ID: 9FsyZxO7jWv
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsPIDOMWindow.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -280,16 +280,17 @@ static const char kStorageEnabled[] = "d
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 using mozilla::BasePrincipal;
 using mozilla::OriginAttributes;
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 using mozilla::dom::cache::CacheStorage;
+using mozilla::layout::FlushingFlag;
 
 static LazyLogModule gDOMLeakPRLog("DOMLeak");
 
 nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nullptr;
 bool nsGlobalWindow::sWarnedAboutWindowInternal = false;
 bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false;
 
 static int32_t              gRefCnt                    = 0;
@@ -10988,54 +10989,71 @@ nsGlobalWindow::UpdateCanvasFocus(bool a
 }
 
 already_AddRefed<nsICSSDeclaration>
 nsGlobalWindow::GetComputedStyle(Element& aElt, const nsAString& aPseudoElt,
                                  ErrorResult& aError)
 {
   MOZ_ASSERT(IsInnerWindow());
   FORWARD_TO_OUTER_OR_THROW(GetComputedStyleOuter,
-                            (aElt, aPseudoElt), aError, nullptr);
+                            (aElt, aPseudoElt,
+                             FlushingFlag::eNormalFlushing), aError, nullptr);
+}
+
+already_AddRefed<nsICSSDeclaration>
+nsGlobalWindow::GetComputedStyleWithoutFlushing(Element& aElt,
+                                                const nsAString& aPseudoElt,
+                                                ErrorResult& aError)
+{
+  MOZ_ASSERT(IsInnerWindow());
+  FORWARD_TO_OUTER_OR_THROW(GetComputedStyleOuter,
+                            (aElt, aPseudoElt,
+                             FlushingFlag::eWithoutFlushing), aError, nullptr);
 }
 
 already_AddRefed<nsICSSDeclaration>
 nsGlobalWindow::GetComputedStyleOuter(Element& aElt,
-                                      const nsAString& aPseudoElt)
+                                      const nsAString& aPseudoElt,
+                                      FlushingFlag aFlushingFlag)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
 
   if (!mDocShell) {
     return nullptr;
   }
 
   nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
 
   if (!presShell) {
-    // Try flushing frames on our parent in case there's a pending
-    // style change that will create the presshell.
-    auto* parent = nsGlobalWindow::Cast(GetPrivateParent());
-    if (!parent) {
-      return nullptr;
-    }
-
-    parent->FlushPendingNotifications(FlushType::Frames);
+    if (aFlushingFlag == FlushingFlag::eNormalFlushing) {
+      // Try flushing frames on our parent in case there's a pending
+      // style change that will create the presshell.
+      auto* parent = nsGlobalWindow::Cast(GetPrivateParent());
+      if (!parent) {
+        return nullptr;
+      }
+
+      parent->FlushPendingNotifications(FlushType::Frames);
+    }
 
     // Might have killed mDocShell
     if (!mDocShell) {
       return nullptr;
     }
 
     presShell = mDocShell->GetPresShell();
     if (!presShell) {
       return nullptr;
     }
   }
 
   RefPtr<nsComputedDOMStyle> compStyle =
-    NS_NewComputedDOMStyle(&aElt, aPseudoElt, presShell);
+    NS_NewComputedDOMStyle(&aElt, aPseudoElt, presShell,
+                           nsComputedDOMStyle::eWithAnimation,
+                           aFlushingFlag);
 
   return compStyle.forget();
 }
 
 Storage*
 nsGlobalWindow::GetSessionStorage(ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsInnerWindow());
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -147,16 +147,19 @@ class WakeLock;
 class WindowOrientationObserver;
 #endif
 class Worklet;
 namespace cache {
 class CacheStorage;
 } // namespace cache
 class IDBFactory;
 } // namespace dom
+namespace layout {
+enum class FlushingFlag : uint8_t;
+} // namespace layout
 } // namespace mozilla
 
 extern already_AddRefed<nsIScriptTimeoutHandler>
 NS_CreateJSTimeoutHandler(JSContext* aCx, nsGlobalWindow *aWindow,
                           mozilla::dom::Function& aFunction,
                           const mozilla::dom::Sequence<JS::Value>& aArguments,
                           mozilla::ErrorResult& aError);
 
@@ -1040,16 +1043,20 @@ public:
   GetLocalStorage(mozilla::ErrorResult& aError);
   mozilla::dom::Selection* GetSelectionOuter();
   mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aError);
   already_AddRefed<nsISelection> GetSelection() override;
   mozilla::dom::IDBFactory* GetIndexedDB(mozilla::ErrorResult& aError);
   already_AddRefed<nsICSSDeclaration>
     GetComputedStyle(mozilla::dom::Element& aElt, const nsAString& aPseudoElt,
                      mozilla::ErrorResult& aError) override;
+  already_AddRefed<nsICSSDeclaration>
+  GetComputedStyleWithoutFlushing(mozilla::dom::Element& aElt,
+                                  const nsAString& aPseudoElt,
+                                  mozilla::ErrorResult& aError) override;
   already_AddRefed<mozilla::dom::MediaQueryList> MatchMediaOuter(const nsAString& aQuery);
   already_AddRefed<mozilla::dom::MediaQueryList> MatchMedia(const nsAString& aQuery,
                                                             mozilla::ErrorResult& aError);
   nsScreen* GetScreen(mozilla::ErrorResult& aError);
   nsIDOMScreen* GetScreen() override;
   void MoveToOuter(int32_t aXPos, int32_t aYPos,
                    mozilla::dom::CallerType aCallerType,
                    mozilla::ErrorResult& aError);
@@ -1724,17 +1731,18 @@ protected:
 
 public:
   // Outer windows only.
   nsDOMWindowList* GetWindowList();
 
 protected:
   already_AddRefed<nsICSSDeclaration>
     GetComputedStyleOuter(mozilla::dom::Element& aElt,
-                          const nsAString& aPseudoElt);
+                          const nsAString& aPseudoElt,
+                          mozilla::layout::FlushingFlag aFlushingFlag);
 
   // Outer windows only.
   void PreloadLocalStorage();
 
   // Returns CSS pixels based on primary screen.  Outer windows only.
   mozilla::CSSIntPoint GetScreenXY(mozilla::dom::CallerType aCallerType,
                                    mozilla::ErrorResult& aError);
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -589,16 +589,20 @@ public:
                               nsISupports* aExtraArgument,
                               nsPIDOMWindowOuter** _retval) = 0;
 
   virtual nsresult GetInnerWidth(int32_t* aWidth) = 0;
   virtual nsresult GetInnerHeight(int32_t* aHeight) = 0;
   virtual already_AddRefed<nsICSSDeclaration>
     GetComputedStyle(mozilla::dom::Element& aElt, const nsAString& aPseudoElt,
                      mozilla::ErrorResult& aError) = 0;
+  virtual already_AddRefed<nsICSSDeclaration>
+  GetComputedStyleWithoutFlushing(mozilla::dom::Element& aElt,
+                                  const nsAString& aPseudoElt,
+                                  mozilla::ErrorResult& aError) = 0;
   virtual already_AddRefed<nsIDOMElement> GetFrameElement() = 0;
   virtual already_AddRefed<nsIDOMOfflineResourceList> GetApplicationCache() = 0;
   virtual bool Closed() = 0;
   virtual bool GetFullScreen() = 0;
   virtual nsresult SetFullScreen(bool aFullScreen) = 0;
 
   virtual nsresult Focus() = 0;
   virtual nsresult Close() = 0;
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -62,21 +62,22 @@ using namespace mozilla::dom;
 /*
  * This is the implementation of the readonly CSSStyleDeclaration that is
  * returned by the getComputedStyle() function.
  */
 
 already_AddRefed<nsComputedDOMStyle>
 NS_NewComputedDOMStyle(dom::Element* aElement, const nsAString& aPseudoElt,
                        nsIPresShell* aPresShell,
-                       nsComputedDOMStyle::AnimationFlag aFlag)
+                       nsComputedDOMStyle::AnimationFlag aFlag,
+                       mozilla::layout::FlushingFlag aFlushingFlag)
 {
   RefPtr<nsComputedDOMStyle> computedStyle;
   computedStyle = new nsComputedDOMStyle(aElement, aPseudoElt,
-                                         aPresShell, aFlag);
+                                         aPresShell, aFlag, aFlushingFlag);
   return computedStyle.forget();
 }
 
 static nsDOMCSSValueList*
 GetROCSSValueList(bool aCommaDelimited)
 {
   return new nsDOMCSSValueList(aCommaDelimited, true);
 }
@@ -240,25 +241,27 @@ nsComputedStyleMap::Update()
     }
   }
   mExposedPropertyCount = index;
 }
 
 nsComputedDOMStyle::nsComputedDOMStyle(dom::Element* aElement,
                                        const nsAString& aPseudoElt,
                                        nsIPresShell* aPresShell,
-                                       AnimationFlag aFlag)
+                                       AnimationFlag aFlag,
+                                       FlushingFlag aFlushingFlag)
   : mDocumentWeak(nullptr)
   , mOuterFrame(nullptr)
   , mInnerFrame(nullptr)
   , mPresShell(nullptr)
   , mStyleContextGeneration(0)
   , mExposeVisitedStyle(false)
   , mResolvedStyleContext(false)
   , mAnimationFlag(aFlag)
+  , mFlushingFlag(aFlushingFlag)
 {
   MOZ_ASSERT(aElement && aPresShell);
 
   mDocumentWeak = do_GetWeakReference(aPresShell->GetDocument());
   mContent = aElement;
   mPseudo = nsCSSPseudoElements::GetPseudoAtom(aPseudoElt);
 
   MOZ_ASSERT(aPresShell->GetPresContext());
@@ -753,27 +756,30 @@ void
 nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush)
 {
   nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocumentWeak);
   if (!document) {
     ClearStyleContext();
     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
-  // may be different.
-  document->FlushPendingNotifications(
-    aNeedsLayoutFlush ? FlushType::Layout : FlushType::Style);
+  AssertFlushedPendingReflows();
+  if (mFlushingFlag == FlushingFlag::eNormalFlushing) {
+    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
+    // may be different.
+    document->FlushPendingNotifications(
+      aNeedsLayoutFlush ? FlushType::Layout : FlushType::Style);
 #ifdef DEBUG
-  mFlushedPendingReflows = aNeedsLayoutFlush;
+    mFlushedPendingReflows = aNeedsLayoutFlush;
 #endif
+  }
 
   mPresShell = document->GetShell();
   if (!mPresShell || !mPresShell->GetPresContext()) {
     ClearStyleContext();
     return;
   }
 
   uint64_t currentGeneration =
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -41,24 +41,34 @@ class nsROCSSPrimitiveValue;
 class nsStyleCoord;
 class nsStyleCorners;
 struct nsStyleFilter;
 class nsStyleGradient;
 struct nsStyleImage;
 class nsStyleSides;
 struct nsTimingFunction;
 
+namespace mozilla {
+namespace layout {
+enum class FlushingFlag : uint8_t {
+  eNormalFlushing,
+  eWithoutFlushing,
+};
+} // namespace layout
+} // namespace mozilla
+
 class nsComputedDOMStyle final : public nsDOMCSSDeclaration
                                , public nsStubMutationObserver
 {
 private:
   // Convenience typedefs:
   typedef nsCSSProps::KTableEntry KTableEntry;
   typedef mozilla::dom::CSSValue CSSValue;
   typedef mozilla::StyleGeometryBox StyleGeometryBox;
+  typedef mozilla::layout::FlushingFlag FlushingFlag;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsComputedDOMStyle,
                                                                    nsICSSDeclaration)
 
   NS_DECL_NSICSSDECLARATION
 
@@ -72,17 +82,18 @@ public:
   enum AnimationFlag {
     eWithAnimation,
     eWithoutAnimation,
   };
 
   nsComputedDOMStyle(mozilla::dom::Element* aElement,
                      const nsAString& aPseudoElt,
                      nsIPresShell* aPresShell,
-                     AnimationFlag aFlag = eWithAnimation);
+                     AnimationFlag aFlag = eWithAnimation,
+                     FlushingFlag aFlushingFlag = FlushingFlag::eNormalFlushing);
 
   virtual nsINode *GetParentObject() override
   {
     return mContent;
   }
 
   static already_AddRefed<nsStyleContext>
   GetStyleContext(mozilla::dom::Element* aElement, nsIAtom* aPseudo,
@@ -748,21 +759,28 @@ private:
    */
   bool mResolvedStyleContext;
 
   /**
    * Whether we include animation rules in the computed style.
    */
   AnimationFlag mAnimationFlag;
 
+  /**
+   * Whether we should ignore style flushing while getting properties.
+   */
+  FlushingFlag mFlushingFlag;
+
 #ifdef DEBUG
   bool mFlushedPendingReflows;
 #endif
 };
 
 already_AddRefed<nsComputedDOMStyle>
 NS_NewComputedDOMStyle(mozilla::dom::Element* aElement,
                        const nsAString& aPseudoElt,
                        nsIPresShell* aPresShell,
                        nsComputedDOMStyle::AnimationFlag aFlag =
-                         nsComputedDOMStyle::eWithAnimation);
+                         nsComputedDOMStyle::eWithAnimation,
+                       mozilla::layout::FlushingFlag aFlushingFlag =
+                         mozilla::layout::FlushingFlag::eNormalFlushing);
 
 #endif /* nsComputedDOMStyle_h__ */