tor-browser

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

commit 93b4c09f7e85f99a7cde190b99405cb98227fa07
parent 104e206a3af8ce5a2ee8213096c5accbc64bb5ed
Author: Emilio Cobos Álvarez <emilio@crisal.io>
Date:   Tue, 14 Oct 2025 12:00:34 +0000

Bug 1933181 - Stop using nsView for popups. r=layout-reviewers,tnikkel

Make the popup frame listener to the widget events itself, rather than
via the view tree.

This is a first step towards getting rid of views as a whole.

This creates the (annoying, but temporary) subtlety of the view tree
jumping across popups, so nsIFrame::GetNearestWidget and co need to
become a bit careful and avoid doing that.

Similarly a couple places relied on view visibility, which now need to
check nsMenuPopupFrame::IsOpen() as well.

Other than that this seems to just work.

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

Diffstat:
Maccessible/generic/LocalAccessible.cpp | 12+++++++-----
Mdom/base/nsContentUtils.cpp | 1+
Mdom/base/nsDOMWindowUtils.cpp | 1+
Mlayout/base/PresShell.cpp | 138++++++++++++++++++++++++++++++++++++-------------------------------------------
Mlayout/base/PresShell.h | 3---
Mlayout/base/nsDocumentViewer.cpp | 2++
Mlayout/base/nsLayoutUtils.cpp | 2+-
Mlayout/base/nsLayoutUtils.h | 2+-
Mlayout/base/nsRefreshDriver.cpp | 4++++
Mlayout/generic/FrameClasses.py | 2+-
Mlayout/generic/nsIFrame.cpp | 57+++++++++++++++++++++++++++++++++++++++++++++------------
Mlayout/xul/nsMenuPopupFrame.cpp | 266++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mlayout/xul/nsMenuPopupFrame.h | 43+++++++++++++++++++++++++++++++------------
Mlayout/xul/nsXULPopupManager.cpp | 172+++++++++++++++++++++++++++++++++----------------------------------------------
Mlayout/xul/nsXULPopupManager.h | 16++--------------
Mview/nsView.cpp | 335+++++++++++++++++++++++--------------------------------------------------------
Mview/nsView.h | 25++++++-------------------
Mview/nsViewManager.cpp | 27++++++++++++---------------
Mwidget/gtk/nsWindow.cpp | 23+++++++----------------
Mwidget/gtk/nsWindow.h | 3+--
Mwidget/nsBaseWidget.cpp | 16++++++----------
Mwidget/nsBaseWidget.h | 2--
Mwidget/nsIWidget.h | 8--------
Mwidget/nsIWidgetListener.h | 4++++
24 files changed, 538 insertions(+), 626 deletions(-)

