Bug 1157941 - If the current PresShell is suppressed, paint the old one if it is available r=tn,Enn
authorGeorge Wright <george@mozilla.com>
Tue, 21 Jul 2015 21:09:02 -0400
changeset 254044 60f82e40f039995f757ebd373d968433ac6f6ad7
parent 254043 fa5d3f420ee69e56962b68fa40b0596d5ffa12de
child 254045 6713ac169edc7a276a01e5716fd5cdf4dcf4181e
push id29087
push usercbook@mozilla.com
push dateWed, 22 Jul 2015 12:01:23 +0000
treeherdermozilla-central@e7434cafdf2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn, Enn
bugs1157941
milestone42.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 1157941 - If the current PresShell is suppressed, paint the old one if it is available r=tn,Enn
browser/base/content/test/general/browser_bug427559.js
view/nsView.cpp
view/nsView.h
view/nsViewManager.cpp
widget/PuppetWidget.cpp
widget/PuppetWidget.h
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
--- a/browser/base/content/test/general/browser_bug427559.js
+++ b/browser/base/content/test/general/browser_bug427559.js
@@ -13,41 +13,26 @@ const URL = 'data:text/html;charset=utf-
 
 function getFocusedLocalName(browser) {
   return ContentTask.spawn(browser, null, function* () {
     return content.document.activeElement.localName;
   });
 }
 
 add_task(function* () {
-  gBrowser.selectedTab = gBrowser.addTab(URL);
-  let browser = gBrowser.selectedBrowser;
-  yield BrowserTestUtils.browserLoaded(browser);
+  let testTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
+
+  let browser = testTab.linkedBrowser;
 
   is((yield getFocusedLocalName(browser)), "button", "button is focused");
 
-  let promiseFocused = ContentTask.spawn(browser, null, function* () {
-    return new Promise(resolve => {
-      content.addEventListener("focus", function onFocus({target}) {
-        if (String(target.location).startsWith("data:")) {
-          content.removeEventListener("focus", onFocus);
-          resolve();
-        }
-      });
-    });
-  });
+  let blankTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
 
-  // The test page loaded, so open an empty tab, select it, then restore
-  // the test tab. This causes the test page's focused element to be removed
-  // from its document.
-  gBrowser.selectedTab = gBrowser.addTab("about:blank");
-  yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
-  gBrowser.removeCurrentTab();
-
-  // Wait until the original tab is focused again.
-  yield promiseFocused;
+  yield BrowserTestUtils.switchTab(gBrowser, testTab);
 
   // Make sure focus is given to the window because the element is now gone.
   is((yield getFocusedLocalName(browser)), "body", "body is focused");
 
   // Cleanup.
+  gBrowser.removeTab(blankTab);
   gBrowser.removeCurrentTab();
+
 });
--- a/view/nsView.cpp
+++ b/view/nsView.cpp
@@ -85,16 +85,20 @@ nsView::~nsView()
     
     mViewManager = nullptr;
   }
   else if (mParent)
   {
     mParent->RemoveChild(this);
   }
 
