Don't unsuppress painting until we've known the website background, to prevent flashing. draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Mon, 22 Mar 2021 17:48:29 +0100
changeset 3620184 0101da6686798ae54dc44678acc91df3964a3b2a
parent 3619876 729eaf579f2827239182db122d59e42730ea540f
child 3620185 ec7934a39939a6c64ae29889fadef3fef8914238
push id671436
push useremilio@crisal.io
push dateMon, 22 Mar 2021 17:03:42 +0000
treeherdertry@ec7934a39939 [default view] [failures only]
milestone88.0a1
Don't unsuppress painting until we've known the website background, to prevent flashing.
layout/base/PresShell.cpp
layout/base/PresShell.h
modules/libpref/init/StaticPrefList.yaml
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -1812,16 +1812,36 @@ void PresShell::EndObservingDocument() {
   mIsDocumentGone = true;
   mIsObservingDocument = false;
 }
 
 #ifdef DEBUG_kipp
 char* nsPresShell_ReflowStackPointerTop;
 #endif
 
+void PresShell::InitPaintSuppressionTimer() {
+  // Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value.
+  Document* doc = mDocument->GetDisplayDocument()
+                      ? mDocument->GetDisplayDocument()
+                      : mDocument.get();
+  int32_t delay = Preferences::GetInt(
+      !doc->GetBrowsingContext() ||
+              doc->GetBrowsingContext()->Top()->IsInProcess()
+          ? "nglayout.initialpaint.delay"
+          : "nglayout.initialpaint.delay_in_oopif",
+      PAINTLOCK_EVENT_DELAY);
+  mPaintSuppressionTimer->InitWithNamedFuncCallback(
+      [](nsITimer* aTimer, void* aPresShell) {
+        RefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
+        self->UnsuppressPaintingFromTimer();
+      },
+      this, delay, nsITimer::TYPE_ONE_SHOT,
+      "PresShell::sPaintSuppressionCallback");
+}
+
 nsresult PresShell::Initialize() {
   if (mIsDestroying) {
     return NS_OK;
   }
 
   if (!mDocument) {
     // Nothing to do
     return NS_OK;
@@ -1927,50 +1947,31 @@ nsresult PresShell::Initialize() {
     Document::ReadyState readyState = mDocument->GetReadyStateEnum();
     if (readyState != Document::READYSTATE_COMPLETE) {
       mPaintSuppressionTimer = NS_NewTimer();
     }
     if (!mPaintSuppressionTimer) {
       mPaintingSuppressed = false;
     } else {
       // Initialize the timer.
-
-      // Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value.
-      Document* doc = mDocument->GetDisplayDocument()
-                          ? mDocument->GetDisplayDocument()
-                          : mDocument.get();
-      int32_t delay = Preferences::GetInt(
-          !doc->GetBrowsingContext() ||
-                  doc->GetBrowsingContext()->Top()->IsInProcess()
-              ? "nglayout.initialpaint.delay"
-              : "nglayout.initialpaint.delay_in_oopif",
-          PAINTLOCK_EVENT_DELAY);
-
       mPaintSuppressionTimer->SetTarget(
           mDocument->EventTargetFor(TaskCategory::Other));
-      mPaintSuppressionTimer->InitWithNamedFuncCallback(
-          sPaintSuppressionCallback, this, delay, nsITimer::TYPE_ONE_SHOT,
-          "PresShell::sPaintSuppressionCallback");
+      InitPaintSuppressionTimer();
     }
   }
 
   // If we get here and painting is not suppressed, we still want to run the
   // unsuppression logic, so set mShouldUnsuppressPainting to true.
   if (!mPaintingSuppressed) {
     mShouldUnsuppressPainting = true;
   }
 
   return NS_OK;  // XXX this needs to be real. MMP
 }
 
-void PresShell::sPaintSuppressionCallback(nsITimer* aTimer, void* aPresShell) {
-  RefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
-  if (self) self->UnsuppressPainting();
-}
-
 nsresult PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight,
                                  ResizeReflowOptions aOptions) {
   if (mZoomConstraintsClient) {
     // If we have a ZoomConstraintsClient and the available screen area
     // changed, then we might need to disable double-tap-to-zoom, so notify
     // the ZCC to update itself.
     mZoomConstraintsClient->ScreenSizeChanged();
   }
@@ -3904,23 +3905,46 @@ void PresShell::UnsuppressAndInvalidate(
   if (nsPIDOMWindowOuter* win = mDocument->GetWindow()) win->SetReadyForFocus();
 
   if (!mHaveShutDown) {
     SynthesizeMouseMove(false);
     ScheduleApproximateFrameVisibilityUpdateNow();
   }
 }
 
+void PresShell::UnsuppressPaintingFromTimer() {
+  if (mPaintSuppressionTimer) {
+    mPaintSuppressionTimer->Cancel();
+    mPaintSuppressionTimer = nullptr;
+  }
+  if (mIsDocumentGone || !mPaintingSuppressed) {
+    return;
+  }
+  if (!StaticPrefs::layout_unsuppress_painting_with_no_background()) {
+    if (mPresContext->IsRootContentDocumentCrossProcess()) {
+      UpdateCanvasBackground();
+      if (!mHasCSSBackgroundColor) {
+        // We don't unsuppress painting if the page has a transparent background,
+        // as that usually means that the page is unstyled.
+        return;
+      }
+    }
+  }
+  UnsuppressPainting();
+}
+
 void PresShell::UnsuppressPainting() {
   if (mPaintSuppressionTimer) {
     mPaintSuppressionTimer->Cancel();
     mPaintSuppressionTimer = nullptr;
   }
 
-  if (mIsDocumentGone || !mPaintingSuppressed) return;
+  if (mIsDocumentGone || !mPaintingSuppressed) {
+    return;
+  }
 
   // If we have reflows pending, just wait until we process
   // the reflows and get all the frames where we want them
   // before actually unlocking the painting.  Otherwise
   // go ahead and unlock now.
   if (!mDirtyRoots.IsEmpty())
     mShouldUnsuppressPainting = true;
   else
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -676,20 +676,19 @@ class PresShell final : public nsStubDoc
 
   /**
    * Called to find out if painting is suppressed for this presshell.  If it is
    * suppressd, we don't allow the painting of any layer but the background, and
    * we don't recur into our children.
    */
   bool IsPaintingSuppressed() const { return mPaintingSuppressed; }
 
-  /**
-   * Unsuppress painting.
-   */
   void UnsuppressPainting();
+  void UnsuppressPaintingFromTimer();
+  void InitPaintSuppressionTimer();
 
   /**
    * Reconstruct frames for all elements in the document
    */
   MOZ_CAN_RUN_SCRIPT void ReconstructFrames();
 
   /**
    * See if reflow verification is enabled. To enable reflow verification add
@@ -2717,19 +2716,16 @@ class PresShell final : public nsStubDoc
     static TimeStamp sLastInputProcessed;
     static StaticRefPtr<dom::Element> sLastKeyDownEventTargetElement;
   };
 
   PresShell* GetRootPresShell() const;
 
   nscolor GetDefaultBackgroundColorToDraw();
 
-  // The callback for the mPaintSuppressionTimer timer.
-  static void sPaintSuppressionCallback(nsITimer* aTimer, void* aPresShell);
-
   //////////////////////////////////////////////////////////////////////////////
   // Approximate frame visibility tracking implementation.
   //////////////////////////////////////////////////////////////////////////////
 
   void UpdateApproximateFrameVisibility();
   void DoUpdateApproximateFrameVisibility(bool aRemoveOnly);
 
   void ClearApproximatelyVisibleFramesList(
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -6826,16 +6826,23 @@
   value: 64
   mirror: always
 
 - name: layout.paint_rects_separately
   type: bool
   value: true
   mirror: once
 
+# Whether we should unsuppress painting from a timer when there's no CSS
+# background specified on the page.
+- name: layout.unsuppress_painting_with_no_background
+  type: bool
+  value: false
+  mirror: always
+
 # On Android, don't synth mouse move events after scrolling, as they cause
 # unexpected user-visible behaviour. Can remove this after bug 1633450 is
 # satisfactorily resolved.
 - name: layout.reflow.synthMouseMove
   type: bool
   value: @IS_NOT_ANDROID@
   mirror: always