commit d81a1a074fa7c0a4ebc538d329ad8f35cd3d8c85
parent bd2ca4efdc8ef0e379fd98e170b4b254bf6ed05f
Author: Emilio Cobos Álvarez <emilio@crisal.io>
Date: Fri, 10 Oct 2025 09:33:10 +0000
Bug 1993570 - Introduce nsIWidget::Get{,Popup}Frame. r=layout-reviewers,dshin
And use it instead of manually using views.
Differential Revision: https://phabricator.services.mozilla.com/D268206
Diffstat:
10 files changed, 87 insertions(+), 105 deletions(-)
diff --git a/dom/events/ContentEventHandler.cpp b/dom/events/ContentEventHandler.cpp
@@ -2974,9 +2974,7 @@ nsresult ContentEventHandler::OnQueryCharacterAtPoint(
// a popup but the rootFrame is the document root.
if (rootWidget != aEvent->mWidget) {
MOZ_ASSERT(aEvent->mWidget, "The event must have the widget");
- nsView* view = nsView::GetViewFor(aEvent->mWidget);
- NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
- rootFrame = view->GetFrame();
+ rootFrame = aEvent->mWidget->GetFrame();
NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE);
rootWidget = rootFrame->GetNearestWidget();
NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE);
diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -33,6 +33,7 @@
#include "mozilla/dom/Document.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsLayoutUtils.h"
+#include "nsMenuPopupFrame.h"
#include "nsPrintfCString.h"
#include "nsPIDOMWindow.h"
#include "nsRefreshDriver.h"
@@ -644,20 +645,14 @@ static dom::Element* GetDisplayportElementFor(
static dom::Element* GetRootElementFor(nsIWidget* aWidget) {
// This returns the root element that ChromeProcessController sets the
// displayport on during initialization.
- if (nsView* view = nsView::GetViewFor(aWidget)) {
- if (aWidget->GetWindowType() == widget::WindowType::Popup) {
- MOZ_ASSERT(view->GetFrame() && view->GetFrame()->IsMenuPopupFrame() &&
- view->GetFrame()->GetContent() &&
- view->GetFrame()->GetContent()->IsElement());
- return view->GetFrame()->GetContent()->AsElement();
- }
-
- if (PresShell* presShell = view->GetPresShell()) {
- MOZ_ASSERT(presShell->GetDocument());
- return presShell->GetDocument()->GetDocumentElement();
- }
+ auto* frame = aWidget->GetFrame();
+ if (!frame) {
+ return nullptr;
}
- return nullptr;
+ if (frame->IsMenuPopupFrame()) {
+ return frame->GetContent()->AsElement();
+ }
+ return frame->PresContext()->Document()->GetDocumentElement();
}
namespace {
@@ -809,15 +804,11 @@ void DisplayportSetListener::OnPostRefresh() {
nsIFrame* GetRootFrameForWidget(const nsIWidget* aWidget,
const PresShell* aPresShell) {
- if (aWidget->GetWindowType() == widget::WindowType::Popup) {
+ if (auto* popup = aWidget->GetPopupFrame()) {
// In the case where the widget is popup window and uses APZ, the widget
// frame (i.e. menu popup frame) is the reference frame used for building
// the display list for hit-testing inside the popup.
- MOZ_ASSERT(aWidget->AsyncPanZoomEnabled());
- if (nsView* view = nsView::GetViewFor(aWidget)) {
- MOZ_ASSERT(view->GetFrame() && view->GetFrame()->IsMenuPopupFrame());
- return view->GetFrame();
- }
+ return popup;
}
return aPresShell->GetRootFrame();
@@ -983,31 +974,30 @@ void APZCCallbackHelper::CancelAutoscroll(
void APZCCallbackHelper::NotifyScaleGestureComplete(
const nsCOMPtr<nsIWidget>& aWidget, float aScale) {
MOZ_ASSERT(NS_IsMainThread());
-
- if (nsView* view = nsView::GetViewFor(aWidget)) {
- if (PresShell* presShell = view->GetPresShell()) {
- dom::Document* doc = presShell->GetDocument();
- MOZ_ASSERT(doc);
- if (nsPIDOMWindowInner* win = doc->GetInnerWindow()) {
- dom::AutoJSAPI jsapi;
- if (!jsapi.Init(win)) {
- return;
- }
-
- JSContext* cx = jsapi.cx();
- JS::Rooted<JS::Value> detail(cx, JS::Float32Value(aScale));
- RefPtr<dom::CustomEvent> event =
- NS_NewDOMCustomEvent(doc, nullptr, nullptr);
- event->InitCustomEvent(cx, u"MozScaleGestureComplete"_ns,
- /* CanBubble */ true,
- /* Cancelable */ false, detail);
- event->SetTrusted(true);
- auto* dispatcher = new AsyncEventDispatcher(doc, event.forget(),
- ChromeOnlyDispatch::eYes);
- dispatcher->PostDOMEvent();
- }
- }
+ nsIFrame* frame = aWidget->GetFrame();
+ if (!frame) {
+ return;
+ }
+ dom::Document* doc = frame->PresShell()->GetDocument();
+ MOZ_ASSERT(doc);
+ nsPIDOMWindowInner* win = doc->GetInnerWindow();
+ if (!win) {
+ return;
+ }
+ dom::AutoJSAPI jsapi;
+ if (!jsapi.Init(win)) {
+ return;
}
+ JSContext* cx = jsapi.cx();
+ JS::Rooted<JS::Value> detail(cx, JS::Float32Value(aScale));
+ RefPtr<dom::CustomEvent> event = NS_NewDOMCustomEvent(doc, nullptr, nullptr);
+ event->InitCustomEvent(cx, u"MozScaleGestureComplete"_ns,
+ /* CanBubble */ true,
+ /* Cancelable */ false, detail);
+ event->SetTrusted(true);
+ auto* dispatcher =
+ new AsyncEventDispatcher(doc, event.forget(), ChromeOnlyDispatch::eYes);
+ dispatcher->PostDOMEvent();
}
/* static */
diff --git a/gfx/layers/apz/util/ChromeProcessController.cpp b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -113,10 +113,8 @@ PresShell* ChromeProcessController::GetPresShell() const {
if (!mWidget) {
return nullptr;
}
- if (nsView* view = nsView::GetViewFor(mWidget)) {
- return view->GetPresShell();
- }
- return nullptr;
+ auto* frame = mWidget->GetFrame();
+ return frame ? frame->PresShell() : nullptr;
}
dom::Document* ChromeProcessController::GetRootDocument() const {
@@ -148,7 +146,7 @@ void ChromeProcessController::HandleDoubleTap(
MOZ_ASSERT(mUIThread->IsOnCurrentThread());
RefPtr<dom::Document> document = GetRootContentDocument(aGuid.mScrollId);
- if (!document.get()) {
+ if (!document) {
return;
}
@@ -375,14 +373,5 @@ void ChromeProcessController::NotifyScaleGestureComplete(
}
nsIFrame* ChromeProcessController::GetWidgetFrame() const {
- if (!mWidget) {
- return nullptr;
- }
-
- nsView* view = nsView::GetViewFor(mWidget);
- if (!view) {
- return nullptr;
- }
-
- return view->GetFrame();
+ return mWidget ? mWidget->GetFrame() : nullptr;
}
diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp
@@ -1602,7 +1602,7 @@ nsIFrame* nsLayoutUtils::GetPopupFrameForPoint(
continue;
}
if (aFlags & GetPopupFrameForPointFlags::OnlyReturnFramesWithWidgets) {
- if (!popup->GetView() || !popup->GetView()->HasWidget()) {
+ if (!popup->GetWidget()) {
continue;
}
}
diff --git a/layout/xul/moz.build b/layout/xul/moz.build
@@ -15,6 +15,7 @@ if CONFIG["ENABLE_TESTS"]:
EXPORTS += [
"nsIPopupContainer.h",
"nsIScrollbarMediator.h",
+ "nsMenuPopupFrame.h",
"nsXULPopupManager.h",
"nsXULTooltipListener.h",
]
diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
@@ -1199,10 +1199,6 @@ bool nsWindow::IsWaylandPopup() const {
return GdkIsWaylandDisplay() && IsPopup();
}
-static nsMenuPopupFrame* GetMenuPopupFrame(nsIFrame* aFrame) {
- return do_QueryFrame(aFrame);
-}
-
void nsWindow::AppendPopupToHierarchyList(nsWindow* aToplevelWindow) {
mWaylandToplevel = aToplevelWindow;
@@ -1597,7 +1593,7 @@ void nsWindow::WaylandPopupHierarchyCalculatePositions() {
(int)(popup->mBounds.height / FractionalScaleFactor()));
#ifdef MOZ_LOGGING
if (LOG_ENABLED()) {
- if (nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame())) {
+ if (nsMenuPopupFrame* popupFrame = GetPopupFrame()) {
auto r = LayoutDeviceRect::FromAppUnitsRounded(
popupFrame->GetRect(),
popupFrame->PresContext()->AppUnitsPerDevPixel());
@@ -1634,7 +1630,7 @@ void nsWindow::WaylandPopupHierarchyCalculatePositions() {
}
bool nsWindow::WaylandPopupIsContextMenu() {
- nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
+ nsMenuPopupFrame* popupFrame = GetPopupFrame();
if (!popupFrame) {
return false;
}
@@ -1642,7 +1638,7 @@ bool nsWindow::WaylandPopupIsContextMenu() {
}
bool nsWindow::WaylandPopupIsPermanent() {
- nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
+ nsMenuPopupFrame* popupFrame = GetPopupFrame();
if (!popupFrame) {
// We can always hide popups without frames.
return false;
@@ -1651,7 +1647,7 @@ bool nsWindow::WaylandPopupIsPermanent() {
}
bool nsWindow::WaylandPopupIsAnchored() {
- nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
+ nsMenuPopupFrame* popupFrame = GetPopupFrame();
if (!popupFrame) {
// We can always hide popups without frames.
return false;
@@ -1660,10 +1656,10 @@ bool nsWindow::WaylandPopupIsAnchored() {
}
bool nsWindow::IsWidgetOverflowWindow() {
- if (this->GetFrame() && this->GetFrame()->GetContent()->GetID()) {
- nsCString nodeId;
- this->GetFrame()->GetContent()->GetID()->ToUTF8String(nodeId);
- return nodeId.Equals("widget-overflow");
+ if (auto* frame = GetPopupFrame()) {
+ if (nsAtom* id = frame->GetContent()->GetID()) {
+ return id->Equals(u"widget-overflow"_ns);
+ }
}
return false;
}
@@ -1760,7 +1756,7 @@ bool nsWindow::WaylandPopupConfigure() {
}
// Don't track popups without frame
- nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
+ nsMenuPopupFrame* popupFrame = GetPopupFrame();
if (!popupFrame) {
return false;
}
@@ -1839,7 +1835,7 @@ bool nsWindow::IsInPopupHierarchy() {
void nsWindow::AddWindowToPopupHierarchy() {
LOG("nsWindow::AddWindowToPopupHierarchy\n");
- if (!GetFrame()) {
+ if (!GetPopupFrame()) {
LOG(" Window without frame cannot be added as popup!\n");
return;
}
@@ -2038,7 +2034,7 @@ void nsWindow::WaylandPopupPropagateChangesToLayout(bool aMove, bool aResize) {
if (aResize) {
LOG(" needSizeUpdate\n");
- if (nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame())) {
+ if (nsMenuPopupFrame* popupFrame = GetPopupFrame()) {
RefPtr<PresShell> presShell = popupFrame->PresShell();
presShell->FrameNeedsReflow(popupFrame, IntrinsicDirty::None,
NS_FRAME_IS_DIRTY);
@@ -2171,7 +2167,7 @@ static GdkGravity PopupAlignmentToGdkGravity(int8_t aAlignment) {
}
bool nsWindow::IsPopupDirectionRTL() {
- nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
+ nsMenuPopupFrame* popupFrame = GetPopupFrame();
return popupFrame && popupFrame->IsDirectionRTL();
}
@@ -2483,7 +2479,7 @@ const nsWindow::WaylandPopupMoveToRectParams
nsWindow::WaylandPopupGetPositionFromLayout() {
LOG("nsWindow::WaylandPopupGetPositionFromLayout\n");
- nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
+ nsMenuPopupFrame* popupFrame = GetPopupFrame();
const bool isTopContextMenu = mPopupContextMenu && !mPopupAnchored;
const bool isRTL = popupFrame->IsDirectionRTL();
@@ -2630,7 +2626,7 @@ bool nsWindow::WaylandPopupCheckAndGetAnchor(GdkRectangle* aPopupAnchor,
LOG("nsWindow::WaylandPopupCheckAndGetAnchor");
GdkWindow* gdkWindow = GetToplevelGdkWindow();
- nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
+ nsMenuPopupFrame* popupFrame = GetPopupFrame();
if (!gdkWindow || !popupFrame) {
LOG(" can't use move-to-rect due missing gdkWindow or popupFrame");
return false;
@@ -9735,14 +9731,6 @@ static nsIFrame* FindTitlebarFrame(nsIFrame* aFrame) {
return nullptr;
}
-nsIFrame* nsWindow::GetFrame() const {
- nsView* view = nsView::GetViewFor(this);
- if (!view) {
- return nullptr;
- }
- return view->GetFrame();
-}
-
void nsWindow::UpdateMozWindowActive() {
// Update activation state for the :-moz-window-inactive pseudoclass.
// Normally, this follows focus; we override it here to follow
@@ -10034,7 +10022,7 @@ void nsWindow::SetDragSource(GdkDragContext* aSourceDragContext) {
mSourceDragContext = aSourceDragContext;
if (IsPopup() &&
(widget::GdkIsWaylandDisplay() || widget::IsXWaylandProtocol())) {
- if (auto* menuPopupFrame = GetMenuPopupFrame(GetFrame())) {
+ if (auto* menuPopupFrame = GetPopupFrame()) {
menuPopupFrame->SetIsDragSource(!!aSourceDragContext);
}
}
diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h
@@ -341,7 +341,6 @@ class nsWindow final : public nsBaseWidget {
GdkWindow* GetGdkWindow() const { return mGdkWindow; };
GdkWindow* GetToplevelGdkWindow() const;
GtkWidget* GetGtkWidget() const { return mShell; }
- nsIFrame* GetFrame() const;
nsWindow* GetEffectiveParent();
bool IsDestroyed() const { return mIsDestroyed; }
bool IsPopup() const;
diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp
@@ -74,6 +74,7 @@
#include "nsIScreenManager.h"
#include "nsISimpleEnumerator.h"
#include "nsIWidgetListener.h"
+#include "nsMenuPopupFrame.h"
#include "nsRefPtrHashtable.h"
#include "nsServiceManagerUtils.h"
#include "nsWidgetsCID.h"
@@ -446,6 +447,22 @@ void nsIWidget::RemoveAllChildren() {
}
}
+nsIFrame* nsIWidget::GetFrame() const {
+ if (nsView* view = nsView::GetViewFor(this)) {
+ return view->GetFrame();
+ }
+ return nullptr;
+}
+
+nsMenuPopupFrame* nsIWidget::GetPopupFrame() const {
+ if (mWindowType != WindowType::Popup) {
+ return nullptr;
+ }
+ auto* frame = GetFrame();
+ MOZ_ASSERT_IF(frame, frame->IsMenuPopupFrame());
+ return do_QueryFrame(frame);
+}
+
void nsBaseWidget::DynamicToolbarOffsetChanged(
mozilla::ScreenIntCoord aOffset) {
if (mCompositorBridgeChild) {
diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h
@@ -48,7 +48,7 @@
class nsIBidiKeyboard;
class nsIRollupListener;
class nsIContent;
-class ViewWrapper;
+class nsMenuPopupFrame;
class nsIRunnable;
namespace mozilla {
@@ -2038,6 +2038,11 @@ class nsIWidget : public nsISupports {
static already_AddRefed<nsIBidiKeyboard> CreateBidiKeyboard();
+ // If this is a popup, returns the associated frame if any.
+ nsMenuPopupFrame* GetPopupFrame() const;
+ // Returns the frame currently associated to this widget.
+ nsIFrame* GetFrame() const;
+
/**
* Like GetDefaultScale, but taking into account only the system settings
* and ignoring Gecko preferences.
diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp
@@ -80,6 +80,7 @@
#include "mozilla/widget/WinEventObserver.h"
#include "mozilla/widget/WinMessages.h"
#include "nsLookAndFeel.h"
+#include "nsMenuPopupFrame.h"
#include "nsWindow.h"
#include "nsWindowTaskbarConcealer.h"
#include "nsAppRunner.h"
@@ -7209,20 +7210,14 @@ a11y::LocalAccessible* nsWindow::GetAccessible() {
}
// In case of popup window return a popup accessible.
- nsView* view = nsView::GetViewFor(this);
- if (view) {
- nsIFrame* frame = view->GetFrame();
- if (frame && nsLayoutUtils::IsPopup(frame)) {
- nsAccessibilityService* accService = GetOrCreateAccService();
- if (accService) {
- a11y::DocAccessible* docAcc =
- GetAccService()->GetDocAccessible(frame->PresShell());
- if (docAcc) {
- NS_LOG_WMGETOBJECT(
- this, mWnd,
- docAcc->GetAccessibleOrDescendant(frame->GetContent()));
- return docAcc->GetAccessibleOrDescendant(frame->GetContent());
- }
+ if (auto* frame = GetPopupFrame()) {
+ if (nsAccessibilityService* accService = GetOrCreateAccService()) {
+ a11y::DocAccessible* docAcc =
+ accService->GetDocAccessible(frame->PresShell());
+ if (docAcc) {
+ NS_LOG_WMGETOBJECT(
+ this, mWnd, docAcc->GetAccessibleOrDescendant(frame->GetContent()));
+ return docAcc->GetAccessibleOrDescendant(frame->GetContent());
}
}
}