Bug 565388 - No paint for invisible docshell. r=tnikkel
authorGabor Krizsanits <gkrizsanits@mozilla.com>
Wed, 16 Oct 2013 15:18:42 +0200
changeset 165709 9373baf8aec2c029e2be797eaec2b40bc100d026
parent 165708 98fe3c07b042053f02dd65e9723ca8d3abe69f46
child 165710 9027ca2649fcf15315d8cdec0694bbe3976ad39c
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstnikkel
bugs565388
milestone27.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 565388 - No paint for invisible docshell. r=tnikkel
content/base/src/nsDocument.cpp
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIDocShell.idl
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
view/src/nsViewManager.cpp
xpfe/appshell/src/nsAppShellService.cpp
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3473,16 +3473,21 @@ nsDocument::doCreateShell(nsPresContext*
   FillStyleSet(aStyleSet);
 
   nsRefPtr<PresShell> shell = new PresShell;
   shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
 
   // Note: we don't hold a ref to the shell (it holds a ref to us)
   mPresShell = shell;
 
+  // Make sure to never paint if we belong to an invisible DocShell.
+  nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocumentContainer);
+  if (docShell && docShell->IsInvisible())
+    shell->SetNeverPainting(true);
+
   mExternalResourceMap.ShowViewers();
 
   MaybeRescheduleAnimationFrameNotifications();
 
   return shell.forget();
 }
 
 void
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -760,16 +760,17 @@ nsDocShell::nsDocShell():
     mIsBeingDestroyed(false),
     mIsExecutingOnLoadHandler(false),
     mIsPrintingOrPP(false),
     mSavingOldViewer(false),
 #ifdef DEBUG
     mInEnsureScriptEnv(false),
 #endif
     mAffectPrivateSessionLifetime(true),
+    mInvisible(false),
     mDefaultLoadFlags(nsIRequest::LOAD_NORMAL),
     mFrameType(eFrameTypeRegular),
     mOwnOrContainingAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID),
     mParentCharsetSource(0)
 {
     mHistoryID = ++gDocshellIDCounter;
     if (gDocShellCount++ == 0) {
         NS_ASSERTION(sURIFixup == nullptr,
@@ -12796,8 +12797,20 @@ nsDocShell::HasUnloadedParent()
             if (inUnload) {
                 return true;
             }
         }
         currentTreeItem.swap(parentTreeItem);
     }
     return false;
 }
+
+bool
+nsDocShell::IsInvisible()
+{
+    return mInvisible;
+}
+
+void
+nsDocShell::SetInvisible(bool aInvisible)
+{
+    mInvisible = aInvisible;
+}
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -841,16 +841,17 @@ protected:
     bool                       mSavingOldViewer;
     
     // @see nsIDocShellHistory::createdDynamically
     bool                       mDynamicallyCreated;
 #ifdef DEBUG
     bool                       mInEnsureScriptEnv;
 #endif
     bool                       mAffectPrivateSessionLifetime;
+    bool                       mInvisible;
     uint64_t                   mHistoryID;
     uint32_t                   mDefaultLoadFlags;
 
     static nsIURIFixup *sURIFixup;
 
     nsRefPtr<nsDOMNavigationTiming> mTiming;
 
     // Are we a regular frame, a browser frame, or an app frame?
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -38,17 +38,17 @@ interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(4ca172c3-67bf-4e6d-89a3-cbfb929c370d)]
+[scriptable, builtinclass, uuid(c0bb3b19-5448-4f4d-9366-cbcd4d7cdb96)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -917,9 +917,17 @@ interface nsIDocShell : nsIDocShellTreeI
 
   /**
     * Cherry picked parts of nsIController.
     * They are here, because we want to call these functions
     * from JS.
     */
   boolean isCommandEnabled(in string command);
   void doCommand(in string command);
+
+  /**
+   * Invisible DocShell are dummy construct to simulate DOM windows
+   * without any actual visual representation. They have to be marked
+   * at construction time, to avoid any painting activity.
+   */
+  [noscript, notxpcom] bool IsInvisible();
+  [noscript, notxpcom] void SetInvisible(in bool aIsInvisibleDochsell);
 };
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -121,20 +121,20 @@ typedef struct CapturingContentInfo {
   bool mAllowed;
   bool mPointerLock;
   bool mRetargetToElement;
   bool mPreventDrag;
   nsIContent* mContent;
 } CapturingContentInfo;
 
 
-// d39cd4ce-6b38-4793-8c1a-00985c56d931
+// f5b542a9-eaf0-4560-a656-37a9d379864c
 #define NS_IPRESSHELL_IID \
-{ 0xd39cd4ce, 0x6b38, 0x4793, \
-  { 0x8c, 0x1a, 0x00, 0x98, 0x5c, 0x56, 0xd9, 0x31 } }
+{ 0xf5b542a9, 0xeaf0, 0x4560, \
+  { 0x37, 0xa9, 0xd3, 0x79, 0x86, 0x4c } }
 
 // debug VerifyReflow flags
 #define VERIFY_REFLOW_ON                    0x01
 #define VERIFY_REFLOW_NOISY                 0x02
 #define VERIFY_REFLOW_ALL                   0x04
 #define VERIFY_REFLOW_DUMP_COMMANDS         0x08
 #define VERIFY_REFLOW_NOISY_RC              0x10
 #define VERIFY_REFLOW_REALLY_NOISY_RC       0x20