+  if (mPreviousWindow) {
+    mPreviousWindow->SetPreviouslyAttachedWidgetListener(nullptr);
+  }
+
   // Destroy and release the widget
   DestroyWidget();
 
   delete mDirtyRegion;
 }
 
 class DestroyWidgetRunnable : public nsRunnable {
 public:
@@ -717,16 +721,28 @@ nsresult nsView::AttachToTopLevelWidget(
 
 // Detach this view from an attached widget. 
 nsresult nsView::DetachFromTopLevelWidget()
 {
   NS_PRECONDITION(mWidgetIsTopLevel, "Not attached currently!");
   NS_PRECONDITION(mWindow, "null mWindow for DetachFromTopLevelWidget!");
 
   mWindow->SetAttachedWidgetListener(nullptr);
+  nsIWidgetListener* listener = mWindow->GetPreviouslyAttachedWidgetListener();
+
+  if (listener && listener->GetView()) {
+    // Ensure the listener doesn't think it's being used anymore
+    listener->GetView()->SetPreviousWidget(nullptr);
+  }
+
+  // If the new view's frame is paint suppressed then the window
+  // will want to use us instead until that's done
+  mWindow->SetPreviouslyAttachedWidgetListener(this);
+
+  mPreviousWindow = mWindow;
   mWindow = nullptr;
 
   mWidgetIsTopLevel = false;
   
   return NS_OK;
 }
 
 void nsView::SetZIndex(bool aAuto, int32_t aZIndex)
@@ -1091,8 +1107,14 @@ nsView::HandleEvent(WidgetGUIEvent* aEve
 
   if (view) {
     nsRefPtr<nsViewManager> vm = view->GetViewManager();
     vm->DispatchEvent(aEvent, view, &result);
   }
 
   return result;
 }
+
+bool
+nsView::IsPrimaryFramePaintSuppressed()
+{
+  return mFrame ? mFrame->PresContext()->PresShell()->IsPaintingSuppressed() : false;
+}
--- a/view/nsView.h
+++ b/view/nsView.h
@@ -277,16 +277,24 @@ public:
    * If we believe that all cutout view have a native widget, this
    * could be a replacement.
    * @param aWidget out parameter for widget that this view contains,
    *        or nullptr if there is none.
    */
   nsIWidget* GetWidget() const { return mWindow; }
 
   /**
+   * The widget which we have attached a listener to can also have a "previous"
+   * listener set on it. This is to keep track of the last nsView when navigating
+   * to a new one so that we can continue to paint that if the new one isn't ready
+   * yet.
+   */
+  void SetPreviousWidget(nsIWidget* aWidget) { mPreviousWindow = aWidget; }
+
+  /**
    * Returns true if the view has a widget associated with it.
    */
   bool HasWidget() const { return mWindow != nullptr; }
   
   void SetForcedRepaint(bool aForceRepaint) { 
     mForcedRepaint = aForceRepaint; 
   }
 
@@ -378,16 +386,18 @@ public:
   virtual nsEventStatus HandleEvent(mozilla::WidgetGUIEvent* aEvent,
                                     bool aUseAttachedEvents) override;
 
   virtual ~nsView();
 
   nsPoint GetOffsetTo(const nsView* aOther, const int32_t aAPD) const;
   nsIWidget* GetNearestWidget(nsPoint* aOffset, const int32_t aAPD) const;
 
+  bool IsPrimaryFramePaintSuppressed();
+
 private:
   explicit nsView(nsViewManager* aViewManager = nullptr,
                   nsViewVisibility aVisibility = nsViewVisibility_kShow);
 
   bool ForcedRepaint() { return mForcedRepaint; }
 
   // Do the actual work of ResetWidgetBounds, unconditionally.  Don't
   // call this method if we have no widget.
@@ -445,16 +455,17 @@ private:
   // If the hierarchy is being removed, aViewManagerParent points to the view
   // manager for the hierarchy's old parent, and will have its mouse grab
   // released if it points to any view in this view hierarchy.
   void InvalidateHierarchy(nsViewManager *aViewManagerParent);
 
   nsViewManager    *mViewManager;
   nsView           *mParent;
   nsCOMPtr<nsIWidget> mWindow;
+  nsCOMPtr<nsIWidget> mPreviousWindow;
   nsView           *mNextSibling;
   nsView           *mFirstChild;
   nsIFrame         *mFrame;
   nsRegion         *mDirtyRegion;
   int32_t           mZIndex;
   nsViewVisibility  mVis;
   // position relative our parent view origin but in our appunits
   nscoord           mPosX, mPosY;
--- a/view/nsViewManager.cpp
+++ b/view/nsViewManager.cpp
@@ -435,21 +435,30 @@ nsViewManager::ProcessPendingUpdatesPain
            : nullptr) {
       if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
           vm->mRootView->IsEffectivelyVisible() &&
           vm->mPresShell && vm->mPresShell->IsVisible()) {
         vm->FlushDelayedResize(true);
       }
     }
     nsView* view = nsView::GetViewFor(aWidget);
+
     if (!view) {
       NS_ERROR("FlushDelayedResize destroyed the nsView?");
       return;
     }
 
