tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit d7598a4a4a878f626044056ba651d3dc3d1e2d8c
parent 94d0c59e525f9a4531119b4e07088e655a70bb5c
Author: Emilio Cobos Álvarez <emilio@crisal.io>
Date:   Thu, 27 Nov 2025 08:49:59 +0000

Bug 2002232 - Clean up top level widget painting. r=jwatt

Turns out this PaintSynchronously() call is needed, however we can do
better and centralize all the painting in a single PaintWindow():

 * DidPaintWindow is not needed, it's only used to dispatch an observer
   notification for testing.

 * WillPaintWindow and PaintWindow can be unified. Both can already run
   script in practice, and toplevel window bounds and widget bounds
   shouldn't change here anyways nowadays, since we recompute widget
   bounds only during the rendering loop.

Differential Revision: https://phabricator.services.mozilla.com/D273997

Diffstat:
Mlayout/base/PresShell.cpp | 23++++++++---------------
Mlayout/base/PresShell.h | 9+++------
Mlayout/xul/nsMenuPopupFrame.cpp | 3+--
Mlayout/xul/nsMenuPopupFrame.h | 2+-
Mview/nsView.cpp | 29+++++++++++++++--------------
Mview/nsView.h | 6+-----
Mview/nsViewManager.cpp | 31-------------------------------
Mview/nsViewManager.h | 4----
Mwidget/PuppetWidget.cpp | 9+--------
Mwidget/cocoa/nsCocoaWindow.h | 5++---
Mwidget/cocoa/nsCocoaWindow.mm | 30++++++------------------------
Mwidget/gtk/nsWindow.cpp | 52+++++++++++-----------------------------------------
Mwidget/nsIWidgetListener.h | 28++--------------------------
Mwidget/uikit/nsWindow.h | 3+--
Mwidget/uikit/nsWindow.mm | 27++++-----------------------
Mwidget/windows/nsWindowGfx.cpp | 93+++++++++++++++++++++++++++++++++++++------------------------------------------
16 files changed, 100 insertions(+), 254 deletions(-)

diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp @@ -10137,23 +10137,16 @@ void PresShell::WillPaint() { } void PresShell::DidPaintWindow() { - nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext(); - if (rootPresContext != mPresContext) { - // This could be a popup's presshell. No point in notifying XPConnect - // about compositing of popups. + if (mHasReceivedPaintMessage) { return; } - - if (!mHasReceivedPaintMessage) { - mHasReceivedPaintMessage = true; - - nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService(); - if (obsvc && mDocument) { - nsPIDOMWindowOuter* window = mDocument->GetWindow(); - if (window && nsGlobalWindowOuter::Cast(window)->IsChromeWindow()) { - obsvc->NotifyObservers(window, "widget-first-paint", nullptr); - } - } + mHasReceivedPaintMessage = true; + nsPIDOMWindowOuter* win = mDocument->GetWindow(); + if (!win || !nsGlobalWindowOuter::Cast(win)->IsChromeWindow()) { + return; + } + if (nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService()) { + obsvc->NotifyObservers(win, "widget-first-paint", nullptr); } } diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h @@ -1086,12 +1086,9 @@ class PresShell final : public nsStubDocumentObserver, nsresult HandleEvent(nsIFrame* aFrame, WidgetGUIEvent* aEvent, bool aDontRetargetEvents, nsEventStatus* aEventStatus); bool ShouldIgnoreInvalidation(); - /** - * Notify that we called Paint with PaintFlags::PaintComposite. - * Fires on the presshell for the painted widget. - * This is issued at a time when it's safe to modify widget geometry. - */ - MOZ_CAN_RUN_SCRIPT void DidPaintWindow(); + // Notify that we called PaintWindow() from widget. + MOZ_CAN_RUN_SCRIPT + void DidPaintWindow(); bool IsVisible() const; bool IsUnderHiddenEmbedderElement() const { diff --git a/layout/xul/nsMenuPopupFrame.cpp b/layout/xul/nsMenuPopupFrame.cpp @@ -2593,7 +2593,7 @@ nsEventStatus nsMenuPopupFrame::HandleEvent(mozilla::WidgetGUIEvent* aEvent) { return status; } -bool nsMenuPopupFrame::PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion) { +void nsMenuPopupFrame::PaintWindow(nsIWidget* aWidget) { MOZ_ASSERT(aWidget == mWidget); nsAutoScriptBlocker scriptBlocker; RefPtr ps = PresShell(); @@ -2603,7 +2603,6 @@ bool nsMenuPopupFrame::PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion) { } else { ps->SyncPaintFallback(this, renderer); } - return true; } void nsMenuPopupFrame::DidCompositeWindow( diff --git a/layout/xul/nsMenuPopupFrame.h b/layout/xul/nsMenuPopupFrame.h @@ -206,7 +206,7 @@ class nsMenuPopupFrame final : public nsBlockFrame, public nsIWidgetListener { MOZ_CAN_RUN_SCRIPT_BOUNDARY nsEventStatus HandleEvent(mozilla::WidgetGUIEvent* aEvent) override; MOZ_CAN_RUN_SCRIPT_BOUNDARY - bool PaintWindow(nsIWidget* aWidget, mozilla::LayoutDeviceIntRegion) override; + void PaintWindow(nsIWidget* aWidget) override; void DidCompositeWindow(mozilla::layers::TransactionId aTransactionId, const mozilla::TimeStamp& aCompositeStart, const mozilla::TimeStamp& aCompositeEnd) override; diff --git a/view/nsView.cpp b/view/nsView.cpp @@ -22,6 +22,7 @@ #include "nsContentUtils.h" // for nsAutoScriptBlocker #include "nsDocShell.h" #include "nsLayoutUtils.h" +#include "WindowRenderer.h" #include "mozilla/StartupTimeline.h" using namespace mozilla; @@ -228,20 +229,20 @@ void nsView::AndroidPipModeChanged(bool aPipMode) { } #endif -void nsView::WillPaintWindow(nsIWidget* aWidget) { - RefPtr<nsViewManager> vm = mViewManager; - vm->WillPaintWindow(aWidget); -} - -bool nsView::PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion) { - RefPtr<nsViewManager> vm = mViewManager; - vm->PaintWindow(aWidget); - return true; -} - -void nsView::DidPaintWindow() { - RefPtr<nsViewManager> vm = mViewManager; - vm->DidPaintWindow(); +void nsView::PaintWindow(nsIWidget* aWidget) { + RefPtr ps = GetPresShell(); + if (!ps) { + return; + } + RefPtr renderer = aWidget->GetWindowRenderer(); + if (!renderer->NeedsWidgetInvalidation()) { + ps->PaintSynchronously(); + renderer->FlushRendering(wr::RenderReasons::WIDGET); + } else { + ps->SyncPaintFallback(ps->GetRootFrame(), renderer); + } + mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT); + ps->DidPaintWindow(); } void nsView::DidCompositeWindow(mozilla::layers::TransactionId aTransactionId, diff --git a/view/nsView.h b/view/nsView.h @@ -199,11 +199,7 @@ class nsView final : public nsIWidgetListener { void AndroidPipModeChanged(bool) override; #endif MOZ_CAN_RUN_SCRIPT_BOUNDARY - void WillPaintWindow(nsIWidget* aWidget) override; - MOZ_CAN_RUN_SCRIPT_BOUNDARY - bool PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion aRegion) override; - MOZ_CAN_RUN_SCRIPT_BOUNDARY - void DidPaintWindow() override; + void PaintWindow(nsIWidget* aWidget) override; void DidCompositeWindow(mozilla::layers::TransactionId aTransactionId, const mozilla::TimeStamp& aCompositeStart, const mozilla::TimeStamp& aCompositeEnd) override; diff --git a/view/nsViewManager.cpp b/view/nsViewManager.cpp @@ -150,37 +150,6 @@ void nsViewManager::FlushDelayedResize() { } } -void nsViewManager::PaintWindow(nsIWidget* aWidget) { - RefPtr ps = mPresShell; - if (!ps) { - return; - } - RefPtr renderer = aWidget->GetWindowRenderer(); - if (!renderer->NeedsWidgetInvalidation()) { - renderer->FlushRendering(wr::RenderReasons::WIDGET); - } else { - ps->SyncPaintFallback(ps->GetRootFrame(), renderer); - } - mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT); -} - -void nsViewManager::WillPaintWindow(nsIWidget* aWidget) { - WindowRenderer* renderer = aWidget->GetWindowRenderer(); - if (renderer->NeedsWidgetInvalidation()) { - return; - } - if (RefPtr ps = mPresShell) { - // FIXME(bug 2002232): Why is this needed? - ps->PaintSynchronously(); - } -} - -void nsViewManager::DidPaintWindow() { - if (RefPtr<PresShell> presShell = mPresShell) { - presShell->DidPaintWindow(); - } -} - void nsViewManager::MaybeUpdateLastUserEventTime(WidgetGUIEvent* aEvent) { WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); if ((mouseEvent && diff --git a/view/nsViewManager.h b/view/nsViewManager.h @@ -106,10 +106,6 @@ class nsViewManager final { MOZ_CAN_RUN_SCRIPT_BOUNDARY void DoSetWindowDimensions(const nsSize&); bool ShouldDelayResize() const; - MOZ_CAN_RUN_SCRIPT void WillPaintWindow(nsIWidget* aWidget); - MOZ_CAN_RUN_SCRIPT void PaintWindow(nsIWidget* aWidget); - MOZ_CAN_RUN_SCRIPT void DidPaintWindow(); - mozilla::PresShell* mPresShell; // The size for a resize that we delayed until the root view becomes diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp @@ -896,18 +896,11 @@ PuppetWidget::WidgetPaintTask::Run() { } void PuppetWidget::Paint() { - if (!GetPaintListener()) { - return; - } - mWidgetPaintTask.Revoke(); RefPtr<PuppetWidget> strongThis(this); - - GetPaintListener()->WillPaintWindow(this); - if (auto* listener = GetPaintListener()) { - listener->DidPaintWindow(); + listener->PaintWindow(this); } } diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h @@ -375,9 +375,8 @@ class nsCocoaWindow final : public nsIWidget { int32_t RoundsWidgetCoordinatesTo() override; // Mac specific methods - void WillPaintWindow(); - bool PaintWindow(LayoutDeviceIntRegion aRegion); - bool PaintWindowInDrawTarget(mozilla::gfx::DrawTarget* aDT, + void PaintWindow(); + void PaintWindowInDrawTarget(mozilla::gfx::DrawTarget* aDT, const LayoutDeviceIntRegion& aRegion, const mozilla::gfx::IntSize& aSurfaceSize); diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm @@ -753,33 +753,17 @@ void nsCocoaWindow::Invalidate(const LayoutDeviceIntRect& aRect) { #pragma mark - -void nsCocoaWindow::WillPaintWindow() { +void nsCocoaWindow::PaintWindow() { if (nsIWidgetListener* listener = GetPaintListener()) { - listener->WillPaintWindow(this); + listener->PaintWindow(this); } } -bool nsCocoaWindow::PaintWindow(LayoutDeviceIntRegion aRegion) { - nsIWidgetListener* listener = GetPaintListener(); - if (!listener) { - return false; - } - - bool returnValue = listener->PaintWindow(this, aRegion); - - listener = GetPaintListener(); - if (listener) { - listener->DidPaintWindow(); - } - - return returnValue; -} - -bool nsCocoaWindow::PaintWindowInDrawTarget( +void nsCocoaWindow::PaintWindowInDrawTarget( gfx::DrawTarget* aDT, const LayoutDeviceIntRegion& aRegion, const gfx::IntSize& aSurfaceSize) { if (!aDT || !aDT->IsValid()) { - return false; + return; } gfxContext targetContext(aDT); @@ -795,9 +779,8 @@ bool nsCocoaWindow::PaintWindowInDrawTarget( nsAutoRetainCocoaObject kungFuDeathGrip(mChildView); if (GetWindowRenderer()->GetBackendType() == LayersBackend::LAYERS_NONE) { nsIWidget::AutoLayerManagerSetup setupLayerManager(this, &targetContext); - return PaintWindow(aRegion); + PaintWindow(); } - return false; } void nsCocoaWindow::EnsureContentLayerForMainThreadPainting() { @@ -843,7 +826,6 @@ void nsCocoaWindow::PaintWindowInContentLayer() { void nsCocoaWindow::HandleMainThreadCATransaction() { AUTO_PROFILER_MARKER("HandleMainThreadCATransaction", GRAPHICS); - WillPaintWindow(); if (GetWindowRenderer()->GetBackendType() == LayersBackend::LAYERS_NONE) { // We're in BasicLayers mode, i.e. main thread software compositing. @@ -854,7 +836,7 @@ void nsCocoaWindow::HandleMainThreadCATransaction() { // NotifySurfaceReady on the compositor thread to update mNativeLayerRoot's // contents, and the main thread (this thread) will wait inside PaintWindow // during that time. - PaintWindow(LayoutDeviceIntRegion(GetClientBounds())); + PaintWindow(); } { diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp @@ -4107,11 +4107,6 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) { RefPtr<nsWindow> strongThis(this); - // Dispatch WillPaintWindow notification to allow scripts etc. to run - // before we paint. It also spins event loop which may show/hide the window - // so we may have new renderer etc. - GetPaintListener()->WillPaintWindow(this); - // If the window has been destroyed during the will paint notification, // there is nothing left to do. if (mIsDestroyed) { @@ -4137,9 +4132,6 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) { layerManager->SetNeedsComposite(false); } - // Our bounds may have changed after calling WillPaintWindow. Clip - // to the new bounds here. The region is relative to this - // window. region.AndWith(LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetClientSize())); if (region.IsEmpty()) { LOG("quit, region.IsEmpty()"); @@ -4149,16 +4141,7 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) { // If this widget uses OMTC... if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) { LOG("redirect painting to OMTC rendering..."); - listener->PaintWindow(this, region); - - // Re-get the listener since the will paint notification might have - // killed it. - listener = GetPaintListener(); - if (!listener) { - return TRUE; - } - - listener->DidPaintWindow(); + listener->PaintWindow(this); return TRUE; } @@ -4191,25 +4174,16 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) { #endif // MOZ_X11 - { - if (renderer->GetBackendType() == LayersBackend::LAYERS_NONE) { - if (GetTransparencyMode() == TransparencyMode::Transparent && - mHasAlphaVisual) { - // If our draw target is unbuffered and we use an alpha channel, - // clear the image beforehand to ensure we don't get artifacts from a - // reused SHM image. See bug 1258086. - dt->ClearRect(Rect(boundsRect)); - } - AutoLayerManagerSetup setupLayerManager(this, ctx.ptrOr(nullptr)); - listener->PaintWindow(this, region); - - // Re-get the listener since the will paint notification might have - // killed it. - listener = GetPaintListener(); - if (!listener) { - return TRUE; - } + if (renderer->GetBackendType() == LayersBackend::LAYERS_NONE) { + if (GetTransparencyMode() == TransparencyMode::Transparent && + mHasAlphaVisual) { + // If our draw target is unbuffered and we use an alpha channel, + // clear the image beforehand to ensure we don't get artifacts from a + // reused SHM image. See bug 1258086. + dt->ClearRect(Rect(boundsRect)); } + AutoLayerManagerSetup setupLayerManager(this, ctx.ptrOr(nullptr)); + listener->PaintWindow(this); } #ifdef MOZ_X11 @@ -4219,12 +4193,8 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) { EndRemoteDrawingInRegion(dt, region); - listener->DidPaintWindow(); - // Synchronously flush any new dirty areas - cairo_region_t* dirtyArea = gdk_window_get_update_area(mGdkWindow); - - if (dirtyArea) { + if (cairo_region_t* dirtyArea = gdk_window_get_update_area(mGdkWindow)) { gdk_window_invalidate_region(mGdkWindow, dirtyArea, false); cairo_region_destroy(dirtyArea); gdk_window_process_updates(mGdkWindow, false); diff --git a/widget/nsIWidgetListener.h b/widget/nsIWidgetListener.h @@ -110,33 +110,9 @@ class nsIWidgetListener { */ virtual bool RequestWindowClose(nsIWidget* aWidget) { return false; } - /* - * Indicate that a paint is about to occur on this window. This is called - * at a time when it's OK to change the geometry of this widget or of - * other widgets. Must be called before every call to PaintWindow. - */ - MOZ_CAN_RUN_SCRIPT_BOUNDARY - virtual void WillPaintWindow(nsIWidget* aWidget) {} - - /** - * Paint the specified region of the window. Returns true if the - * notification was handled. - * This is called at a time when it is not OK to change the geometry of - * this widget or of other widgets. - */ - MOZ_CAN_RUN_SCRIPT_BOUNDARY - virtual bool PaintWindow(nsIWidget* aWidget, - mozilla::LayoutDeviceIntRegion aRegion) { - return false; - } - /** - * Indicates that a paint occurred. - * This is called at a time when it is OK to change the geometry of - * this widget or of other widgets. - * Must be called after every call to PaintWindow. - */ + /** Paint the window if needed. */ MOZ_CAN_RUN_SCRIPT_BOUNDARY - virtual void DidPaintWindow() {} + virtual void PaintWindow(nsIWidget* aWidget) {} virtual void DidCompositeWindow(mozilla::layers::TransactionId aTransactionId, const mozilla::TimeStamp& aCompositeStart, diff --git a/widget/uikit/nsWindow.h b/widget/uikit/nsWindow.h @@ -85,8 +85,7 @@ class nsWindow final : public nsIWidget { void Invalidate(const LayoutDeviceIntRect& aRect) override; - void WillPaintWindow(); - bool PaintWindow(LayoutDeviceIntRegion aRegion); + void PaintWindow(); bool HasModalDescendents() { return false; } diff --git a/widget/uikit/nsWindow.mm b/widget/uikit/nsWindow.mm @@ -412,11 +412,7 @@ class nsAutoRetainUIKitObject { if (!mGeckoChild->IsVisible()) return; mWaitingForPaint = NO; - - LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds(); - LayoutDeviceIntRegion region(geckoBounds); - - mGeckoChild->PaintWindow(region); + mGeckoChild->PaintWindow(); } // Called asynchronously after setNeedsDisplay in order to avoid entering the @@ -958,25 +954,12 @@ void nsWindow::SetFocus(Raise, mozilla::dom::CallerType) { [mNativeView becomeFirstResponder]; } -void nsWindow::WillPaintWindow() { +void nsWindow::PaintWindow() { if (mWidgetListener) { - mWidgetListener->WillPaintWindow(this); + mWidgetListener->PaintWindow(this); } } -bool nsWindow::PaintWindow(LayoutDeviceIntRegion aRegion) { - if (!mWidgetListener) return false; - - bool returnValue = false; - returnValue = mWidgetListener->PaintWindow(this, aRegion); - - if (mWidgetListener) { - mWidgetListener->DidPaintWindow(); - } - - return returnValue; -} - void nsWindow::ReportMoveEvent() { NotifyWindowMoved(mBounds.x, mBounds.y); } void nsWindow::ReportSizeModeEvent(nsSizeMode aMode) { @@ -1141,13 +1124,11 @@ layers::NativeLayerRoot* nsWindow::GetNativeLayerRoot() { } void nsWindow::HandleMainThreadCATransaction() { - WillPaintWindow(); - // Trigger a synchronous OMTC composite. This will call NextSurface and // NotifySurfaceReady on the compositor thread to update mNativeLayerRoot's // contents, and the main thread (this thread) will wait inside PaintWindow // during that time. - PaintWindow(LayoutDeviceIntRegion(GetBounds())); + PaintWindow(); { // Apply the changes inside mNativeLayerRoot to the underlying CALayers. Now diff --git a/widget/windows/nsWindowGfx.cpp b/widget/windows/nsWindowGfx.cpp @@ -179,13 +179,6 @@ bool nsWindow::OnPaint() { mLastPaintBounds = mBounds; RefPtr<nsWindow> strongThis(this); - if (nsIWidgetListener* listener = GetPaintListener()) { - // WillPaintWindow will update our transparent area if needed, which we use - // below. Note that this might kill the listener. - listener->WillPaintWindow(this); - } - - bool didPaint = false; // BeginPaint/EndPaint must be called to make Windows think that invalid // area is painted. Otherwise it will continue sending the same message // endlessly. Note that we need to call it after WillPaintWindow, which @@ -196,39 +189,9 @@ bool nsWindow::OnPaint() { HDC hDC = ::BeginPaint(mWnd, &ps); auto endPaint = MakeScopeExit([&] { ::EndPaint(mWnd, &ps); - if (didPaint) { - mLastPaintEndTime = TimeStamp::Now(); - if (nsIWidgetListener* listener = GetPaintListener()) { - listener->DidPaintWindow(); - } - } + mLastPaintEndTime = TimeStamp::Now(); }); - LayoutDeviceIntRegion region = GetRegionToPaint(ps, hDC); - LayoutDeviceIntRegion regionToClear; - // Clear the translucent region if needed. - if (isTransparent) { - auto translucentRegion = GetTranslucentRegion(); - // Clear the parts of the translucent region that aren't clear already or - // that Windows has told us to repaint. - // NOTE(emilio): Ordering of region ops is a bit subtle to avoid - // unnecessary copies, but we want to end up with: - // regionToClear = translucentRegion - (mClearedRegion - region) - // mClearedRegion = translucentRegion; - // And add translucentRegion to region afterwards. - regionToClear = translucentRegion; - if (!mClearedRegion.IsEmpty()) { - mClearedRegion.SubOut(region); - regionToClear.SubOut(mClearedRegion); - } - region.OrWith(translucentRegion); - mClearedRegion = std::move(translucentRegion); - } - - if (region.IsEmpty() || !GetPaintListener()) { - return false; - } - Maybe<FallbackPaintContext> fallback; if (isFallback) { uint32_t flags = isTransparent ? gfxWindowsSurface::FLAG_IS_TRANSPARENT : 0; @@ -246,12 +209,23 @@ bool nsWindow::OnPaint() { fallback.emplace(this, std::move(targetSurface), std::move(dt)); } - if (knowsCompositor && layerManager) { - layerManager->SendInvalidRegion(region.ToUnknownRegion()); - layerManager->ScheduleComposite(wr::RenderReasons::WIDGET); + LayoutDeviceIntRegion region = GetRegionToPaint(ps, hDC); + if (!mClearedRegion.IsEmpty()) { + // Don't consider regions that Windows has told us to repaint as clear, even + // if we've cleared them before. + mClearedRegion.SubOut(region); } - if (!regionToClear.IsEmpty()) { + auto ComputeAndClearTranslucentRegion = [&]() { + if (!isTransparent) { + return; + } + const LayoutDeviceIntRegion translucentRegion = GetTranslucentRegion(); + auto regionToClear = translucentRegion; + if (!mClearedRegion.IsEmpty()) { + regionToClear.SubOut(mClearedRegion); + } + mClearedRegion = std::move(translucentRegion); auto black = reinterpret_cast<HBRUSH>(::GetStockObject(BLACK_BRUSH)); // We could use RegionToHRGN, but at least for simple regions (and // possibly for complex ones too?) FillRect is faster; see bug 1946365 @@ -268,6 +242,33 @@ bool nsWindow::OnPaint() { ::FillRect(hDC, &rect, black); } } + region.OrWith(regionToClear); + }; + + // This is a bit subtle: For fallback windows, we paint directly into the DC, + // so we need to clear it before PaintWindow(). + // + // For composited windows, we first need to paint (so that we know the + // translucent area, which is computed from the display list), then we clear + // it. + // + // We don't track the transparent region for popups (meaning we always rely on + // the whole client area being cleared) so this works out. + if (fallback) { + ComputeAndClearTranslucentRegion(); + } + + if (auto* listener = GetPaintListener()) { + listener->PaintWindow(this); + } + + if (!fallback) { + ComputeAndClearTranslucentRegion(); + } + + if (knowsCompositor && layerManager && !region.IsEmpty()) { + layerManager->SendInvalidRegion(region.ToUnknownRegion()); + layerManager->ScheduleComposite(wr::RenderReasons::WIDGET); } #ifdef WIDGET_DEBUG_OUTPUT @@ -275,18 +276,12 @@ bool nsWindow::OnPaint() { (int32_t)mWnd); #endif // WIDGET_DEBUG_OUTPUT - bool result = true; - if (nsIWidgetListener* listener = GetPaintListener()) { - result = listener->PaintWindow(this, region); - } - if (!isFallback && !gfxEnv::MOZ_DISABLE_FORCE_PRESENT()) { NS_DispatchToMainThread(NewRunnableMethod("nsWindow::ForcePresent", this, &nsWindow::ForcePresent)); } - didPaint = true; - return result; + return true; } bool nsWindow::NeedsToTrackWindowOcclusionState() {