tor-browser

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

commit e318afd7771d07ef1f18f64c2ab789a2b3b50df5
parent 81568ecfb7bfe264c334797d7edbe72c52762299
Author: agoloman <agoloman@mozilla.com>
Date:   Tue, 25 Nov 2025 14:25:22 +0200

Revert "Bug 2000504 - Make PresShell drive more of painting. r=jwatt" for causing assertions.

This reverts commit 68078cc21e6792ffc2a7fa9a69bfecc62c1ca761.

Diffstat:
Mlayout/base/PresShell.cpp | 68++++++++++++++++++++++----------------------------------------------
Mlayout/base/PresShell.h | 6+-----
Mlayout/base/nsRefreshDriver.cpp | 5++---
Mview/nsView.cpp | 4++--
Mview/nsViewManager.cpp | 234++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mview/nsViewManager.h | 46+++++++++++++++++++++++++++++++++++++++++++++-
Mxpfe/appshell/AppWindow.cpp | 4----
7 files changed, 291 insertions(+), 76 deletions(-)

diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp @@ -69,7 +69,6 @@ #include "mozilla/ServoBindings.h" #include "mozilla/ServoStyleSet.h" #include "mozilla/Sprintf.h" -#include "mozilla/StartupTimeline.h" #include "mozilla/StaticAnalysisFunctions.h" #include "mozilla/StaticPrefs_apz.h" #include "mozilla/StaticPrefs_dom.h" @@ -4256,9 +4255,17 @@ bool PresShell::IsSafeToFlush() const { if (mIsReflowing || mChangeNestCount || mIsDestroying) { return false; } - // Not safe either if we're painting. - // TODO(emilio): What could call us while painting now? - return !mIsPainting; + + // Not safe if we are painting + if (nsViewManager* viewManager = GetViewManager()) { + bool isPainting = false; + viewManager->IsPainting(isPainting); + if (isPainting) { + return false; + } + } + + return true; } void PresShell::NotifyFontFaceSetOnRefresh() { @@ -4475,6 +4482,12 @@ void PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush) { // scrollable frame and the primary frame of the scroll container. TriggerPendingScrollTimelineAnimations(mDocument); } + + if (flushType >= FlushType::Layout) { + if (!mIsDestroying) { + viewManager->UpdateWidgetGeometry(); + } + } } MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::CharacterDataChanged( @@ -12184,13 +12197,10 @@ void PresShell::ResetVisualViewportSize() { } void PresShell::SetNeedsWindowPropertiesSync() { - if (XRE_IsContentProcess() || !IsRoot()) { - // Window properties are only relevant to top level widgets in the parent - // process - return; - } mNeedsWindowPropertiesSync = true; - SchedulePaint(); + if (mViewManager) { + mViewManager->PostPendingUpdate(); + } } bool PresShell::SetVisualViewportOffset(const nsPoint& aScrollOffset, @@ -12468,42 +12478,8 @@ PresShell::WindowSizeConstraints PresShell::GetWindowSizeConstraints() { return {minSize, maxSize}; } -void PresShell::PaintSynchronously() { - MOZ_ASSERT(!mIsPainting, "re-entrant paint?"); - if (IsNeverPainting() || IsPaintingSuppressed() || !IsVisible() || - MOZ_UNLIKELY(NS_WARN_IF(mIsPainting))) { - return; - } - RefPtr widget = GetOwnWidget(); - if (NS_WARN_IF(!widget)) { - // We were asked to paint a non-root pres shell, or an already-detached - // shell. - return; - } - MOZ_ASSERT(widget->IsTopLevelWidget()); - if (!widget->NeedsPaint()) { - return; - } - - // FIXME: This might not be needed now except for widget paints - // (WillPaintWindow) and maybe FlushWillPaintObservers. - WillPaint(); - - if (MOZ_UNLIKELY(mIsDestroying)) { - return; - } - - mViewManager->FlushDelayedResize(); - - mIsPainting = true; - auto cleanUpPaintingBit = MakeScopeExit([&] { mIsPainting = false; }); - nsAutoScriptBlocker blocker; - RefPtr<WindowRenderer> renderer = widget->GetWindowRenderer(); - PaintAndRequestComposite(GetRootFrame(), renderer, PaintFlags::None); -} - -void PresShell::SyncWindowPropertiesIfNeeded() { - if (!mNeedsWindowPropertiesSync) { +void PresShell::SyncWindowProperties() { + if (!mNeedsWindowPropertiesSync || XRE_IsContentProcess()) { return; } diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h @@ -1293,10 +1293,7 @@ class PresShell final : public nsStubDocumentObserver, return mNeedLayoutFlush || mNeedStyleFlush; } - void MOZ_CAN_RUN_SCRIPT PaintSynchronously(); - // Ensures the top-level window has the right size constraints / - // color-scheme / etc. - void SyncWindowPropertiesIfNeeded(); + void SyncWindowProperties(); struct WindowSizeConstraints { nsSize mMinSize; nsSize mMaxSize; @@ -3416,7 +3413,6 @@ class PresShell final : public nsStubDocumentObserver, bool mDidInitialize : 1; bool mIsDestroying : 1; bool mIsReflowing : 1; - bool mIsPainting : 1 = false; bool mIsObservingDocument : 1; // Whether we shouldn't ever get to FlushPendingNotifications. This flag is diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp @@ -2628,11 +2628,10 @@ bool nsRefreshDriver::PaintIfNeeded() { } mCompositionPayloads.Clear(); } - RefPtr<PresShell> ps = mPresContext->PresShell(); + RefPtr<nsViewManager> vm = mPresContext->PresShell()->GetViewManager(); { PaintTelemetry::AutoRecordPaint record; - ps->SyncWindowPropertiesIfNeeded(); - ps->PaintSynchronously(); + vm->ProcessPendingUpdates(); // Paint our popups. if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) { pm->PaintPopups(this); diff --git a/view/nsView.cpp b/view/nsView.cpp @@ -263,9 +263,9 @@ void nsView::WillPaintWindow(nsIWidget* aWidget) { vm->WillPaintWindow(aWidget); } -bool nsView::PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion) { +bool nsView::PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion aRegion) { RefPtr<nsViewManager> vm = mViewManager; - vm->PaintWindow(aWidget); + vm->Refresh(this, aRegion); return true; } diff --git a/view/nsViewManager.cpp b/view/nsViewManager.cpp @@ -54,7 +54,9 @@ uint32_t nsViewManager::gLastUserEventTime = 0; nsViewManager::nsViewManager() : mPresShell(nullptr), mDelayedResize(NSCOORD_NONE, NSCOORD_NONE), - mRootView(nullptr) {} + mRootView(nullptr), + mPainting(false), + mHasPendingWidgetGeometryChanges(false) {} nsViewManager::~nsViewManager() { if (mRootView) { @@ -169,28 +171,170 @@ nsViewManager* nsViewManager::GetParentViewManager() const { return nullptr; } -void nsViewManager::PaintWindow(nsIWidget* aWidget) { - RefPtr ps = mPresShell; - if (!ps) { +/** + 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 LayoutDeviceIntRegion& aRegion) { + NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager"); + + if (mPresShell && mPresShell->IsNeverPainting()) { return; } - RefPtr renderer = aWidget->GetWindowRenderer(); - if (renderer->NeedsWidgetInvalidation()) { - renderer->FlushRendering(wr::RenderReasons::WIDGET); - } else { - ps->SyncPaintFallback(ps->GetRootFrame(), renderer); + + if (aRegion.IsEmpty()) { + return; + } + + nsIWidget* widget = aView->GetWidget(); + if (!widget) { + return; + } + + MOZ_ASSERT(!IsPainting(), "recursive painting not permitted"); + if (NS_WARN_IF(IsPainting())) { + return; + } + + { + nsAutoScriptBlocker scriptBlocker; + SetPainting(true); + + if (RefPtr<PresShell> presShell = mPresShell) { +#ifdef MOZ_DUMP_PAINTING + if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { + printf_stderr("--COMPOSITE-- %p\n", presShell.get()); + } +#endif + RefPtr<WindowRenderer> renderer = widget->GetWindowRenderer(); + if (!renderer->NeedsWidgetInvalidation()) { + renderer->FlushRendering(wr::RenderReasons::WIDGET); + } else { + presShell->SyncPaintFallback(presShell->GetRootFrame(), renderer); + } +#ifdef MOZ_DUMP_PAINTING + if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { + printf_stderr("--ENDCOMPOSITE--\n"); + } +#endif + mozilla::StartupTimeline::RecordOnce( + mozilla::StartupTimeline::FIRST_PAINT); + } + + SetPainting(false); + } +} + +void nsViewManager::ProcessPendingUpdatesForView(nsView* aView, + bool aFlushDirtyRegion) { + NS_ASSERTION(IsRootVM(), "Updates will be missed"); + if (!aView) { + return; + } + + RefPtr<PresShell> rootPresShell = mPresShell; + AutoTArray<nsCOMPtr<nsIWidget>, 1> widgets; + aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets); + for (nsIWidget* widget : widgets) { + MOZ_ASSERT(widget->IsTopLevelWidget()); + if (RefPtr ps = widget->GetPresShell()) { + ps->SyncWindowProperties(); + } + } + if (rootPresShell->GetViewManager() != this) { + return; // presentation might have been torn down + } + if (aFlushDirtyRegion) { + nsAutoScriptBlocker scriptBlocker; + SetPainting(true); + for (nsIWidget* widget : widgets) { + if (RefPtr ps = widget->GetPresShell()) { + RefPtr vm = ps->GetViewManager(); + vm->ProcessPendingUpdatesPaint(MOZ_KnownLive(widget)); + } + } + SetPainting(false); + } +} + +void nsViewManager::ProcessPendingUpdatesRecurse( + nsView* aView, AutoTArray<nsCOMPtr<nsIWidget>, 1>& aWidgets) { + if (mPresShell && mPresShell->IsNeverPainting()) { + return; + } + + if (nsIWidget* widget = aView->GetWidget()) { + aWidgets.AppendElement(widget); + } +} + +void nsViewManager::ProcessPendingUpdatesPaint(nsIWidget* aWidget) { + if (!aWidget->NeedsPaint()) { + return; + } + // If an ancestor widget was hidden and then shown, we could + // have a delayed resize to handle. + if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) && mPresShell && + mPresShell->IsVisible()) { + FlushDelayedResize(); + } + + if (!mRootView || !mPresShell) { + NS_ERROR("FlushDelayedResize destroyed the view?"); + return; + } + + nsIWidgetListener* previousListener = + aWidget->GetPreviouslyAttachedWidgetListener(); + + if (previousListener && previousListener != mRootView && + mRootView->IsPrimaryFramePaintSuppressed()) { + return; + } + + RefPtr ps = mPresShell; +#ifdef MOZ_DUMP_PAINTING + if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { + printf_stderr("---- PAINT START ----PresShell(%p), nsIWidget(%p)\n", + ps.get(), aWidget); + } +#endif + + ps->PaintAndRequestComposite(ps->GetRootFrame(), aWidget->GetWindowRenderer(), + PaintFlags::None); + mRootView->SetForcedRepaint(false); + +#ifdef MOZ_DUMP_PAINTING + if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { + printf_stderr("---- PAINT END ----\n"); + } +#endif +} + +void nsViewManager::PostPendingUpdate() { + nsViewManager* rootVM = RootViewManager(); + rootVM->mHasPendingWidgetGeometryChanges = true; + if (rootVM->mPresShell) { + rootVM->mPresShell->SetNeedLayoutFlush(); + rootVM->mPresShell->SchedulePaint(); } - mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT); } void nsViewManager::WillPaintWindow(nsIWidget* aWidget) { - WindowRenderer* renderer = aWidget->GetWindowRenderer(); - if (renderer->NeedsWidgetInvalidation()) { + if (!aWidget) { return; } - if (RefPtr ps = mPresShell) { - // FIXME(bug 2002232): Why is this needed? - ps->PaintSynchronously(); + WindowRenderer* renderer = aWidget->GetWindowRenderer(); + if (mRootView && + (mRootView->ForcedRepaint() || !renderer->NeedsWidgetInvalidation())) { + ProcessPendingUpdates(); + // Re-get the view pointer here since the ProcessPendingUpdates might have + // destroyed it during CallWillPaintOnObservers. + if (mRootView) { + mRootView->SetForcedRepaint(false); + } } } @@ -225,3 +369,63 @@ void nsViewManager::ResizeView(nsView* aView, const nsSize& aSize) { // pointer, and this resize is implicitly changing the clip rect, it's OK // because layout will change it back again if necessary. } + +void nsViewManager::IsPainting(bool& aIsPainting) { + aIsPainting = IsPainting(); +} + +void nsViewManager::ProcessPendingUpdates() { + if (!IsRootVM()) { + RefPtr<nsViewManager> rootViewManager = RootViewManager(); + rootViewManager->ProcessPendingUpdates(); + return; + } + + // Flush things like reflows by calling WillPaint on observer presShells. + if (mPresShell) { + RefPtr<nsViewManager> strongThis(this); + CallWillPaintOnObservers(); + + ProcessPendingUpdatesForView(mRootView, true); + } +} + +void nsViewManager::UpdateWidgetGeometry() { + if (!IsRootVM()) { + RefPtr<nsViewManager> rootViewManager = RootViewManager(); + rootViewManager->UpdateWidgetGeometry(); + return; + } + + if (mHasPendingWidgetGeometryChanges) { + mHasPendingWidgetGeometryChanges = false; + ProcessPendingUpdatesForView(mRootView, false); + } +} + +/* static */ void nsViewManager::CollectVMsForWillPaint( + nsView* aView, nsViewManager* aParentVM, + nsTArray<RefPtr<nsViewManager>>& aVMs) { + nsViewManager* vm = aView->GetViewManager(); + if (vm != aParentVM) { + aVMs.AppendElement(vm); + } +} + +void nsViewManager::CallWillPaintOnObservers() { + MOZ_ASSERT(IsRootVM(), "Must be root VM for this to be called!"); + + if (!mRootView) { + return; + } + + AutoTArray<RefPtr<nsViewManager>, 2> VMs; + CollectVMsForWillPaint(mRootView, nullptr, VMs); + for (const auto& vm : VMs) { + if (vm->GetRootView()) { + if (RefPtr<PresShell> presShell = vm->GetPresShell()) { + presShell->WillPaint(); + } + } + } +} diff --git a/view/nsViewManager.h b/view/nsViewManager.h @@ -101,6 +101,14 @@ class nsViewManager final { public: /** + * Indicate whether the viewmanager is currently painting + * + * @param aPainting true if the viewmanager is painting + * false otherwise + */ + void IsPainting(bool& aIsPainting); + + /** * Retrieve the time of the last user event. User events * include mouse and keyboard events. The viewmanager * saves the time of the last user event. @@ -114,9 +122,28 @@ class nsViewManager final { */ MOZ_CAN_RUN_SCRIPT void ProcessPendingUpdates(); + /** + * Just update widget geometry without flushing the dirty region + */ + MOZ_CAN_RUN_SCRIPT void UpdateWidgetGeometry(); + + // Call this when you need to let the viewmanager know that it now has + // pending updates. + void PostPendingUpdate(); + private: static uint32_t gLastUserEventTime; + void FlushPendingInvalidates(); + + MOZ_CAN_RUN_SCRIPT + void ProcessPendingUpdatesForView(nsView* aView, + bool aFlushDirtyRegion = true); + void ProcessPendingUpdatesRecurse( + nsView* aView, AutoTArray<nsCOMPtr<nsIWidget>, 1>& aWidgets); + MOZ_CAN_RUN_SCRIPT + void ProcessPendingUpdatesPaint(nsIWidget* aWidget); + /** * Call WillPaint() on all view observers under this vm root. */ @@ -124,15 +151,24 @@ class nsViewManager final { static void CollectVMsForWillPaint(nsView* aView, nsViewManager* aParentVM, nsTArray<RefPtr<nsViewManager>>& aVMs); + // aView is the view for aWidget and aRegion is relative to aWidget. + MOZ_CAN_RUN_SCRIPT + void Refresh(nsView* aView, const LayoutDeviceIntRegion& aRegion); + MOZ_CAN_RUN_SCRIPT_BOUNDARY void DoSetWindowDimensions(const nsSize&); bool ShouldDelayResize() const; + bool IsPainting() const { return RootViewManager()->mPainting; } + + void SetPainting(bool aPainting) { RootViewManager()->mPainting = aPainting; } + nsViewManager* RootViewManager() const; nsViewManager* GetParentViewManager() const; bool IsRootVM() const { return !GetParentViewManager(); } MOZ_CAN_RUN_SCRIPT void WillPaintWindow(nsIWidget* aWidget); - MOZ_CAN_RUN_SCRIPT void PaintWindow(nsIWidget* aWidget); + MOZ_CAN_RUN_SCRIPT + bool PaintWindow(nsIWidget* aWidget, const LayoutDeviceIntRegion& aRegion); MOZ_CAN_RUN_SCRIPT void DidPaintWindow(); mozilla::PresShell* mPresShell; @@ -142,6 +178,14 @@ class nsViewManager final { nsSize mDelayedResize; nsView* mRootView; + + // The following members should not be accessed directly except by + // the root view manager. Some have accessor functions to enforce + // this, as noted. + // Use IsPainting() and SetPainting() to access mPainting. + bool mPainting; + bool mHasPendingWidgetGeometryChanges; + // from here to public should be static and locked... MMP }; diff --git a/xpfe/appshell/AppWindow.cpp b/xpfe/appshell/AppWindow.cpp @@ -1081,10 +1081,6 @@ void AppWindow::OnChromeLoaded() { mChromeLoaded = true; ApplyChromeFlags(); SyncAttributesToWidget(); - if (RefPtr ps = GetPresShell()) { - // Sync window properties now, before showing the window. - ps->SyncWindowPropertiesIfNeeded(); - } if (mWindow) { SizeShell(); if (mShowAfterLoad) {