@@ -1462,16 +1462,27 @@ public:
   /**
    * Clear the flag indicating whether a reflow on zoom event is pending. This
    * is performed at the very end of DoReflow().
    */
   void ClearReflowOnZoomPending() {
     mReflowOnZoomPending = false;
   }
 
+  /**
+   * Documents belonging to an invisible DocShell must not be painted ever.
+   */
+  bool IsNeverPainting() {
+    return mIsNeverPainting;
+  }
+
+  void SetNeverPainting(bool aNeverPainting) {
+    mIsNeverPainting = aNeverPainting;
+  }
+
 protected:
   friend class nsRefreshDriver;
 
   // IMPORTANT: The ownership implicit in the following member variables
   // has been explicitly checked.  If you add any members to this class,
   // please make the ownership explicit (pinkerton, scc).
 
   // These are the same Document and PresContext owned by the DocViewer.
@@ -1583,11 +1594,16 @@ protected:
 
   // Flag to indicate whether or not there is a reflow on zoom event pending.
   // See IsReflowOnZoomPending() for more information.
   bool mReflowOnZoomPending;
 
   // The maximum width of a line box. Text on a single line that exceeds this
   // width will be wrapped. A value of 0 indicates that no limit is enforced.
   nscoord mMaxLineBoxWidth;
+  
+  // If a document belongs to an invisible DocShell, this flag must be set
+  // to true, so we can avoid any paint calls for widget related to this
+  // presshell.
+  bool mIsNeverPainting;
 };
 
 #endif /* nsIPresShell_h___ */
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -7419,17 +7419,17 @@ PresShell::GetCurrentItemAndPositionForE
   }
 
   NS_IF_ADDREF(*aTargetToUse = focusedContent);
 }
 
 bool
 PresShell::ShouldIgnoreInvalidation()
 {
-  return mPaintingSuppressed || !mIsActive;
+  return mPaintingSuppressed || !mIsActive || mIsNeverPainting;
 }
 
 void
 PresShell::WillPaint()
 {
   nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
   if (!rootPresContext) {
     // In some edge cases, such as when we don't have a root frame yet,
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -290,16 +290,20 @@ nsView* nsViewManager::GetDisplayRootFor
    aRegion is given in device coordinates!!
    aContext may be null, in which case layers should be used for
    rendering.
 */
 void nsViewManager::Refresh(nsView *aView, const nsIntRegion& aRegion)
 {
   NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
 
+  if (mPresShell && mPresShell->IsNeverPainting()) {
+    return;
+  }
+
   // damageRegion is the damaged area, in twips, relative to the view origin
   nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel());
   // move region from widget coordinates into view coordinates
   damageRegion.MoveBy(-aView->ViewToWidgetOffset());
 
   if (damageRegion.IsEmpty()) {
 #ifdef DEBUG_roc
     nsRect viewRect = aView->GetDimensions();
@@ -365,16 +369,20 @@ void nsViewManager::ProcessPendingUpdate
 {
   NS_ASSERTION(IsRootVM(), "Updates will be missed");
 
   // Protect against a null-view.
   if (!aView) {
     return;
   }
 
+  if (mPresShell && mPresShell->IsNeverPainting()) {
+    return;
+  }
+
   if (aView->HasWidget()) {
     aView->ResetWidgetBounds(false, true);
   }
 
   // process pending updates in child view.
   for (nsView* childView = aView->GetFirstChild(); childView;
        childView = childView->GetNextSibling()) {
     ProcessPendingUpdatesForView(childView, aFlushDirtyRegion);
@@ -410,16 +418,17 @@ void nsViewManager::ProcessPendingUpdate
       SetPainting(true);
       mPresShell->Paint(aView, nsRegion(),
                         nsIPresShell::PAINT_LAYERS);
 #ifdef MOZ_DUMP_PAINTING
       if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
         printf("---- PAINT END ----\n");
       }
 #endif
+
       aView->SetForcedRepaint(false);
       SetPainting(false);
       FlushDirtyRegionToWidget(aView);
     } else {
       FlushDirtyRegionToWidget(aView);
     }
   }
 }
--- a/xpfe/appshell/src/nsAppShellService.cpp
+++ b/xpfe/appshell/src/nsAppShellService.cpp
@@ -390,16 +390,19 @@ nsAppShellService::CreateWindowlessBrows
   widget->Create(nullptr, 0, nsIntRect(nsIntPoint(0, 0), nsIntSize(0, 0)),
                  nullptr, nullptr);
   nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(navigation);
   window->InitWindow(0, widget, 0, 0, 0, 0);
   window->Create();
 
   nsISupports *isstub = NS_ISUPPORTS_CAST(nsIWebBrowserChrome2*, stub);
   nsRefPtr<nsIWebNavigation> result = new WindowlessBrowserStub(browser, isstub);
+  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(result);
+  docshell->SetInvisible(true);
+
   result.forget(aResult);
   return NS_OK;
 }
 
 uint32_t
 nsAppShellService::CalculateWindowZLevel(nsIXULWindow *aParent,
                                          uint32_t      aChromeMask)
 {