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:
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; }