diff --git a/browser/base/content/test/general/browser_bug427559.js b/browser/base/content/test/general/browser_bug427559.js index ea2c714f3a3..78cecdefae1 100644 --- a/browser/base/content/test/general/browser_bug427559.js +++ b/browser/base/content/test/general/browser_bug427559.js @@ -18,36 +18,21 @@ function getFocusedLocalName(browser) { } 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(); + }); diff --git a/view/nsView.cpp b/view/nsView.cpp index ee4db7d33d0..eefe5b849bf 100644 --- a/view/nsView.cpp +++ b/view/nsView.cpp @@ -90,6 +90,10 @@ nsView::~nsView() mParent->RemoveChild(this); } + if (mPreviousWindow) { + mPreviousWindow->SetPreviouslyAttachedWidgetListener(nullptr); + } + // Destroy and release the widget DestroyWidget(); @@ -722,6 +726,18 @@ nsresult nsView::DetachFromTopLevelWidget() 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; @@ -1096,3 +1112,9 @@ nsView::HandleEvent(WidgetGUIEvent* aEvent, return result; } + +bool +nsView::IsPrimaryFramePaintSuppressed() +{ + return mFrame ? mFrame->PresContext()->PresShell()->IsPaintingSuppressed() : false; +} diff --git a/view/nsView.h b/view/nsView.h index 6aa2ba407be..e4980a7a7fe 100644 --- a/view/nsView.h +++ b/view/nsView.h @@ -281,6 +281,14 @@ public: */ 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. */ @@ -383,6 +391,8 @@ public: 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); @@ -450,6 +460,7 @@ private: nsViewManager *mViewManager; nsView *mParent; nsCOMPtr mWindow; + nsCOMPtr mPreviousWindow; nsView *mNextSibling; nsView *mFirstChild; nsIFrame *mFrame; diff --git a/view/nsViewManager.cpp b/view/nsViewManager.cpp index 43b01618858..28997a79370 100644 --- a/view/nsViewManager.cpp +++ b/view/nsViewManager.cpp @@ -440,11 +440,20 @@ nsViewManager::ProcessPendingUpdatesPaint(nsIWidget* aWidget) } } 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()) { @@ -1149,3 +1158,4 @@ nsViewManager::InvalidateHierarchy() } } } + diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp index 15e5496da57..897b5a936ad 100644 --- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -217,7 +217,14 @@ PuppetWidget::Resize(double aWidth, 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); } @@ -313,8 +320,8 @@ PuppetWidget::DispatchEvent(WidgetGUIEvent* event, nsEventStatus& aStatus) aStatus = nsEventStatus_eIgnore; - if (mAttachedWidgetListener) { - aStatus = mAttachedWidgetListener->HandleEvent(event, mUseAttachedEvents); + if (GetCurrentWidgetListener()) { + aStatus = GetCurrentWidgetListener()->HandleEvent(event, mUseAttachedEvents); } return NS_OK; @@ -912,7 +919,7 @@ PuppetWidget::Paint() { MOZ_ASSERT(!mDirtyRegion.IsEmpty(), "paint event logic messed up"); - if (!mAttachedWidgetListener) + if (!GetCurrentWidgetListener()) return NS_OK; nsIntRegion region = mDirtyRegion; @@ -921,9 +928,9 @@ PuppetWidget::Paint() mDirtyRegion.SetEmpty(); mPaintTask.Revoke(); - mAttachedWidgetListener->WillPaintWindow(this); + GetCurrentWidgetListener()->WillPaintWindow(this); - if (mAttachedWidgetListener) { + if (GetCurrentWidgetListener()) { #ifdef DEBUG debug_DumpPaintEvent(stderr, this, region, nsAutoCString("PuppetWidget"), 0); @@ -940,15 +947,15 @@ PuppetWidget::Paint() 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; @@ -1250,5 +1257,20 @@ PuppetScreenManager::GetSystemDefaultScale(float *aDefaultScale) return NS_OK; } +nsIWidgetListener* +PuppetWidget::GetCurrentWidgetListener() +{ + if (!mPreviouslyAttachedWidgetListener || + !mAttachedWidgetListener) { + return mAttachedWidgetListener; + } + + if (mAttachedWidgetListener->GetView()->IsPrimaryFramePaintSuppressed()) { + return mPreviouslyAttachedWidgetListener; + } + + return mAttachedWidgetListener; +} + } // namespace widget } // namespace mozilla diff --git a/widget/PuppetWidget.h b/widget/PuppetWidget.h index a18d85a779c..75ffe0d362f 100644 --- a/widget/PuppetWidget.h +++ b/widget/PuppetWidget.h @@ -274,6 +274,8 @@ private: bool GetCaretRect(mozilla::LayoutDeviceIntRect& aCaretRect, uint32_t aCaretOffset); uint32_t GetCaretOffset(); + nsIWidgetListener* GetCurrentWidgetListener(); + class PaintTask : public nsRunnable { public: NS_DECL_NSIRUNNABLE diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index fe4f693bc25..a8f7b973a85 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -147,6 +147,7 @@ NS_IMPL_ISUPPORTS(nsBaseWidget, nsIWidget, nsISupportsWeakReference) nsBaseWidget::nsBaseWidget() : mWidgetListener(nullptr) , mAttachedWidgetListener(nullptr) +, mPreviouslyAttachedWidgetListener(nullptr) , mLayerManager(nullptr) , mCompositorVsyncDispatcher(nullptr) , mCursor(eCursor_standard) @@ -399,6 +400,16 @@ nsIWidgetListener* nsBaseWidget::GetAttachedWidgetListener() return mAttachedWidgetListener; } +nsIWidgetListener* nsBaseWidget::GetPreviouslyAttachedWidgetListener() + { + return mPreviouslyAttachedWidgetListener; + } + +void nsBaseWidget::SetPreviouslyAttachedWidgetListener(nsIWidgetListener* aListener) + { + mPreviouslyAttachedWidgetListener = aListener; + } + void nsBaseWidget::SetAttachedWidgetListener(nsIWidgetListener* aListener) { mAttachedWidgetListener = aListener; diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h index e8d2f18dafb..8e2edef8d5e 100644 --- a/widget/nsBaseWidget.h +++ b/widget/nsBaseWidget.h @@ -236,6 +236,8 @@ public: 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, @@ -489,6 +491,7 @@ protected: nsIWidgetListener* mWidgetListener; nsIWidgetListener* mAttachedWidgetListener; + nsIWidgetListener* mPreviouslyAttachedWidgetListener; nsRefPtr mLayerManager; nsRefPtr mCompositorChild; nsRefPtr mCompositorParent; diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 7218ba9bc43..d48d9693ef5 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -121,8 +121,8 @@ typedef void* nsNativeWidget; #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 @@ -1075,6 +1075,8 @@ class nsIWidget : public nsISupports { */ 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