+    nsIWidgetListener* previousListener = aWidget->GetPreviouslyAttachedWidgetListener();
+
+    if (previousListener &&
+        previousListener != view &&
+        view->IsPrimaryFramePaintSuppressed()) {
+      return;
+    }
+
     if (mPresShell) {
 #ifdef MOZ_DUMP_PAINTING
       if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
         printf_stderr("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
                       mPresShell, view, aWidget);
       }
 #endif
 
@@ -1144,8 +1153,9 @@ nsViewManager::InvalidateHierarchy()
       NS_ADDREF(mRootViewManager);
       NS_ASSERTION(mRootViewManager != this,
                    "Root view had a parent, but it has the same view manager");
     } else {
       mRootViewManager = this;
     }
   }
 }
+
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -212,17 +212,24 @@ PuppetWidget::Resize(double aWidth,
   // XXX: roc says that |aRepaint| dictates whether or not to
   // invalidate the expanded area
   if (oldBounds.Size() < mBounds.Size() && aRepaint) {
     nsIntRegion dirty(mBounds);
     dirty.Sub(dirty,  oldBounds);
     InvalidateRegion(this, dirty);
   }
 
+  // call WindowResized() on both the current listener, and possibly
+  // also the previous one if we're in a state where we're drawing that one
+  // because the current one is paint suppressed
   if (!oldBounds.IsEqualEdges(mBounds) && mAttachedWidgetListener) {
+    if (GetCurrentWidgetListener() &&
+        GetCurrentWidgetListener() != mAttachedWidgetListener) {
+      GetCurrentWidgetListener()->WindowResized(this, mBounds.width, mBounds.height);
+    }
     mAttachedWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
   }
 
   return NS_OK;
 }
 
 nsresult
 PuppetWidget::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
@@ -308,18 +315,18 @@ PuppetWidget::DispatchEvent(WidgetGUIEve
     WidgetKeyboardEvent* keyEvent = event->AsKeyboardEvent();
     if (keyEvent) {
       mTabChild->RequestNativeKeyBindings(&autoCache, keyEvent);
     }
   }
 
   aStatus = nsEventStatus_eIgnore;
 