diff --git a/accessible/generic/LocalAccessible.cpp b/accessible/generic/LocalAccessible.cpp @@ -17,6 +17,7 @@ #include "mozilla/a11y/Platform.h" #include "mozilla/FocusModel.h" #include "nsAccUtils.h" +#include "nsMenuPopupFrame.h" #include "nsAccessibilityService.h" #include "ApplicationAccessible.h" #include "nsGenericHTMLElement.h" @@ -316,13 +317,14 @@ uint64_t LocalAccessible::VisibilityState() const { // scrolled out. nsIFrame* curFrame = frame; do { - nsView* view = curFrame->GetView(); - if (view && view->GetVisibility() == ViewVisibility::Hide) { - return states::INVISIBLE; + if (nsView* view = curFrame->GetView()) { + if (view->GetVisibility() == ViewVisibility::Hide) { + return states::INVISIBLE; + } } - if (nsLayoutUtils::IsPopup(curFrame)) { - return 0; + if (nsMenuPopupFrame* popup = do_QueryFrame(curFrame)) { + return popup->IsOpen() ? 0 : states::INVISIBLE; } if (curFrame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) { diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp @@ -117,6 +117,7 @@ #include "mozilla/StaticPrefs_dom.h" #include "mozilla/extensions/WebExtensionPolicy.h" #include "nsIOService.h" +#include "nsMenuPopupFrame.h" #include "nsObjectLoadingContent.h" #ifdef FUZZING # include "mozilla/StaticPrefs_fuzzing.h" diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp @@ -67,6 +67,7 @@ #include "nsJSEnvironment.h" #include "nsJSUtils.h" #include "nsLayoutUtils.h" +#include "nsMenuPopupFrame.h" #include "nsPresContext.h" #include "nsQueryContentEventResult.h" #include "nsQueryObject.h" diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp @@ -4146,20 +4146,6 @@ void PresShell::SchedulePaint() { } } -void PresShell::DispatchSynthMouseOrPointerMove( - WidgetMouseEvent* aMouseOrPointerMoveEvent) { - AUTO_PROFILER_TRACING_MARKER_DOCSHELL("Paint", - "DispatchSynthMouseOrPointerMove", - GRAPHICS, mPresContext->GetDocShell()); - nsEventStatus status = nsEventStatus_eIgnore; - nsView* targetView = nsView::GetViewFor(aMouseOrPointerMoveEvent->mWidget); - if (!targetView) { - return; - } - RefPtr<nsViewManager> viewManager = targetView->GetViewManager(); - viewManager->DispatchEvent(aMouseOrPointerMoveEvent, targetView, &status); -} - void PresShell::ClearMouseCaptureOnView(nsView* aView) { if (nsIContent* capturingContent = GetCapturingContent()) { if (aView) { @@ -5910,13 +5896,12 @@ void PresShell::SynthesizeMouseMove(bool aFromScroll) { } } -static nsView* FindFloatingViewContaining(nsPresContext* aRootPresContext, - nsIWidget* aRootWidget, - const LayoutDeviceIntPoint& aPt) { - nsIFrame* popupFrame = nsLayoutUtils::GetPopupFrameForPoint( +static nsMenuPopupFrame* FindPopupFrame(nsPresContext* aRootPresContext, + nsIWidget* aRootWidget, + const LayoutDeviceIntPoint& aPt) { + return nsLayoutUtils::GetPopupFrameForPoint( aRootPresContext, aRootWidget, aPt, nsLayoutUtils::GetPopupFrameForPointFlags::OnlyReturnFramesWithWidgets); - return popupFrame ? popupFrame->GetView() : nullptr; } /* @@ -5925,8 +5910,7 @@ static nsView* FindFloatingViewContaining(nsPresContext* aRootPresContext, * floating view. It assumes that only floating views extend outside the bounds * of their parents. * - * This methods should only be called if FindFloatingViewContaining returns - * null. + * This methods should only be called if FindPopupFrame returns null. * * aPt is relative aRelativeToView with the viewport type * aRelativeToViewportType. aRelativeToView will always have a frame. If aView @@ -6123,74 +6107,69 @@ void PresShell::ProcessSynthMouseOrPointerMoveEvent( int32_t APD = mPresContext->AppUnitsPerDevPixel(); - // We need a widget to put in the event we are going to dispatch so we look - // for a view that has a widget and the mouse location is over. We first look - // for floating views, if there isn't one we use the root view. |view| holds - // that view. - nsView* view = nullptr; - - // The appunits per devpixel ratio of |view|. - int32_t viewAPD; - // mRefPoint will be mMouseLocation relative to the widget of |view|, the - // widget we will put in the event we dispatch, in viewAPD appunits + // widget we will put in the event we dispatch, in widgetAPD appunits nsPoint refpoint(0, 0); - // We always dispatch the event to the pres shell that contains the view that - // the mouse is over. pointVM is the VM of that pres shell. - nsViewManager* pointVM = nullptr; - nsView* const rootView = mViewManager ? mViewManager->GetRootView() : nullptr; if (!rootView || !rootView->HasWidget()) { return; } -#ifdef DEBUG - nsCOMPtr<nsIDragSession> dragSession = - nsContentUtils::GetDragSession(rootView->GetWidget()); - MOZ_ASSERT(!dragSession); -#endif + MOZ_ASSERT(!nsCOMPtr{nsContentUtils::GetDragSession(rootView->GetWidget())}); + // We need a widget to put in the event we are going to dispatch so we look + // for a view that has a widget and the mouse location is over. We first look + // for floating views, if there isn't one we use the root view. |view| holds + // that view. + nsCOMPtr<nsIWidget> widget; + // We always dispatch the event to the pres shell that contains the view that + // the mouse is over. pointShell is that. + RefPtr<PresShell> pointShell; + // The appunits per devpixel ratio of |widget|. + int32_t widgetAPD; + // If we're in a child process and the view points to an OOP iframe, this is + // its BrowserBridgeChild. + RefPtr<BrowserBridgeChild> bbc; + + // We either dispatch the event to a popup, or a view. + nsMenuPopupFrame* popupFrame = nullptr; if (rootView->GetFrame()) { - view = - FindFloatingViewContaining(mPresContext, rootView->GetWidget(), - LayoutDeviceIntPoint::FromAppUnitsToNearest( - aPointerInfo.mLastRefPointInRootDoc + - rootView->ViewToWidgetOffset(), - APD)); - } - - nsView* pointView = view; - if (!view) { - view = rootView; + popupFrame = FindPopupFrame(mPresContext, rootView->GetWidget(), + LayoutDeviceIntPoint::FromAppUnitsToNearest( + aPointerInfo.mLastRefPointInRootDoc + + rootView->ViewToWidgetOffset(), + APD)); + if (popupFrame) { + pointShell = popupFrame->PresShell(); + widget = popupFrame->GetWidget(); + widgetAPD = popupFrame->PresContext()->AppUnitsPerDevPixel(); + refpoint = aPointerInfo.mLastRefPointInRootDoc; + DebugOnly<nsLayoutUtils::TransformResult> result = + nsLayoutUtils::TransformPoint( + RelativeTo{rootView->GetFrame(), ViewportType::Visual}, + RelativeTo{popupFrame, ViewportType::Layout}, refpoint); + MOZ_ASSERT(result == nsLayoutUtils::TRANSFORM_SUCCEEDED); + } + } + if (!widget) { + widget = rootView->GetWidget(); + widgetAPD = APD; + nsView* pointView = rootView; if (rootView->GetFrame()) { pointView = FindViewContaining(rootView, ViewportType::Visual, rootView, aPointerInfo.mLastRefPointInRootDoc); - } else { - pointView = rootView; } // pointView can be null in situations related to mouse capture - pointVM = (pointView ? pointView : view)->GetViewManager(); + pointShell = (pointView ? pointView : rootView)->GetPresShell(); + bbc = GetChildBrowser(pointView); refpoint = aPointerInfo.mLastRefPointInRootDoc + rootView->ViewToWidgetOffset(); - viewAPD = APD; - } else { - pointVM = view->GetViewManager(); - nsIFrame* frame = view->GetFrame(); - NS_ASSERTION(frame, "floating views can't be anonymous"); - viewAPD = frame->PresContext()->AppUnitsPerDevPixel(); - refpoint = aPointerInfo.mLastRefPointInRootDoc; - DebugOnly<nsLayoutUtils::TransformResult> result = - nsLayoutUtils::TransformPoint( - RelativeTo{rootView->GetFrame(), ViewportType::Visual}, - RelativeTo{frame, ViewportType::Layout}, refpoint); - MOZ_ASSERT(result == nsLayoutUtils::TRANSFORM_SUCCEEDED); - refpoint += view->ViewToWidgetOffset(); - } - NS_ASSERTION(view->GetWidget(), "view should have a widget here"); + } + NS_ASSERTION(widget, "view should have a widget here"); Maybe<WidgetMouseEvent> mouseMoveEvent; Maybe<WidgetPointerEvent> pointerMoveEvent; if (aMoveMessage == eMouseMove) { - mouseMoveEvent.emplace(true, eMouseMove, view->GetWidget(), + mouseMoveEvent.emplace(true, eMouseMove, widget, WidgetMouseEvent::eSynthesized); mouseMoveEvent->mButton = MouseButton::ePrimary; // We don't want to dispatch preceding pointer event since the caller @@ -6198,7 +6177,7 @@ void PresShell::ProcessSynthMouseOrPointerMoveEvent( // iframe, we'll set this to true again below. mouseMoveEvent->convertToPointer = false; } else { - pointerMoveEvent.emplace(true, ePointerMove, view->GetWidget()); + pointerMoveEvent.emplace(true, ePointerMove, widget); pointerMoveEvent->mButton = MouseButton::eNotPressed; pointerMoveEvent->mReason = WidgetMouseEvent::eSynthesized; } @@ -6214,13 +6193,13 @@ void PresShell::ProcessSynthMouseOrPointerMoveEvent( event.mFlags.mIsSynthesizedForTests = aPointerInfo.mIsSynthesizedForTests; event.mRefPoint = - LayoutDeviceIntPoint::FromAppUnitsToNearest(refpoint, viewAPD); + LayoutDeviceIntPoint::FromAppUnitsToNearest(refpoint, widgetAPD); event.mButtons = aPointerInfo.mLastButtons; event.mInputSource = aPointerInfo.mInputSource; event.pointerId = aPointerId; event.mModifiers = PresShell::GetCurrentModifiers(); - if (BrowserBridgeChild* bbc = GetChildBrowser(pointView)) { + if (bbc) { // If we have a BrowserBridgeChild, we're going to be dispatching this // mouse event into an OOP iframe of the current document if and only if // we're synthesizing a mouse move. @@ -6237,7 +6216,7 @@ void PresShell::ProcessSynthMouseOrPointerMoveEvent( return; } - if (RefPtr<PresShell> presShell = pointVM->GetPresShell()) { + if (pointShell) { // Since this gets run in a refresh tick there isn't an InputAPZContext on // the stack from the nsBaseWidget. We need to simulate one with at least // the correct target guid, so that the correct callback transform gets @@ -6246,7 +6225,16 @@ void PresShell::ProcessSynthMouseOrPointerMoveEvent( // input block. Same for the APZ response field. InputAPZContext apzContext(aPointerInfo.mLastTargetGuid, 0, nsEventStatus_eIgnore); - presShell->DispatchSynthMouseOrPointerMove(&event); + AUTO_PROFILER_TRACING_MARKER_DOCSHELL( + "Paint", "DispatchSynthMouseOrPointerMove", GRAPHICS, + pointShell->GetPresContext()->GetDocShell()); + nsEventStatus status = nsEventStatus_eIgnore; + if (popupFrame) { + pointShell->HandleEvent(popupFrame, &event, false, &status); + } else { + RefPtr<nsViewManager> viewManager = rootView->GetViewManager(); + viewManager->DispatchEvent(&event, rootView, &status); + } } } diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h @@ -1091,9 +1091,6 @@ class PresShell final : public nsStubDocumentObserver, mUnderHiddenEmbedderElement = aUnderHiddenEmbedderElement; } - MOZ_CAN_RUN_SCRIPT void DispatchSynthMouseOrPointerMove( - WidgetMouseEvent* aMouseOrPointerMoveEvent); - /* Temporarily ignore the Displayport for better paint performance. We * trigger a repaint once suppression is disabled. Without that * the displayport may get left at the suppressed size for an extended diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp @@ -3245,6 +3245,8 @@ bool nsDocumentViewer::ShouldAttachToTopLevel() { nsIWidgetListener* parentListener = mParentWidget->GetWidgetListener(); MOZ_ASSERT(!parentListener || !parentListener->GetView(), "Expect a top level widget"); + MOZ_ASSERT(!parentListener || !parentListener->GetAsMenuPopupFrame(), + "Expect a top level widget"); #endif return true; } diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp @@ -1582,7 +1582,7 @@ nsIFrame* nsLayoutUtils::GetPopupFrameForEventCoordinates( guiEvent->mRefPoint); } -nsIFrame* nsLayoutUtils::GetPopupFrameForPoint( +nsMenuPopupFrame* nsLayoutUtils::GetPopupFrameForPoint( nsPresContext* aRootPresContext, nsIWidget* aWidget, const mozilla::LayoutDeviceIntPoint& aPoint, GetPopupFrameForPointFlags aFlags /* = GetPopupFrameForPointFlags(0) */) { diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h @@ -719,7 +719,7 @@ class nsLayoutUtils { enum class GetPopupFrameForPointFlags : uint8_t { OnlyReturnFramesWithWidgets = 0x1, }; - static nsIFrame* GetPopupFrameForPoint( + static nsMenuPopupFrame* GetPopupFrameForPoint( nsPresContext* aRootPresContext, nsIWidget* aWidget, const mozilla::LayoutDeviceIntPoint& aPoint, GetPopupFrameForPointFlags aFlags = GetPopupFrameForPointFlags(0)); diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp @@ -2638,6 +2638,10 @@ bool nsRefreshDriver::PaintIfNeeded() { { PaintTelemetry::AutoRecordPaint record; vm->ProcessPendingUpdates(); + // Paint our popups. + if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) { + pm->PaintPopups(this); + } } return true; } diff --git a/layout/generic/FrameClasses.py b/layout/generic/FrameClasses.py @@ -95,7 +95,7 @@ FRAME_CLASSES = [ Frame("nsMathMLmtrFrame", "TableRow", TABLE_PART | MATHML), Frame("nsMathMLmunderoverFrame", "None", MATHML_CONTAINER), Frame("nsMathMLTokenFrame", "None", MATHML_CONTAINER), - Frame("nsMenuPopupFrame", "MenuPopup", BLOCK | MAY_HAVE_VIEW), + Frame("nsMenuPopupFrame", "MenuPopup", BLOCK), Frame("nsNumberControlFrame", "TextInput", REPLACED | LEAF), Frame("nsPageBreakFrame", "PageBreak", COMMON | LEAF), Frame("nsPageContentFrame", "PageContent", BLOCK), diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp @@ -89,6 +89,7 @@ #include "nsImageFrame.h" #include "nsInlineFrame.h" #include "nsLayoutUtils.h" +#include "nsMenuPopupFrame.h" #include "nsNameSpaceManager.h" #include "nsPlaceholderFrame.h" #include "nsPresContext.h" @@ -351,9 +352,14 @@ bool nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const { // involves loading more memory. It's only allowed in chrome sheets so let's // only support it in the parent process so we can mostly optimize this out // in content processes. - if (XRE_IsParentProcess() && - frame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) { - return false; + if (XRE_IsParentProcess()) { + if (const nsMenuPopupFrame* popup = do_QueryFrame(frame); + popup && !popup->IsOpen()) { + return false; + } + if (frame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) { + return false; + } } // This method is used to determine if a frame is focusable, because it's @@ -937,8 +943,7 @@ void nsIFrame::Destroy(DestroyContext& aContext) { ps->ClearFrameRefs(this); } - nsView* view = GetView(); - if (view) { + if (nsView* view = GetView()) { view->SetFrame(nullptr); view->Destroy(); } @@ -7932,16 +7937,44 @@ void nsIFrame::GetOffsetFromView(nsPoint& aOffset, nsView** aView) const { } nsIWidget* nsIFrame::GetNearestWidget() const { - return GetClosestView()->GetNearestWidget(nullptr); + if (!HasAnyStateBits(NS_FRAME_IN_POPUP)) { + return PresShell()->GetViewManager()->GetRootWidget(); + } + nsPoint unused; + return GetNearestWidget(unused); } nsIWidget* nsIFrame::GetNearestWidget(nsPoint& aOffset) const { - nsPoint offsetToView; - nsPoint offsetToWidget; - nsIWidget* widget = - GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget); - aOffset = offsetToView + offsetToWidget; - return widget; + aOffset.MoveTo(0, 0); + nsIFrame* frame = const_cast<nsIFrame*>(this); + do { + if (frame->IsMenuPopupFrame()) { + return static_cast<nsMenuPopupFrame*>(frame)->GetWidget(); + } + if (auto* view = frame->GetView()) { + // NOTE(emilio): NS_FRAME_IN_POPUP gets propagated across documents, so we + // just need to be careful to not jump to the view hierarchy in this case, + // since popups are not part of it. + if (!frame->HasAnyStateBits(NS_FRAME_IN_POPUP)) { + nsPoint offsetToWidget; + nsIWidget* widget = view->GetNearestWidget(&offsetToWidget); + aOffset += offsetToWidget; + return widget; + } + MOZ_ASSERT(!view->GetWidget(), + "Shouldn't have a nested widget inside a popup"); + } + aOffset += frame->GetPosition(); + nsPoint crossDocOffset; + frame = + nsLayoutUtils::GetCrossDocParentFrameInProcess(frame, &crossDocOffset); + aOffset += crossDocOffset; + MOZ_ASSERT(frame, "Root frame should have a widget"); + if (!frame) { + break; + } + } while (true); + return PresShell()->GetViewManager()->GetRootWidget(); } Matrix4x4Flagged nsIFrame::GetTransformMatrix(ViewportType aViewportType, diff --git a/layout/xul/nsMenuPopupFrame.cpp b/layout/xul/nsMenuPopupFrame.cpp @@ -9,6 +9,7 @@ #include <algorithm> #include "LayoutConstants.h" +#include "WindowRenderer.h" #include "X11UndefineNone.h" #include "XULButtonElement.h" #include "XULPopupElement.h" @@ -171,10 +172,6 @@ void nsMenuPopupFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, nsIFrame* aPrevInFlow) { nsBlockFrame::Init(aContent, aParent, aPrevInFlow); - CreatePopupView(); - - nsView* ourView = GetView(); - const auto& el = PopupElement(); mPopupType = PopupType::Panel; if (el.IsMenu()) { @@ -198,8 +195,10 @@ void nsMenuPopupFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, } } - if (!ourView->HasWidget() && ShouldHaveWidgetWhenHidden()) { - CreateWidgetForView(ourView); + // To improve performance, create the widget for the popup if needed. Popups + // such as menus will create their widgets later when the popup opens. + if (!mWidget && ShouldHaveWidgetWhenHidden()) { + CreateWidget(); } AddStateBits(NS_FRAME_IN_POPUP); @@ -262,19 +261,15 @@ void nsMenuPopupFrame::PrepareWidget(bool aForceRecreate) { if (mExpirationState.IsTracked()) { PopupExpirationTracker::Get()->RemoveObject(this); } - nsView* ourView = GetView(); if (auto* widget = GetWidget()) { nsCOMPtr<nsIWidget> parent = ComputeParentWidget(); if (aForceRecreate || widget->GetParent() != parent || widget->NeedsRecreateToReshow()) { - // Widget's WebRender resources needs to be cleared before creating new - // widget. - widget->ClearCachedWebrenderResources(); - ourView->DestroyWidget(); + DestroyWidget(); } } - if (!ourView->HasWidget()) { - CreateWidgetForView(ourView); + if (!mWidget) { + CreateWidget(); } else { PropagateStyleToWidget(); } @@ -302,13 +297,13 @@ already_AddRefed<nsIWidget> nsMenuPopupFrame::ComputeParentWidget() const { parentWidget = baseWindow->GetMainWidget(); } } - if (!parentWidget && mView && mView->GetParent()) { - parentWidget = mView->GetParent()->GetNearestWidget(nullptr); + if (!parentWidget) { + parentWidget = GetParent()->GetNearestWidget(); } return parentWidget.forget(); } -nsresult nsMenuPopupFrame::CreateWidgetForView(nsView* aView) { +void nsMenuPopupFrame::CreateWidget() { // Create a widget for ourselves. widget::InitData widgetData; widgetData.mWindowType = widget::WindowType::Popup; @@ -326,21 +321,36 @@ nsresult nsMenuPopupFrame::CreateWidgetForView(nsView* aView) { nsCOMPtr<nsIWidget> parentWidget = ComputeParentWidget(); if (NS_WARN_IF(!parentWidget)) { - return NS_ERROR_FAILURE; + return; } - nsresult rv = aView->CreateWidgetForPopup(&widgetData, parentWidget); - if (NS_FAILED(rv)) { - return rv; + mWidget = parentWidget->CreateChild(CalcWidgetBounds(), widgetData); + if (NS_WARN_IF(!mWidget)) { + return; } - - nsIWidget* widget = aView->GetWidget(); - MOZ_ASSERT(widget->GetParent() == parentWidget); - - widget->SetTransparencyMode(mode); + mWidget->SetWidgetListener(this); PropagateStyleToWidget(); +} - return NS_OK; +LayoutDeviceIntRect nsMenuPopupFrame::CalcWidgetBounds() const { + return nsView::CalcWidgetBounds( + GetRect(), PresContext()->AppUnitsPerDevPixel(), + PresShell()->GetViewManager()->GetRootView(), nullptr, + widget::WindowType::Popup, + nsLayoutUtils::GetFrameTransparency(this, this)); +} + +void nsMenuPopupFrame::DestroyWidget() { + RefPtr widget = mWidget.forget(); + if (!widget) { + return; + } + // Widget's WebRender resources needs to be cleared before creating new + // widget. + widget->ClearCachedWebrenderResources(); + widget->SetWidgetListener(nullptr); + NS_DispatchToMainThread( + NewRunnableMethod("DestroyWidget", widget, &nsIWidget::Destroy)); } void nsMenuPopupFrame::PropagateStyleToWidget(WidgetStyleFlags aFlags) const { @@ -647,20 +657,17 @@ void nsMenuPopupFrame::LayoutPopup(nsPresContext* aPresContext, aStatus); } + if (mIsOpenChanged || !mRect.IsEqualEdges(constraints.mUsedRect)) { + SchedulePendingWidgetMoveResize(); + } + // Set our size, since AbsoluteContainingBlock won't. SetRect(constraints.mUsedRect); - nsView* view = GetView(); if (isOpen) { - nsViewManager* viewManager = view->GetViewManager(); - viewManager->ResizeView(view, - nsRect(nsPoint(), constraints.mUsedRect.Size())); if (mPopupState == ePopupOpening) { mPopupState = ePopupVisible; } - - viewManager->SetViewVisibility(view, ViewVisibility::Show); - SyncFrameViewProperties(view); } // Perform our move now. That will position the view and so on. @@ -980,6 +987,7 @@ void nsMenuPopupFrame::ShowPopup(bool aIsContextMenu) { mIsContextMenu = aIsContextMenu; InvalidateFrameSubtree(); + SchedulePendingWidgetMoveResize(); if (mPopupState == ePopupShowing || mPopupState == ePopupPositioning) { mPopupState = ePopupOpening; @@ -1074,12 +1082,11 @@ void nsMenuPopupFrame::HidePopup(bool aDeselectMenu, nsPopupState aNewState, if (!aFromFrameDestruction && !ShouldHaveWidgetWhenHidden()) { PopupExpirationTracker::GetOrCreate().AddObject(this); } + NS_DispatchToMainThread( + NewRunnableMethod<bool>("HideWidget", widget, &nsIWidget::Show, false)); } - nsView* view = GetView(); - nsViewManager* viewManager = view->GetViewManager(); - viewManager->SetViewVisibility(view, ViewVisibility::Hide); - + ClearPendingWidgetMoveResize(); RefPtr popup = &PopupElement(); // XXX, bug 137033, In Windows, if mouse is outside the window when the // menupopup closes, no mouse_enter/mouse_exit event will be fired to clear @@ -1093,6 +1100,14 @@ void nsMenuPopupFrame::HidePopup(bool aDeselectMenu, nsPopupState aNewState, popup->PopupClosed(aDeselectMenu); } +void nsMenuPopupFrame::SchedulePendingWidgetMoveResize() { + if (mPendingWidgetMoveResize) { + return; + } + mPendingWidgetMoveResize = true; + SchedulePaint(); +} + nsPoint nsMenuPopupFrame::AdjustPositionForAnchorAlign( nsRect& anchorRect, const nsSize& aPrefSize, FlipStyle& aHFlip, FlipStyle& aVFlip) const { @@ -1456,9 +1471,6 @@ auto nsMenuPopupFrame::GetRects(const nsSize& aPrefSize) const -> Rects { nsPresContext* pc = PresContext(); nsIFrame* rootFrame = pc->PresShell()->GetRootFrame(); - NS_ASSERTION(rootFrame->GetView() && GetView() && - rootFrame->GetView() == GetView()->GetParent(), - "rootFrame's view is not our view's parent???"); // Indicators of whether the popup should be flipped or resized. FlipStyle hFlip = FlipStyle::None; @@ -1558,10 +1570,7 @@ auto nsMenuPopupFrame::GetRects(const nsSize& aPrefSize) const -> Rects { const int32_t a2d = pc->AppUnitsPerDevPixel(); - nsView* view = GetView(); - NS_ASSERTION(view, "popup with no view"); - - nsIWidget* widget = view->GetWidget(); + nsIWidget* widget = mWidget; // If a panel has flip="none", don't constrain or flip it. // Also, always do this for content shells, so that the popup doesn't extend @@ -1722,13 +1731,14 @@ void nsMenuPopupFrame::SetPopupPosition(bool aIsMove) { void nsMenuPopupFrame::PerformMove(const Rects& aRects) { auto* ps = PresShell(); - // We're just moving, sync frame position and offset as needed. - ps->GetViewManager()->MoveViewTo(GetView(), aRects.mViewPoint.x, - aRects.mViewPoint.y); - // Now that we've positioned the view, sync up the frame's origin. - nsBlockFrame::SetPosition(aRects.mViewPoint - - GetParent()->GetOffsetTo(ps->GetRootFrame())); + const nsPoint oldPos = mRect.TopLeft(); + const nsPoint newPos = + aRects.mViewPoint - GetParent()->GetOffsetTo(ps->GetRootFrame()); + nsBlockFrame::SetPosition(newPos); + if (oldPos != newPos) { + SchedulePendingWidgetMoveResize(); + } // If the popup is in the positioned state or if it is shown and the position // or size changed, dispatch a popuppositioned event if the popup wants it. @@ -2081,9 +2091,7 @@ XULButtonElement* nsMenuPopupFrame::FindMenuWithShortcut( return nullptr; } -nsIWidget* nsMenuPopupFrame::GetWidget() const { - return mView ? mView->GetWidget() : nullptr; -} +nsIWidget* nsMenuPopupFrame::GetWidget() const { return mWidget.get(); } // helpers ///////////////////////////////////////////////////////////// @@ -2158,7 +2166,6 @@ void nsMenuPopupFrame::Destroy(DestroyContext& aContext) { // but alas, also pre-existing. HidePopup(/* aDeselectMenu = */ false, ePopupClosed, /* aFromFrameDestruction = */ true); - if (mExpirationState.IsTracked()) { PopupExpirationTracker::Get()->RemoveObject(this); } @@ -2167,6 +2174,7 @@ void nsMenuPopupFrame::Destroy(DestroyContext& aContext) { pm->PopupDestroyed(this); } + DestroyWidget(); nsBlockFrame::Destroy(aContext); } @@ -2210,9 +2218,7 @@ void nsMenuPopupFrame::DestroyWidgetIfNeeded() { MOZ_ASSERT_UNREACHABLE("Shouldn't be tracked while visible"); return; } - if (auto* view = GetView()) { - view->DestroyWidget(); - } + DestroyWidget(); } void nsMenuPopupFrame::MoveTo(const CSSPoint& aPos, bool aUpdateAttrs, @@ -2340,37 +2346,6 @@ int8_t nsMenuPopupFrame::GetAlignmentPosition() const { return position; } -/** - * KEEP THIS IN SYNC WITH nsIFrame::CreateView - * as much as possible. Until we get rid of views finally... - */ -void nsMenuPopupFrame::CreatePopupView() { - if (mView) { - return; - } - - nsViewManager* viewManager = PresContext()->GetPresShell()->GetViewManager(); - NS_ASSERTION(viewManager, "null view manager"); - - // Create a view - nsView* parentView = viewManager->GetRootView(); - auto visibility = ViewVisibility::Hide; - - NS_ASSERTION(parentView, "no parent view"); - - // Create a view - nsView* view = viewManager->CreateView(GetRect(), parentView, visibility); - // XXX put view last in document order until we can do better - viewManager->InsertChild(parentView, view, nullptr, true); - - // Remember our view - SetView(view); - - NS_FRAME_LOG( - NS_FRAME_TRACE_CALLS, - ("nsMenuPopupFrame::CreatePopupView: frame=%p view=%p", this, view)); -} - bool nsMenuPopupFrame::ShouldFollowAnchor() const { if (mAnchorType != MenuPopupAnchorType::Node || !mAnchorContent) { return false; @@ -2457,8 +2432,7 @@ void nsMenuPopupFrame::CheckForAnchorChange(nsRect& aRect) { } if (shouldHide) { - nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); - if (pm) { + if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) { // As the caller will be iterating over the open popups, hide // asyncronously. pm->HidePopup(mContent->AsElement(), @@ -2476,3 +2450,115 @@ void nsMenuPopupFrame::CheckForAnchorChange(nsRect& aRect) { SetPopupPosition(true); } } + +bool nsMenuPopupFrame::WindowMoved(nsIWidget* aWidget, int32_t aX, int32_t aY, + ByMoveToRect aByMoveToRect) { + MOZ_ASSERT(aWidget == mWidget); + + if (!IsVisibleOrShowing()) { + return true; + } + + LayoutDeviceIntPoint point(aX, aY); + + // Don't do anything if the popup is already at the specified location. This + // prevents recursive calls when a popup is positioned. + LayoutDeviceIntRect curDevBounds = CalcWidgetBounds(); + if (curDevBounds.TopLeft() == point && + aWidget->GetClientOffset() == GetLastClientOffset()) { + return true; + } + + // Update the popup's position using SetPopupPosition if the popup is + // anchored and at the parent level as these maintain their position + // relative to the parent window (except if positioned by move to rect, in + // which case we better make sure that layout matches that). Otherwise, just + // update the popup to the specified screen coordinates. + if (IsAnchored() && GetPopupLevel() == widget::PopupLevel::Parent && + aByMoveToRect == ByMoveToRect::No) { + SetPopupPosition(true); + } else { + CSSPoint cssPos = point / PresContext()->CSSToDevPixelScale(); + MoveTo(cssPos, false, aByMoveToRect == ByMoveToRect::Yes); + } + return true; +} + +bool nsMenuPopupFrame::WindowResized(nsIWidget* aWidget, int32_t aWidth, + int32_t aHeight) { + MOZ_ASSERT(aWidget == mWidget); + if (!IsVisibleOrShowing()) { + return true; + } + + LayoutDeviceIntSize size(aWidth, aHeight); + const LayoutDeviceIntRect curDevBounds = CalcWidgetBounds(); + // If the size is what we think it is, we have nothing to do. + if (curDevBounds.Size() == size) { + return true; + } + + RefPtr<Element> popup = &PopupElement(); + + // Only set the width and height if the popup already has these attributes. + if (!popup->HasAttr(nsGkAtoms::width) || !popup->HasAttr(nsGkAtoms::height)) { + return true; + } + + // The size is different. Convert the actual size to css pixels and store it + // as 'width' and 'height' attributes on the popup. + nsPresContext* presContext = PresContext(); + + CSSIntSize newCSS(presContext->DevPixelsToIntCSSPixels(size.width), + presContext->DevPixelsToIntCSSPixels(size.height)); + + nsAutoString width, height; + width.AppendInt(newCSS.width); + height.AppendInt(newCSS.height); + popup->SetAttr(kNameSpaceID_None, nsGkAtoms::width, width, true); + popup->SetAttr(kNameSpaceID_None, nsGkAtoms::height, height, true); + return true; +} + +bool nsMenuPopupFrame::RequestWindowClose(nsIWidget* aWidget) { + MOZ_ASSERT(aWidget == mWidget); + if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) { + pm->HidePopup(&PopupElement(), {HidePopupOption::DeselectMenu}); + return true; + } + return false; +} + +nsEventStatus nsMenuPopupFrame::HandleEvent(mozilla::WidgetGUIEvent* aEvent, + bool aUseAttachedEvents) { + MOZ_ASSERT(aEvent->mWidget); + MOZ_ASSERT(aEvent->mWidget == mWidget); + nsEventStatus status = nsEventStatus_eIgnore; + RefPtr ps = PresShell(); + ps->HandleEvent(this, aEvent, false, &status); + return status; +} + +bool nsMenuPopupFrame::PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion) { + MOZ_ASSERT(aWidget == mWidget); + nsAutoScriptBlocker scriptBlocker; + RefPtr ps = PresShell(); + RefPtr<WindowRenderer> renderer = aWidget->GetWindowRenderer(); + if (!renderer->NeedsWidgetInvalidation()) { + renderer->FlushRendering(wr::RenderReasons::WIDGET); + } else { + ps->SyncPaintFallback(this, renderer); + } + return true; +} + +void nsMenuPopupFrame::DidCompositeWindow( + mozilla::layers::TransactionId aTransactionId, + const TimeStamp& aCompositeStart, const TimeStamp& aCompositeEnd) { + RefPtr rootPc = PresContext()->GetRootPresContext(); + if (!rootPc) { + return; + } + nsAutoScriptBlocker scriptBlocker; + rootPc->NotifyDidPaintForSubtree(aTransactionId, aCompositeEnd); +} diff --git a/layout/xul/nsMenuPopupFrame.h b/layout/xul/nsMenuPopupFrame.h @@ -21,6 +21,7 @@ #include "nsCOMPtr.h" #include "nsExpirationState.h" #include "nsIDOMEventListener.h" +#include "nsIWidgetListener.h" #include "nsXULPopupManager.h" class nsIWidget; @@ -102,7 +103,6 @@ enum class MenuPopupAnchorType : uint8_t { nsIFrame* NS_NewMenuPopupFrame(mozilla::PresShell* aPresShell, mozilla::ComputedStyle* aStyle); -class nsView; class nsMenuPopupFrame; // this class is used for dispatching popupshown events asynchronously. @@ -128,7 +128,7 @@ class nsXULPopupShownEvent final : public mozilla::Runnable, const RefPtr<nsPresContext> mPresContext; }; -class nsMenuPopupFrame final : public nsBlockFrame { +class nsMenuPopupFrame final : public nsBlockFrame, public nsIWidgetListener { using PopupLevel = mozilla::widget::PopupLevel; using PopupType = mozilla::widget::PopupType; @@ -197,6 +197,23 @@ class nsMenuPopupFrame final : public nsBlockFrame { nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, AttrModType aModType) override; + // nsIWidgetListener + mozilla::PresShell* GetPresShell() override { return PresShell(); } + nsMenuPopupFrame* GetAsMenuPopupFrame() override { return this; } + bool WindowMoved(nsIWidget*, int32_t aX, int32_t aY, ByMoveToRect) override; + bool WindowResized(nsIWidget*, int32_t aWidth, int32_t aHeight) override; + bool RequestWindowClose(nsIWidget*) override; + MOZ_CAN_RUN_SCRIPT_BOUNDARY + nsEventStatus HandleEvent(mozilla::WidgetGUIEvent* aEvent, + bool aUseAttachedEvents) override; + MOZ_CAN_RUN_SCRIPT_BOUNDARY + bool PaintWindow(nsIWidget* aWidget, mozilla::LayoutDeviceIntRegion) override; + void DidCompositeWindow(mozilla::layers::TransactionId aTransactionId, + const mozilla::TimeStamp& aCompositeStart, + const mozilla::TimeStamp& aCompositeEnd) override; + bool ShouldNotBeVisible() override { return !IsOpen(); } + using nsIFrame::HandleEvent; // Needed to silence warning. + // FIXME: This shouldn't run script (this can end up calling HidePopup). MOZ_CAN_RUN_SCRIPT_BOUNDARY void Destroy(DestroyContext&) override; @@ -225,7 +242,8 @@ class nsMenuPopupFrame final : public nsBlockFrame { MOZ_CAN_RUN_SCRIPT void EnsureActiveMenuListItemIsVisible(); - nsresult CreateWidgetForView(nsView* aView); + void CreateWidget(); + void DestroyWidget(); mozilla::WindowShadow GetShadowStyle() const; void DidSetComputedStyle(ComputedStyle* aOldStyle) override; @@ -283,6 +301,10 @@ class nsMenuPopupFrame final : public nsBlockFrame { bool IsDragSource() const { return mIsDragSource; } void SetIsDragSource(bool aIsDragSource) { mIsDragSource = aIsDragSource; } + bool PendingWidgetMoveResize() const { return mPendingWidgetMoveResize; } + void ClearPendingWidgetMoveResize() { mPendingWidgetMoveResize = false; } + void SchedulePendingWidgetMoveResize(); + static nsIContent* GetTriggerContent(nsMenuPopupFrame* aMenuPopupFrame); void ClearTriggerContent() { mTriggerContent = nullptr; } void ClearTriggerContentIncludingDocument(); @@ -405,6 +427,8 @@ class nsMenuPopupFrame final : public nsBlockFrame { return mLastClientOffset; } + mozilla::LayoutDeviceIntRect CalcWidgetBounds() const; + // Return the alignment of the popup int8_t GetAlignmentPosition() const; @@ -500,13 +524,6 @@ class nsMenuPopupFrame final : public nsBlockFrame { // attributes. void MoveToAttributePosition(); - // Create a popup view for this frame. The view is added a child of the root - // view, and is initially hidden. - void CreatePopupView(); - - nsView* GetViewInternal() const override { return mView; } - void SetViewInternal(nsView* aView) override { mView = aView; } - // Returns true if the popup should try to remain at the same relative // location as the anchor while it is open. If the anchor becomes hidden // either directly or indirectly because a parent popup or other element @@ -573,7 +590,7 @@ class nsMenuPopupFrame final : public nsBlockFrame { // was clicked. It will be cleared when the popup is hidden. nsCOMPtr<nsIContent> mTriggerContent; - nsView* mView = nullptr; + RefPtr<nsIWidget> mWidget; RefPtr<nsXULPopupShownEvent> mPopupShownDispatcher; @@ -653,6 +670,9 @@ class nsMenuPopupFrame final : public nsBlockFrame { // popup on Wayland as it cancel whole D&D operation. bool mIsDragSource = false; + // Whether there's a pending move-resize of our widget. + bool mPendingWidgetMoveResize = false; + // When POPUPPOSITION_SELECTION is used, this indicates the vertical offset // that the original selected item was. This needs to be used in case the // popup gets changed so that we can keep the popup at the same vertical @@ -666,7 +686,6 @@ class nsMenuPopupFrame final : public nsBlockFrame { nsRect mOverrideConstraintRect; nsExpirationState mExpirationState; - static mozilla::TimeStamp sLastKeyTime; }; // class nsMenuPopupFrame diff --git a/layout/xul/nsXULPopupManager.cpp b/layout/xul/nsXULPopupManager.cpp @@ -7,6 +7,7 @@ #include "nsXULPopupManager.h" #include "PopupQueue.h" +#include "WindowRenderer.h" #include "XULButtonElement.h" #include "mozilla/AnimationUtils.h" #include "mozilla/Assertions.h" @@ -674,105 +675,6 @@ void nsXULPopupManager::AdjustPopupsOnWindowChange(PresShell* aPresShell) { } } -static nsMenuPopupFrame* GetPopupToMoveOrResize(nsIFrame* aFrame) { - nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(aFrame); - if (!menuPopupFrame) { - return nullptr; - } - - // no point moving or resizing hidden popups - if (!menuPopupFrame->IsVisible()) { - return nullptr; - } - - nsIWidget* widget = menuPopupFrame->GetWidget(); - if (widget && !widget->IsVisible()) { - return nullptr; - } - - return menuPopupFrame; -} - -void nsXULPopupManager::PopupMoved(nsIFrame* aFrame, - const LayoutDeviceIntPoint& aPoint, - bool aByMoveToRect) { - nsMenuPopupFrame* menuPopupFrame = GetPopupToMoveOrResize(aFrame); - if (!menuPopupFrame) { - return; - } - - nsView* view = menuPopupFrame->GetView(); - if (!view) { - return; - } - - // Don't do anything if the popup is already at the specified location. This - // prevents recursive calls when a popup is positioned. - LayoutDeviceIntRect curDevBounds = view->RecalcWidgetBounds(); - nsIWidget* widget = menuPopupFrame->GetWidget(); - if (curDevBounds.TopLeft() == aPoint && - (!widget || - widget->GetClientOffset() == menuPopupFrame->GetLastClientOffset())) { - return; - } - - // Update the popup's position using SetPopupPosition if the popup is - // anchored and at the parent level as these maintain their position - // relative to the parent window (except if positioned by move to rect, in - // which case we better make sure that layout matches that). Otherwise, just - // update the popup to the specified screen coordinates. - if (menuPopupFrame->IsAnchored() && - menuPopupFrame->GetPopupLevel() == widget::PopupLevel::Parent && - !aByMoveToRect) { - menuPopupFrame->SetPopupPosition(true); - } else { - CSSPoint cssPos = - aPoint / menuPopupFrame->PresContext()->CSSToDevPixelScale(); - menuPopupFrame->MoveTo(cssPos, false, aByMoveToRect); - } -} - -void nsXULPopupManager::PopupResized(nsIFrame* aFrame, - const LayoutDeviceIntSize& aSize) { - nsMenuPopupFrame* menuPopupFrame = GetPopupToMoveOrResize(aFrame); - if (!menuPopupFrame) { - return; - } - - nsView* view = menuPopupFrame->GetView(); - if (!view) { - return; - } - - const LayoutDeviceIntRect curDevBounds = view->RecalcWidgetBounds(); - // If the size is what we think it is, we have nothing to do. - if (curDevBounds.Size() == aSize) { - return; - } - - Element* popup = menuPopupFrame->GetContent()->AsElement(); - - // Only set the width and height if the popup already has these attributes. - if (!popup->HasAttr(nsGkAtoms::width) || !popup->HasAttr(nsGkAtoms::height)) { - return; - } - - // The size is different. Convert the actual size to css pixels and store it - // as 'width' and 'height' attributes on the popup. - nsPresContext* presContext = menuPopupFrame->PresContext(); - - CSSIntSize newCSS(presContext->DevPixelsToIntCSSPixels(aSize.width), - presContext->DevPixelsToIntCSSPixels(aSize.height)); - - nsAutoString width, height; - width.AppendInt(newCSS.width); - height.AppendInt(newCSS.height); - // FIXME(emilio): aNotify should be consistent (probably true in the two calls - // below?). - popup->SetAttr(kNameSpaceID_None, nsGkAtoms::width, width, false); - popup->SetAttr(kNameSpaceID_None, nsGkAtoms::height, height, true); -} - nsMenuPopupFrame* nsXULPopupManager::GetPopupFrameForContent( nsIContent* aContent, bool aShouldFlush) { if (aShouldFlush) { @@ -1698,8 +1600,76 @@ void nsXULPopupManager::HidePopupsInDocShell( void nsXULPopupManager::UpdatePopupPositions(nsRefreshDriver* aRefreshDriver) { for (nsMenuChainItem* item = mPopups.get(); item; item = item->GetParent()) { - if (item->Frame()->PresContext()->RefreshDriver() == aRefreshDriver) { - item->CheckForAnchorChange(); + nsMenuPopupFrame* frame = item->Frame(); + if (frame->PresContext()->RefreshDriver() != aRefreshDriver) { + continue; + } + item->CheckForAnchorChange(); + } +} + +void nsXULPopupManager::PaintPopups(nsRefreshDriver* aRefreshDriver) { + if (!mPopups) { + return; + } + + AutoTArray<std::pair<RefPtr<nsIWidget>, WeakFrame>, 32> visiblePopups; + for (nsMenuChainItem* item = mPopups.get(); item; item = item->GetParent()) { + nsMenuPopupFrame* frame = item->Frame(); + if (!frame->IsVisible() || + frame->PresContext()->GetRootPresContext()->RefreshDriver() != + aRefreshDriver) { + continue; + } + if (nsIWidget* widget = frame->GetWidget()) { + visiblePopups.AppendElement(std::make_pair(widget, frame)); + } + } + + for (const auto& visiblePopup : Reversed(visiblePopups)) { + nsIWidget* widget = visiblePopup.first; + nsMenuPopupFrame* frame = do_QueryFrame(visiblePopup.second.GetFrame()); + if (!frame) { + continue; + } + if (frame->PendingWidgetMoveResize()) { + frame->ClearPendingWidgetMoveResize(); + + LayoutDeviceIntRect curBounds = widget->GetClientBounds(); + auto newBounds = frame->CalcWidgetBounds(); + widget->ConstrainSize(&newBounds.width, &newBounds.height); + const bool changedPos = curBounds.TopLeft() != newBounds.TopLeft(); + const bool changedSize = curBounds.Size() != newBounds.Size(); + + if (changedPos || changedSize) { + DesktopToLayoutDeviceScale scale = widget->GetDesktopToDeviceScale(); + DesktopRect deskRect = newBounds / scale; + if (changedPos) { + if (changedSize) { + widget->ResizeClient(deskRect, true); + } else { + widget->MoveClient(deskRect.TopLeft()); + } + } else if (changedSize) { + widget->ResizeClient(deskRect.Size(), true); + } + } + } + if (!widget->IsVisible()) { + widget->Show(true); + } + if (!visiblePopup.second.IsAlive() || !widget->NeedsPaint()) { + continue; + } + nsAutoScriptBlocker scriptBlocker; + RefPtr<PresShell> ps = frame->PresShell(); + RefPtr<WindowRenderer> renderer = widget->GetWindowRenderer(); + if (renderer->AsFallback()) { + // FIXME: A bit of a hack. This matches what PaintAndRequestComposite + // does for views (eventually). + widget->Invalidate(LayoutDeviceIntRect({}, widget->GetBounds().Size())); + } else { + ps->PaintAndRequestComposite(frame, renderer, PaintFlags::None); } } } diff --git a/layout/xul/nsXULPopupManager.h b/layout/xul/nsXULPopupManager.h @@ -551,6 +551,8 @@ class nsXULPopupManager final : public nsIDOMEventListener, * Only those popups that pertain to the supplied aRefreshDriver are updated. */ void UpdatePopupPositions(nsRefreshDriver* aRefreshDriver); + MOZ_CAN_RUN_SCRIPT_BOUNDARY + void PaintPopups(nsRefreshDriver* aRefreshDriver); /** * Get the first nsMenuChainItem that is matched by the matching callback @@ -630,20 +632,6 @@ class nsXULPopupManager final : public nsIDOMEventListener, bool MayShowPopup(nsMenuPopupFrame* aFrame); /** - * Indicate that the popup associated with aView has been moved to the - * specified device pixel coordinates. - */ - void PopupMoved(nsIFrame* aFrame, const mozilla::LayoutDeviceIntPoint& aPoint, - bool aByMoveToRect); - - /** - * Indicate that the popup associated with aView has been resized to the - * given device pixel size aSize. - */ - void PopupResized(nsIFrame* aFrame, - const mozilla::LayoutDeviceIntSize& aSize); - - /** * Called when a popup frame is destroyed. In this case, just remove the * item and later popups from the list. No point going through HidePopup as * the frames have gone away. diff --git a/view/nsView.cpp b/view/nsView.cpp @@ -172,83 +172,76 @@ void nsView::SetPosition(nscoord aX, nscoord aY) { NS_ASSERTION(GetParent() || (aX == 0 && aY == 0), "Don't try to move the root widget to something non-zero"); - - ResetWidgetBounds(true, false); -} - -void nsView::ResetWidgetBounds(bool aRecurse, bool aForceSync) { - if (mWindow) { - if (!aForceSync) { - // Don't change widget geometry synchronously, since that can - // cause synchronous painting. - mViewManager->PostPendingUpdate(); - } else { - DoResetWidgetBounds(false, true); - } - return; - } - - if (aRecurse) { - // reposition any widgets under this view - for (nsView* v = GetFirstChild(); v; v = v->GetNextSibling()) { - v->ResetWidgetBounds(true, aForceSync); - } - } } bool nsView::IsEffectivelyVisible() { for (nsView* v = this; v; v = v->mParent) { - if (v->GetVisibility() == ViewVisibility::Hide) return false; + if (v->GetVisibility() == ViewVisibility::Hide) { + return false; + } } return true; } -LayoutDeviceIntRect nsView::CalcWidgetBounds(WindowType aType, - TransparencyMode aTransparency) { - int32_t p2a = mViewManager->AppUnitsPerDevPixel(); - - nsRect viewBounds(mDimBounds); +struct WidgetViewBounds { + nsRect mBounds; + int32_t mRoundTo = 1; +}; - nsView* parent = GetParent(); +static WidgetViewBounds CalcWidgetViewBounds( + const nsRect& aBounds, int32_t aAppUnitsPerDevPixel, nsView* aParentView, + nsIWidget* aThisWidget, WindowType aType, TransparencyMode aTransparency) { + nsRect viewBounds(aBounds); nsIWidget* parentWidget = nullptr; - if (parent) { + if (aParentView) { nsPoint offset; - parentWidget = parent->GetNearestWidget(&offset, p2a); + parentWidget = aParentView->GetNearestWidget(&offset, aAppUnitsPerDevPixel); // make viewBounds be relative to the parent widget, in appunits viewBounds += offset; - if (parentWidget && aType == WindowType::Popup && IsEffectivelyVisible()) { + if (parentWidget && aType == WindowType::Popup) { // put offset into screen coordinates. (based on client area origin) LayoutDeviceIntPoint screenPoint = parentWidget->WidgetToScreenOffset(); - viewBounds += nsPoint(NSIntPixelsToAppUnits(screenPoint.x, p2a), - NSIntPixelsToAppUnits(screenPoint.y, p2a)); + viewBounds += + nsPoint(NSIntPixelsToAppUnits(screenPoint.x, aAppUnitsPerDevPixel), + NSIntPixelsToAppUnits(screenPoint.y, aAppUnitsPerDevPixel)); } } + nsIWidget* widget = parentWidget ? parentWidget : aThisWidget; + int32_t roundTo = widget ? widget->RoundsWidgetCoordinatesTo() : 1; + return {viewBounds, roundTo}; +} + +static LayoutDeviceIntRect WidgetViewBoundsToDevicePixels( + const WidgetViewBounds& aViewBounds, int32_t aAppUnitsPerDevPixel, + WindowType aType, TransparencyMode aTransparency) { // Compute widget bounds in device pixels - const LayoutDeviceIntRect newBounds = [&] { - // TODO(emilio): We should probably use outside pixels for transparent - // windows (not just popups) as well. - if (aType != WindowType::Popup) { - return LayoutDeviceIntRect::FromUnknownRect( - viewBounds.ToNearestPixels(p2a)); - } - // We use outside pixels for transparent windows if possible, so that we - // don't truncate the contents. For opaque popups, we use nearest pixels - // which prevents having pixels not drawn by the frame. - const bool opaque = aTransparency == TransparencyMode::Opaque; - const auto idealBounds = LayoutDeviceIntRect::FromUnknownRect( - opaque ? viewBounds.ToNearestPixels(p2a) - : viewBounds.ToOutsidePixels(p2a)); - - nsIWidget* widget = parentWidget ? parentWidget : mWindow.get(); - if (!widget) { - return idealBounds; - } - const int32_t round = widget->RoundsWidgetCoordinatesTo(); - return nsIWidget::MaybeRoundToDisplayPixels(idealBounds, aTransparency, - round); - }(); + // TODO(emilio): We should probably use outside pixels for transparent + // windows (not just popups) as well. + if (aType != WindowType::Popup) { + return LayoutDeviceIntRect::FromUnknownRect( + aViewBounds.mBounds.ToNearestPixels(aAppUnitsPerDevPixel)); + } + // We use outside pixels for transparent windows if possible, so that we + // don't truncate the contents. For opaque popups, we use nearest pixels + // which prevents having pixels not drawn by the frame. + const bool opaque = aTransparency == TransparencyMode::Opaque; + const auto idealBounds = LayoutDeviceIntRect::FromUnknownRect( + opaque ? aViewBounds.mBounds.ToNearestPixels(aAppUnitsPerDevPixel) + : aViewBounds.mBounds.ToOutsidePixels(aAppUnitsPerDevPixel)); + + return nsIWidget::MaybeRoundToDisplayPixels(idealBounds, aTransparency, + aViewBounds.mRoundTo); +} + +LayoutDeviceIntRect nsView::CalcWidgetBounds(WindowType aType, + TransparencyMode aTransparency) { + int32_t p2a = mViewManager->AppUnitsPerDevPixel(); + auto viewBounds = CalcWidgetViewBounds(mDimBounds, p2a, GetParent(), + mWindow.get(), aType, aTransparency); + auto newBounds = + WidgetViewBoundsToDevicePixels(viewBounds, p2a, aType, aTransparency); // Compute where the top-left of our widget ended up relative to the parent // widget, in appunits. @@ -261,98 +254,27 @@ LayoutDeviceIntRect nsView::CalcWidgetBounds(WindowType aType, // (mPosX,mPosY) - mDimBounds.TopLeft() + viewBounds.TopLeft(). // Our widget, relative to the parent widget, is roundedOffset. mViewToWidgetOffset = nsPoint(mPosX, mPosY) - mDimBounds.TopLeft() + - viewBounds.TopLeft() - roundedOffset; - + viewBounds.mBounds.TopLeft() - roundedOffset; return newBounds; } +LayoutDeviceIntRect nsView::CalcWidgetBounds( + const nsRect& aBounds, int32_t aAppUnitsPerDevPixel, nsView* aParentView, + nsIWidget* aThisWidget, WindowType aType, TransparencyMode aTransparency) { + auto viewBounds = + CalcWidgetViewBounds(aBounds, aAppUnitsPerDevPixel, aParentView, + aThisWidget, aType, aTransparency); + return WidgetViewBoundsToDevicePixels(viewBounds, aAppUnitsPerDevPixel, aType, + aTransparency); +} + LayoutDeviceIntRect nsView::RecalcWidgetBounds() { MOZ_ASSERT(mWindow); return CalcWidgetBounds(mWindow->GetWindowType(), mWindow->GetTransparencyMode()); } -void nsView::DoResetWidgetBounds(bool aMoveOnly, bool aInvalidateChangedSize) { - // The geometry of a root view's widget is controlled externally, - // NOT by sizing or positioning the view - if (mViewManager->GetRootView() == this) { - return; - } - - MOZ_ASSERT(mWindow, "Why was this called??"); - - // Hold this ref to make sure it stays alive. - nsCOMPtr<nsIWidget> widget = mWindow; - - // Stash a copy of these and use them so we can handle this being deleted (say - // from sync painting/flushing from Show/Move/Resize on the widget). - LayoutDeviceIntRect newBounds; - - WindowType type = widget->GetWindowType(); - - LayoutDeviceIntRect curBounds = widget->GetClientBounds(); - bool invisiblePopup = type == WindowType::Popup && - ((curBounds.IsEmpty() && mDimBounds.IsEmpty()) || - mVis == ViewVisibility::Hide); - - if (invisiblePopup) { - // We're going to hit the early exit below, avoid calling CalcWidgetBounds. - } else { - newBounds = CalcWidgetBounds(type, widget->GetTransparencyMode()); - invisiblePopup = newBounds.IsEmpty(); - } - - bool curVisibility = widget->IsVisible(); - bool newVisibility = !invisiblePopup && IsEffectivelyVisible(); - if (curVisibility && !newVisibility) { - widget->Show(false); - } - - if (invisiblePopup) { - // Don't manipulate empty or hidden popup widgets. For example there's no - // point moving hidden comboboxes around, or doing X server roundtrips - // to compute their true screen position. This could mean that - // WidgetToScreen operations on these widgets don't return up-to-date - // values, but popup positions aren't reliable anyway because of correction - // to be on or off-screen. - return; - } - - // Apply the widget size constraints to newBounds. - widget->ConstrainSize(&newBounds.width, &newBounds.height); - - bool changedPos = curBounds.TopLeft() != newBounds.TopLeft(); - bool changedSize = curBounds.Size() != newBounds.Size(); - - // Child views are never attached to top level widgets, this is safe. - - // Coordinates are converted to desktop pixels for window Move/Resize APIs, - // because of the potential for device-pixel coordinate spaces for mixed - // hidpi/lodpi screens to overlap each other and result in bad placement - // (bug 814434). - - DesktopToLayoutDeviceScale scale = widget->GetDesktopToDeviceScaleByScreen(); - - DesktopRect deskRect = newBounds / scale; - if (changedPos) { - if (changedSize && !aMoveOnly) { - widget->ResizeClient(deskRect, aInvalidateChangedSize); - } else { - widget->MoveClient(deskRect.TopLeft()); - } - } else { - if (changedSize && !aMoveOnly) { - widget->ResizeClient(deskRect.Size(), aInvalidateChangedSize); - } // else do nothing! - } - - if (!curVisibility && newVisibility) { - widget->Show(true); - } -} - -void nsView::SetDimensions(const nsRect& aRect, bool aPaint, - bool aResizeWidget) { +void nsView::SetDimensions(const nsRect& aRect) { nsRect dims = aRect; dims.MoveBy(mPosX, mPosY); @@ -365,10 +287,6 @@ void nsView::SetDimensions(const nsRect& aRect, bool aPaint, } mDimBounds = dims; - - if (aResizeWidget) { - ResetWidgetBounds(false, false); - } } void nsView::NotifyEffectiveVisibilityChanged(bool aEffectivelyVisible) { @@ -378,10 +296,6 @@ void nsView::NotifyEffectiveVisibilityChanged(bool aEffectivelyVisible) { SetForcedRepaint(true); - if (mWindow) { - ResetWidgetBounds(false, false); - } - for (nsView* child = mFirstChild; child; child = child->mNextSibling) { if (child->mVis == ViewVisibility::Hide) { // It was effectively hidden and still is @@ -398,10 +312,13 @@ void nsView::SetVisibility(ViewVisibility aVisibility) { } void nsView::InvalidateHierarchy() { - if (mViewManager->GetRootView() == this) mViewManager->InvalidateHierarchy(); + if (mViewManager->GetRootView() == this) { + mViewManager->InvalidateHierarchy(); + } - for (nsView* child = mFirstChild; child; child = child->GetNextSibling()) + for (nsView* child = mFirstChild; child; child = child->GetNextSibling()) { child->InvalidateHierarchy(); + } } void nsView::InsertChild(nsView* aChild, nsView* aSibling) { @@ -498,24 +415,6 @@ nsresult nsView::CreateWidget(nsIWidget* aParent, bool aEnableDragDrop, return NS_OK; } -nsresult nsView::CreateWidgetForPopup(widget::InitData* aWidgetInitData, - nsIWidget* aParent) { - AssertNoWindow(); - MOZ_ASSERT(aWidgetInitData, "Widget init data required"); - MOZ_ASSERT(aWidgetInitData->mWindowType == WindowType::Popup, - "Use one of the other CreateWidget methods"); - MOZ_ASSERT(aParent); - - LayoutDeviceIntRect trect = CalcWidgetBounds( - aWidgetInitData->mWindowType, aWidgetInitData->mTransparencyMode); - mWindow = aParent->CreateChild(trect, *aWidgetInitData); - if (!mWindow) { - return NS_ERROR_FAILURE; - } - InitializeWindow(/* aEnableDragDrop = */ true, /* aResetVisibility = */ true); - return NS_OK; -} - void nsView::InitializeWindow(bool aEnableDragDrop, bool aResetVisibility) { MOZ_ASSERT(mWindow, "Must have a window to initialize"); @@ -545,10 +444,8 @@ nsresult nsView::AttachToTopLevelWidget(nsIWidget* aWidget) { /// XXXjimm This is a temporary workaround to an issue w/document // viewer (bug 513162). - nsIWidgetListener* listener = aWidget->GetAttachedWidgetListener(); - if (listener) { - nsView* oldView = listener->GetView(); - if (oldView) { + if (nsIWidgetListener* listener = aWidget->GetAttachedWidgetListener()) { + if (nsView* oldView = listener->GetView()) { oldView->DetachFromTopLevelWidget(); } } @@ -577,11 +474,12 @@ nsresult nsView::DetachFromTopLevelWidget() { MOZ_ASSERT(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 (nsIWidgetListener* listener = + mWindow->GetPreviouslyAttachedWidgetListener()) { + if (nsView* view = listener->GetView()) { + // Ensure the listener doesn't think it's being used anymore + view->SetPreviousWidget(nullptr); + } } // If the new view's frame is paint suppressed then the window @@ -774,63 +672,40 @@ bool nsView::IsRoot() const { return mViewManager->GetRootView() == this; } -static bool IsPopupWidget(nsIWidget* aWidget) { - return aWidget->GetWindowType() == WindowType::Popup; -} - PresShell* nsView::GetPresShell() { return GetViewManager()->GetPresShell(); } -bool nsView::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y, - ByMoveToRect aByMoveToRect) { - nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); - if (pm && IsPopupWidget(aWidget)) { - pm->PopupMoved(mFrame, LayoutDeviceIntPoint(x, y), - aByMoveToRect == ByMoveToRect::Yes); - return true; - } - - return false; -} - bool nsView::WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) { // The root view may not be set if this is the resize associated with // window creation SetForcedRepaint(true); - if (this == mViewManager->GetRootView()) { - RefPtr<nsDeviceContext> devContext = mViewManager->GetDeviceContext(); - // ensure DPI is up-to-date, in case of window being opened and sized - // on a non-default-dpi display (bug 829963) - devContext->CheckDPIChange(); - int32_t p2a = devContext->AppUnitsPerDevPixel(); - if (auto* frame = GetFrame()) { - // Usually the resize would deal with this, but there are some cases (like - // web-extension popups) where frames might already be correctly sized etc - // due to a call to e.g. nsDocumentViewer::GetContentSize or so. - frame->InvalidateFrame(); - } + if (this != mViewManager->GetRootView()) { + return false; + } - mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(aWidth, p2a), - NSIntPixelsToAppUnits(aHeight, p2a)); + RefPtr<nsDeviceContext> devContext = mViewManager->GetDeviceContext(); + // ensure DPI is up-to-date, in case of window being opened and sized + // on a non-default-dpi display (bug 829963) + devContext->CheckDPIChange(); + int32_t p2a = devContext->AppUnitsPerDevPixel(); + if (auto* frame = GetFrame()) { + // Usually the resize would deal with this, but there are some cases (like + // web-extension popups) where frames might already be correctly sized etc + // due to a call to e.g. nsDocumentViewer::GetContentSize or so. + frame->InvalidateFrame(); + } - if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) { - PresShell* presShell = mViewManager->GetPresShell(); - if (presShell && presShell->GetDocument()) { - pm->AdjustPopupsOnWindowChange(presShell); - } - } + mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(aWidth, p2a), + NSIntPixelsToAppUnits(aHeight, p2a)); - return true; - } - if (IsPopupWidget(aWidget)) { - nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); - if (pm) { - pm->PopupResized(mFrame, LayoutDeviceIntSize(aWidth, aHeight)); - return true; + if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) { + PresShell* presShell = mViewManager->GetPresShell(); + if (presShell && presShell->GetDocument()) { + pm->AdjustPopupsOnWindowChange(presShell); } } - return false; + return true; } #ifdef MOZ_WIDGET_ANDROID @@ -894,18 +769,6 @@ void nsView::AndroidPipModeChanged(bool aPipMode) { } #endif -bool nsView::RequestWindowClose(nsIWidget* aWidget) { - if (mFrame && IsPopupWidget(aWidget) && mFrame->IsMenuPopupFrame()) { - if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) { - pm->HidePopup(mFrame->GetContent()->AsElement(), - {HidePopupOption::DeselectMenu}); - return true; - } - } - - return false; -} - void nsView::WillPaintWindow(nsIWidget* aWidget) { RefPtr<nsViewManager> vm = mViewManager; vm->WillPaintWindow(aWidget); @@ -942,12 +805,6 @@ void nsView::DidCompositeWindow(mozilla::layers::TransactionId aTransactionId, mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT2, aCompositeEnd); - - // If the two timestamps are identical, this was likely a fake composite - // event which wouldn't be terribly useful to display. - if (aCompositeStart == aCompositeEnd) { - return; - } } void nsView::RequestRepaint() { diff --git a/view/nsView.h b/view/nsView.h @@ -271,15 +271,6 @@ class nsView final : public nsIWidgetListener { bool aResetVisibility = true); /** - * Create a popup widget for this view. Pass |aParentWidget| to - * explicitly set the popup's parent. If it's not passed, the view - * hierarchy will be searched for an appropriate parent widget. The - * other params are the same as for |CreateWidget()|, except that - * |aWidgetInitData| must be nonnull. - */ - nsresult CreateWidgetForPopup(mozilla::widget::InitData*, nsIWidget* aParent); - - /** * Destroys the associated widget for this view. If this method is * not called explicitly, the widget when be destroyed when its * view gets destroyed. @@ -359,6 +350,11 @@ class nsView final : public nsIWidgetListener { */ bool IsRoot() const; + static LayoutDeviceIntRect CalcWidgetBounds( + const nsRect& aBounds, int32_t aAppUnitsPerDevPixel, nsView* aParentView, + nsIWidget* aThisWidget, mozilla::widget::WindowType, + mozilla::widget::TransparencyMode); + LayoutDeviceIntRect CalcWidgetBounds(mozilla::widget::WindowType, mozilla::widget::TransparencyMode); @@ -386,8 +382,6 @@ class nsView final : public nsIWidgetListener { // nsIWidgetListener mozilla::PresShell* GetPresShell() override; nsView* GetView() override { return this; } - bool WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y, - ByMoveToRect) override; bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) override; #ifdef MOZ_WIDGET_ANDROID @@ -396,7 +390,6 @@ class nsView final : public nsIWidgetListener { void KeyboardHeightChanged(mozilla::ScreenIntCoord aHeight) override; void AndroidPipModeChanged(bool) override; #endif - bool RequestWindowClose(nsIWidget* aWidget) override; MOZ_CAN_RUN_SCRIPT_BOUNDARY void WillPaintWindow(nsIWidget* aWidget) override; MOZ_CAN_RUN_SCRIPT_BOUNDARY @@ -426,11 +419,7 @@ class nsView final : public nsIWidgetListener { bool ForcedRepaint() { return mForcedRepaint; } - // Do the actual work of ResetWidgetBounds, unconditionally. Don't - // call this method if we have no widget. - void DoResetWidgetBounds(bool aMoveOnly, bool aInvalidateChangedSize); void InitializeWindow(bool aEnableDragDrop, bool aResetVisibility); - bool IsEffectivelyVisible(); /** @@ -439,8 +428,7 @@ class nsView final : public nsIWidgetListener { * or to the left of its origin position. The term 'dimensions' indicates it * is relative to this view. */ - void SetDimensions(const nsRect& aRect, bool aPaint = true, - bool aResizeWidget = true); + void SetDimensions(const nsRect& aRect); /** * Called to indicate that the visibility of a view has been @@ -459,7 +447,6 @@ class nsView final : public nsIWidgetListener { void InsertChild(nsView* aChild, nsView* aSibling); void RemoveChild(nsView* aChild); - void ResetWidgetBounds(bool aRecurse, bool aForceSync); void AssertNoWindow(); void NotifyEffectiveVisibilityChanged(bool aEffectivelyVisible); diff --git a/view/nsViewManager.cpp b/view/nsViewManager.cpp @@ -96,7 +96,7 @@ nsView* nsViewManager::CreateView(const nsRect& aBounds, nsView* aParent, v->SetParent(aParent); v->SetPosition(aBounds.X(), aBounds.Y()); nsRect dim(0, 0, aBounds.Width(), aBounds.Height()); - v->SetDimensions(dim, false); + v->SetDimensions(dim); return v; } @@ -145,7 +145,7 @@ void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight) { return; } // Don't resize the widget. It is already being set elsewhere. - mRootView->SetDimensions(newDim, true, false); + mRootView->SetDimensions(newDim); if (RefPtr<PresShell> presShell = mPresShell) { presShell->ResizeReflow(aWidth, aHeight); } @@ -305,20 +305,17 @@ void nsViewManager::ProcessPendingUpdatesForView(nsView* aView, aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets); for (uint32_t i = 0; i < widgets.Length(); ++i) { nsView* view = nsView::GetViewFor(widgets[i]); - if (view) { - if (view->mNeedsWindowPropertiesSync) { - view->mNeedsWindowPropertiesSync = false; - if (nsViewManager* vm = view->GetViewManager()) { - if (PresShell* presShell = vm->GetPresShell()) { - presShell->SyncWindowProperties(/* aSync */ true); - } + if (!view) { + continue; + } + if (view->mNeedsWindowPropertiesSync) { + view->mNeedsWindowPropertiesSync = false; + if (nsViewManager* vm = view->GetViewManager()) { + if (PresShell* presShell = vm->GetPresShell()) { + presShell->SyncWindowProperties(/* aSync */ true); } } } - view = nsView::GetViewFor(widgets[i]); - if (view) { - view->ResetWidgetBounds(false, true); - } } if (rootPresShell->GetViewManager() != this) { return; // presentation might have been torn down @@ -604,7 +601,7 @@ void nsViewManager::DispatchEvent(WidgetGUIEvent* aEvent, nsView* aView, } } - if (nullptr != frame) { + if (frame) { // Hold a refcount to the presshell. The continued existence of the // presshell will delay deletion of this view hierarchy should the event // want to cause its destruction in, say, some JavaScript event handler. @@ -690,7 +687,7 @@ void nsViewManager::ResizeView(nsView* aView, const nsRect& aRect) { nsRect oldDimensions = aView->GetDimensions(); if (!oldDimensions.IsEqualEdges(aRect)) { - aView->SetDimensions(aRect, true); + aView->SetDimensions(aRect); } // Note that if layout resizes the view and the view has a custom clip diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp @@ -780,18 +780,6 @@ DesktopToLayoutDeviceScale nsWindow::GetDesktopToDeviceScale() { return DesktopToLayoutDeviceScale(1.0); } -DesktopToLayoutDeviceScale nsWindow::GetDesktopToDeviceScaleByScreen() { -#ifdef MOZ_WAYLAND - if (GdkIsWaylandDisplay()) { - // In wayland there's no absolute screen position, so we need to use the - // scale factor of our top level, which is what FractionalScaleFactor does, - // luckily. - return DesktopToLayoutDeviceScale(FractionalScaleFactor()); - } -#endif - return nsBaseWidget::GetDesktopToDeviceScale(); -} - bool nsWindow::WidgetTypeSupportsAcceleration() { if (IsSmallPopup() || mIsDragPopup) { return false; @@ -6003,7 +5991,7 @@ bool nsWindow::ConfigureX11GLVisual() { #endif nsAutoCString nsWindow::GetFrameTag() const { - if (nsIFrame* frame = GetFrame()) { + if (nsIFrame* frame = GetPopupFrame()) { #ifdef DEBUG_FRAME_DUMP return frame->ListTag(); #else @@ -9741,11 +9729,14 @@ void nsWindow::UpdateMozWindowActive() { void nsWindow::ForceTitlebarRedraw() { MOZ_ASSERT(mDrawInTitlebar, "We should not redraw invisible titlebar."); - if (!mWidgetListener || !mWidgetListener->GetPresShell()) { + if (!mWidgetListener) { return; } - - nsIFrame* frame = GetFrame(); + PresShell* ps = mWidgetListener->GetPresShell(); + if (!ps) { + return; + } + nsIFrame* frame = ps->GetRootFrame(); if (!frame) { return; } diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h @@ -102,6 +102,7 @@ typedef uintptr_t Window; class gfxPattern; class nsIFrame; +class nsMenuPopupFrame; #if !GTK_CHECK_VERSION(3, 18, 0) struct _GdkEventTouchpadPinch; typedef struct _GdkEventTouchpadPinch GdkEventTouchpadPinch; @@ -184,8 +185,6 @@ class nsWindow final : public nsBaseWidget { float GetDPI() override; double GetDefaultScaleInternal() override; mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() override; - mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScaleByScreen() - override; void SetModal(bool aModal) override; bool IsVisible() const override; bool IsMapped() const override; diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp @@ -448,6 +448,9 @@ void nsIWidget::RemoveAllChildren() { } nsIFrame* nsIWidget::GetFrame() const { + if (auto* popup = GetPopupFrame()) { + return popup; + } if (nsView* view = nsView::GetViewFor(this)) { return view->GetFrame(); } @@ -458,9 +461,9 @@ nsMenuPopupFrame* nsIWidget::GetPopupFrame() const { if (mWindowType != WindowType::Popup) { return nullptr; } - auto* frame = GetFrame(); - MOZ_ASSERT_IF(frame, frame->IsMenuPopupFrame()); - return do_QueryFrame(frame); + MOZ_ASSERT_IF(GetWidgetListener(), + GetWidgetListener()->GetAsMenuPopupFrame()); + return static_cast<nsMenuPopupFrame*>(GetWidgetListener()); } void nsBaseWidget::DynamicToolbarOffsetChanged( @@ -2138,17 +2141,10 @@ already_AddRefed<widget::Screen> nsBaseWidget::GetWidgetScreen() { return screenManager.ScreenForRect(deskBounds); } -mozilla::DesktopToLayoutDeviceScale -nsBaseWidget::GetDesktopToDeviceScaleByScreen() { - return (nsView::GetViewFor(this)->GetViewManager()->GetDeviceContext()) - ->GetDesktopToDeviceScale(); -} - nsresult nsIWidget::SynthesizeNativeTouchTap( LayoutDeviceIntPoint aPoint, bool aLongTap, nsISynthesizedEventCallback* aCallback) { AutoSynthesizedEventCallbackNotifier notifier(aCallback); - if (sPointerIdCounter > TOUCH_INJECT_MAX_POINTS) { sPointerIdCounter = 0; } diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h @@ -243,8 +243,6 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference { mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() override { return mozilla::DesktopToLayoutDeviceScale(1.0); } - mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScaleByScreen() - override; void ConstrainPosition(DesktopIntPoint&) override {} // Utility function for derived-class overrides of ConstrainPosition. diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h @@ -558,14 +558,6 @@ class nsIWidget : public nsISupports { */ virtual mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() = 0; - /** - * Return the scaling factor between device pixels and the platform- - * dependent "desktop pixels" by looking up the screen by the position - * of the widget. - */ - virtual mozilla::DesktopToLayoutDeviceScale - GetDesktopToDeviceScaleByScreen() = 0; - virtual void DynamicToolbarOffsetChanged(mozilla::ScreenIntCoord aOffset) = 0; /** diff --git a/widget/nsIWidgetListener.h b/widget/nsIWidgetListener.h @@ -17,6 +17,7 @@ class nsView; class nsIWidget; class nsIAppWindow; +class nsMenuPopupFrame; namespace mozilla { class PresShell; @@ -54,6 +55,9 @@ class nsIWidgetListener { /** If this listener is for an nsView, return it. */ virtual nsView* GetView() { return nullptr; } + /** If this listener is for an nsMenuPopupFrame, return it. */ + virtual nsMenuPopupFrame* GetAsMenuPopupFrame() { return nullptr; } + /** Return the presshell for this widget listener. */ virtual mozilla::PresShell* GetPresShell() { return nullptr; }