-  if (mAttachedWidgetListener) {
-    aStatus = mAttachedWidgetListener->HandleEvent(event, mUseAttachedEvents);
+  if (GetCurrentWidgetListener()) {
+    aStatus = GetCurrentWidgetListener()->HandleEvent(event, mUseAttachedEvents);
   }
 
   return NS_OK;
 }
 
 nsEventStatus
 PuppetWidget::DispatchInputEvent(WidgetInputEvent* aEvent)
 {
@@ -907,53 +914,53 @@ PuppetWidget::ClearCachedCursor()
   mCustomCursor = nullptr;
 }
 
 nsresult
 PuppetWidget::Paint()
 {
   MOZ_ASSERT(!mDirtyRegion.IsEmpty(), "paint event logic messed up");
 
-  if (!mAttachedWidgetListener)
+  if (!GetCurrentWidgetListener())
     return NS_OK;
 
   nsIntRegion region = mDirtyRegion;
 
   // reset repaint tracking
   mDirtyRegion.SetEmpty();
   mPaintTask.Revoke();
 
-  mAttachedWidgetListener->WillPaintWindow(this);
+  GetCurrentWidgetListener()->WillPaintWindow(this);
 
-  if (mAttachedWidgetListener) {
+  if (GetCurrentWidgetListener()) {
 #ifdef DEBUG
     debug_DumpPaintEvent(stderr, this, region,
                          nsAutoCString("PuppetWidget"), 0);
 #endif
 
     if (mozilla::layers::LayersBackend::LAYERS_CLIENT == mLayerManager->GetBackendType()) {
       // Do nothing, the compositor will handle drawing
       if (mTabChild) {
         mTabChild->NotifyPainted();
       }
     } else {
       nsRefPtr<gfxContext> ctx = new gfxContext(mDrawTarget);
       ctx->Rectangle(gfxRect(0,0,0,0));
       ctx->Clip();
       AutoLayerManagerSetup setupLayerManager(this, ctx,
                                               BufferMode::BUFFER_NONE);
-      mAttachedWidgetListener->PaintWindow(this, region);
+      GetCurrentWidgetListener()->PaintWindow(this, region);
       if (mTabChild) {
         mTabChild->NotifyPainted();
       }
     }
   }
 
-  if (mAttachedWidgetListener) {
-    mAttachedWidgetListener->DidPaintWindow();
+  if (GetCurrentWidgetListener()) {
+    GetCurrentWidgetListener()->DidPaintWindow();
   }
 
   return NS_OK;
 }
 
 void
 PuppetWidget::SetChild(PuppetWidget* aChild)
 {
@@ -1245,10 +1252,25 @@ PuppetScreenManager::GetNumberOfScreens(
 
 NS_IMETHODIMP
 PuppetScreenManager::GetSystemDefaultScale(float *aDefaultScale)
 {
   *aDefaultScale = 1.0f;
   return NS_OK;
 }
 
+nsIWidgetListener*
+PuppetWidget::GetCurrentWidgetListener()
+{
+  if (!mPreviouslyAttachedWidgetListener ||
+      !mAttachedWidgetListener) {
+    return mAttachedWidgetListener;
+  }
+
+  if (mAttachedWidgetListener->GetView()->IsPrimaryFramePaintSuppressed()) {
+    return mPreviouslyAttachedWidgetListener;
+  }
+
+  return mAttachedWidgetListener;
+}
+
 } // namespace widget
 } // namespace mozilla
--- a/widget/PuppetWidget.h
+++ b/widget/PuppetWidget.h
@@ -269,16 +269,18 @@ private:
 
   bool CacheEditorRect();
   bool CacheCompositionRects(uint32_t& aStartOffset,
                              nsTArray<mozilla::LayoutDeviceIntRect>& aRectArray,
                              uint32_t& aTargetCauseOffset);
   bool GetCaretRect(mozilla::LayoutDeviceIntRect& aCaretRect, uint32_t aCaretOffset);
   uint32_t GetCaretOffset();
 
+  nsIWidgetListener* GetCurrentWidgetListener();
+
   class PaintTask : public nsRunnable {
   public:
     NS_DECL_NSIRUNNABLE
     explicit PaintTask(PuppetWidget* widget) : mWidget(widget) {}
     void Revoke() { mWidget = nullptr; }
   private:
     PuppetWidget* mWidget;
   };
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -142,16 +142,17 @@ NS_IMPL_ISUPPORTS(nsBaseWidget, nsIWidge
 //
 // nsBaseWidget constructor
 //
 //-------------------------------------------------------------------------
 
 nsBaseWidget::nsBaseWidget()
 : mWidgetListener(nullptr)
 , mAttachedWidgetListener(nullptr)
+, mPreviouslyAttachedWidgetListener(nullptr)
 , mLayerManager(nullptr)
 , mCompositorVsyncDispatcher(nullptr)
 , mCursor(eCursor_standard)
 , mUpdateCursor(true)
 , mBorderStyle(eBorderStyle_none)
 , mUseAttachedEvents(false)
 , mBounds(0,0,0,0)
 , mOriginalBounds(nullptr)
@@ -394,16 +395,26 @@ nsBaseWidget::AttachViewToTopLevel(bool 
   return NS_OK;
 }
 
 nsIWidgetListener* nsBaseWidget::GetAttachedWidgetListener()
  {
    return mAttachedWidgetListener;
  }
 
+nsIWidgetListener* nsBaseWidget::GetPreviouslyAttachedWidgetListener()
+ {
+   return mPreviouslyAttachedWidgetListener;
+ }
+
+void nsBaseWidget::SetPreviouslyAttachedWidgetListener(nsIWidgetListener* aListener)
+ {
+   mPreviouslyAttachedWidgetListener = aListener;
+ }
+
 void nsBaseWidget::SetAttachedWidgetListener(nsIWidgetListener* aListener)
  {
    mAttachedWidgetListener = aListener;
  }
 
 //-------------------------------------------------------------------------
 //
 // Close this nsBaseWidget
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -231,16 +231,18 @@ public:
                                                          double& aOverriddenDeltaY) override;
   virtual already_AddRefed<nsIWidget>
   CreateChild(const nsIntRect  &aRect,
               nsWidgetInitData *aInitData = nullptr,
               bool             aForceUseIWidgetParent = false) override;
   NS_IMETHOD              AttachViewToTopLevel(bool aUseAttachedEvents) override;
   virtual nsIWidgetListener* GetAttachedWidgetListener() override;
   virtual void               SetAttachedWidgetListener(nsIWidgetListener* aListener) override;
+  virtual nsIWidgetListener* GetPreviouslyAttachedWidgetListener() override;
+  virtual void               SetPreviouslyAttachedWidgetListener(nsIWidgetListener* aListener) override;
   NS_IMETHOD_(TextEventDispatcher*) GetTextEventDispatcher() override final;
 
   // Helper function for dispatching events which are not processed by APZ,
   // but need to be transformed by APZ.
   nsEventStatus DispatchInputEvent(mozilla::WidgetInputEvent* aEvent) override;
 
   // Dispatch an event that must be first be routed through APZ.
   nsEventStatus DispatchAPZAwareEvent(mozilla::WidgetInputEvent* aEvent) override;
@@ -484,16 +486,17 @@ protected:
    */
   void DestroyCompositor();
   void DestroyLayerManager();
 
   void FreeShutdownObserver();
 
   nsIWidgetListener* mWidgetListener;
   nsIWidgetListener* mAttachedWidgetListener;
+  nsIWidgetListener* mPreviouslyAttachedWidgetListener;
   nsRefPtr<LayerManager> mLayerManager;
   nsRefPtr<CompositorChild> mCompositorChild;
   nsRefPtr<CompositorParent> mCompositorParent;
   nsRefPtr<mozilla::CompositorVsyncDispatcher> mCompositorVsyncDispatcher;
   nsRefPtr<APZCTreeManager> mAPZC;
   nsRefPtr<APZEventState> mAPZEventState;
   nsRefPtr<SetAllowedTouchBehaviorCallback> mSetAllowedTouchBehaviorCallback;
   nsRefPtr<WidgetShutdownObserver> mShutdownObserver;
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -116,18 +116,18 @@ typedef void* nsNativeWidget;
 #if defined(MOZ_WIDGET_GTK)
 // set/get nsPluginNativeWindowGtk, e10s specific
 #define NS_NATIVE_PLUGIN_OBJECT_PTR    104
 #endif
 // See RegisterPluginWindowForRemoteUpdates
 #define NS_NATIVE_PLUGIN_ID            105
 
 #define NS_IWIDGET_IID \
-{ 0x22b4504e, 0xddba, 0x4211, \
-  { 0xa1, 0x49, 0x6e, 0x11, 0x73, 0xc4, 0x11, 0x45 } }
+{ 0x483BF75C, 0xF909, 0x45C3, \
+  { 0x95, 0xBE, 0x41, 0x89, 0xDB, 0xCE, 0x2E, 0x13 } };
 
 /*
  * Window shadow styles
  * Also used for the -moz-window-shadow CSS property
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE             0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT          1
@@ -1070,16 +1070,18 @@ class nsIWidget : public nsISupports {
     NS_IMETHOD AttachViewToTopLevel(bool aUseAttachedEvents) = 0;
 
     /**
      * Accessor functions to get and set the attached listener. Used by
      * nsView in connection with AttachViewToTopLevel above.
      */
     virtual void SetAttachedWidgetListener(nsIWidgetListener* aListener) = 0;
     virtual nsIWidgetListener* GetAttachedWidgetListener() = 0;
+    virtual void SetPreviouslyAttachedWidgetListener(nsIWidgetListener* aListener) = 0;
+    virtual nsIWidgetListener* GetPreviouslyAttachedWidgetListener() = 0;
 
     /**
      * Accessor functions to get and set the listener which handles various
      * actions for the widget.
      */
     //@{
     virtual nsIWidgetListener* GetWidgetListener() = 0;
     virtual void SetWidgetListener(nsIWidgetListener* alistener) = 0;