Skip to content

Commit 3af23e7

Browse files
committedDec 10, 2024
Bug 1936194 - Rebuild popups when swapped into another frameloader. r=tnikkel,layout-reviewers
That is, recreate it if we're parented to the wrong widget. This can happen with swapFrameLoaders. We could (and should, maybe) do this in SwapFrameLoaders instead. But we already have logic to re-show widgets on some situations in nsMenuPopupFrame, so seems worth reusing that. Fully move parent widget computation to nsMenuPopupFrame. Differential Revision: https://phabricator.services.mozilla.com/D231668
1 parent a10b757 commit 3af23e7

File tree

3 files changed

+53
-47
lines changed

3 files changed

+53
-47
lines changed
 

‎layout/xul/nsMenuPopupFrame.cpp

+47-34
Original file line numberDiff line numberDiff line change
@@ -238,15 +238,17 @@ widget::PopupLevel nsMenuPopupFrame::GetPopupLevel(bool aIsNoAutoHide) const {
238238
return sDefaultLevelIsTop ? PopupLevel::Top : PopupLevel::Parent;
239239
}
240240

241-
void nsMenuPopupFrame::PrepareWidget(bool aRecreate) {
241+
void nsMenuPopupFrame::PrepareWidget(bool aForceRecreate) {
242242
nsView* ourView = GetView();
243-
if (aRecreate) {
244-
if (auto* widget = GetWidget()) {
243+
if (auto* widget = GetWidget()) {
244+
nsCOMPtr<nsIWidget> parent = ComputeParentWidget();
245+
if (aForceRecreate || widget->GetParent() != parent ||
246+
widget->NeedsRecreateToReshow()) {
245247
// Widget's WebRender resources needs to be cleared before creating new
246248
// widget.
247249
widget->ClearCachedWebrenderResources();
250+
ourView->DestroyWidget();
248251
}
249-
ourView->DestroyWidget();
250252
}
251253
if (!ourView->HasWidget()) {
252254
CreateWidgetForView(ourView);
@@ -255,6 +257,35 @@ void nsMenuPopupFrame::PrepareWidget(bool aRecreate) {
255257
}
256258
}
257259

260+
already_AddRefed<nsIWidget> nsMenuPopupFrame::ComputeParentWidget() const {
261+
auto popupLevel = GetPopupLevel(IsNoAutoHide());
262+
// Panels which have a parent level need a parent widget. This allows them to
263+
// always appear in front of the parent window but behind other windows that
264+
// should be in front of it.
265+
nsCOMPtr<nsIWidget> parentWidget;
266+
if (popupLevel != PopupLevel::Top) {
267+
nsCOMPtr<nsIDocShellTreeItem> dsti = PresContext()->GetDocShell();
268+
if (!dsti) {
269+
return nullptr;
270+
}
271+
272+
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
273+
dsti->GetTreeOwner(getter_AddRefs(treeOwner));
274+
if (!treeOwner) {
275+
return nullptr;
276+
}
277+
278+
nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(treeOwner));
279+
if (baseWindow) {
280+
baseWindow->GetMainWidget(getter_AddRefs(parentWidget));
281+
}
282+
}
283+
if (!parentWidget && mView && mView->GetParent()) {
284+
parentWidget = mView->GetParent()->GetNearestWidget(nullptr);
285+
}
286+
return parentWidget.forget();
287+
}
288+
258289
nsresult nsMenuPopupFrame::CreateWidgetForView(nsView* aView) {
259290
// Create a widget for ourselves.
260291
widget::InitData widgetData;
@@ -273,33 +304,16 @@ nsresult nsMenuPopupFrame::CreateWidgetForView(nsView* aView) {
273304
}
274305
}
275306

276-
bool remote = HasRemoteContent();
307+
const bool remote = HasRemoteContent();
277308

278309
const auto mode = nsLayoutUtils::GetFrameTransparency(this, this);
279310
widgetData.mHasRemoteContent = remote;
280311
widgetData.mTransparencyMode = mode;
281312
widgetData.mPopupLevel = GetPopupLevel(widgetData.mNoAutoHide);
282313

283-
// Panels which have a parent level need a parent widget. This allows them to
284-
// always appear in front of the parent window but behind other windows that
285-
// should be in front of it.
286-
nsCOMPtr<nsIWidget> parentWidget;
287-
if (widgetData.mPopupLevel != PopupLevel::Top) {
288-
nsCOMPtr<nsIDocShellTreeItem> dsti = PresContext()->GetDocShell();
289-
if (!dsti) {
290-
return NS_ERROR_FAILURE;
291-
}
292-
293-
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
294-
dsti->GetTreeOwner(getter_AddRefs(treeOwner));
295-
if (!treeOwner) {
296-
return NS_ERROR_FAILURE;
297-
}
298-
299-
nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(treeOwner));
300-
if (baseWindow) {
301-
baseWindow->GetMainWidget(getter_AddRefs(parentWidget));
302-
}
314+
nsCOMPtr<nsIWidget> parentWidget = ComputeParentWidget();
315+
if (NS_WARN_IF(!parentWidget)) {
316+
return NS_ERROR_FAILURE;
303317
}
304318

305319
nsresult rv = aView->CreateWidgetForPopup(&widgetData, parentWidget);
@@ -308,8 +322,9 @@ nsresult nsMenuPopupFrame::CreateWidgetForView(nsView* aView) {
308322
}
309323

310324
nsIWidget* widget = aView->GetWidget();
311-
widget->SetTransparencyMode(mode);
325+
MOZ_ASSERT(widget->GetParent() == parentWidget);
312326

327+
widget->SetTransparencyMode(mode);
313328
PropagateStyleToWidget();
314329

315330
return NS_OK;
@@ -757,9 +772,7 @@ void nsMenuPopupFrame::InitializePopup(nsIContent* aAnchorContent,
757772
int32_t aXPos, int32_t aYPos,
758773
MenuPopupAnchorType aAnchorType,
759774
bool aAttributesOverride) {
760-
auto* widget = GetWidget();
761-
bool recreateWidget = widget && widget->NeedsRecreateToReshow();
762-
PrepareWidget(recreateWidget);
775+
PrepareWidget();
763776

764777
mPopupState = ePopupShowing;
765778
mAnchorContent = aAnchorContent;
@@ -887,9 +900,7 @@ void nsMenuPopupFrame::InitializePopup(nsIContent* aAnchorContent,
887900
void nsMenuPopupFrame::InitializePopupAtScreen(nsIContent* aTriggerContent,
888901
int32_t aXPos, int32_t aYPos,
889902
bool aIsContextMenu) {
890-
auto* widget = GetWidget();
891-
bool recreateWidget = widget && widget->NeedsRecreateToReshow();
892-
PrepareWidget(recreateWidget);
903+
PrepareWidget();
893904

894905
mPopupState = ePopupShowing;
895906
mAnchorContent = nullptr;
@@ -2080,10 +2091,12 @@ nsresult nsMenuPopupFrame::AttributeChanged(int32_t aNameSpaceID,
20802091
MoveToAttributePosition();
20812092
}
20822093

2083-
if (aAttribute == nsGkAtoms::remote) {
2094+
if (aAttribute == nsGkAtoms::remote && GetWidget()) {
20842095
// When the remote attribute changes, we need to create a new widget to
20852096
// ensure that it has the correct compositor and transparency settings to
2086-
// match the new value.
2097+
// match the new value. Do that only if we already have a widget.
2098+
// TODO(emilio): We should consider doing it only when we get re-shown or
2099+
// so.
20872100
PrepareWidget(true);
20882101
}
20892102

‎layout/xul/nsMenuPopupFrame.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ class nsMenuPopupFrame final : public nsBlockFrame {
173173
nsReflowStatus& aStatus) override;
174174

175175
nsIWidget* GetWidget() const;
176+
already_AddRefed<nsIWidget> ComputeParentWidget() const;
176177

177178
enum class WidgetStyle : uint8_t {
178179
ColorScheme,
@@ -213,9 +214,10 @@ class nsMenuPopupFrame final : public nsBlockFrame {
213214
PopupLevel GetPopupLevel() const { return GetPopupLevel(IsNoAutoHide()); }
214215

215216
// Ensure that a widget has already been created for this view, and create
216-
// one if it hasn't. If aRecreate is true, destroys any existing widget and
217-
// creates a new one, regardless of whether one has already been created.
218-
void PrepareWidget(bool aRecreate = false);
217+
// one if it hasn't. If aForceRecreate is true, destroys any existing widget
218+
// and creates a new one, regardless of whether one has already been created.
219+
// Otherwise does so only if needed.
220+
void PrepareWidget(bool aForceRecreate = false);
219221

220222
MOZ_CAN_RUN_SCRIPT void EnsureActiveMenuListItemIsVisible();
221223

‎view/nsView.cpp

+1-10
Original file line numberDiff line numberDiff line change
@@ -526,19 +526,10 @@ nsresult nsView::CreateWidgetForPopup(widget::InitData* aWidgetInitData,
526526
MOZ_ASSERT(aWidgetInitData, "Widget init data required");
527527
MOZ_ASSERT(aWidgetInitData->mWindowType == WindowType::Popup,
528528
"Use one of the other CreateWidget methods");
529+
MOZ_ASSERT(aParent);
529530

530531
LayoutDeviceIntRect trect = CalcWidgetBounds(
531532
aWidgetInitData->mWindowType, aWidgetInitData->mTransparencyMode);
532-
533-
if (!aParent && GetParent()) {
534-
aParent = GetParent()->GetNearestWidget(nullptr);
535-
}
536-
if (!aParent) {
537-
NS_ERROR("nsView::CreateWidgetForPopup without suitable parent widget??");
538-
// Without a parent, we can't make a popup. This used to be able to happen
539-
// when printing, apparently.
540-
return NS_ERROR_FAILURE;
541-
}
542533
mWindow = aParent->CreateChild(trect, *aWidgetInitData);
543534
if (!mWindow) {
544535
return NS_ERROR_FAILURE;

0 commit comments

Comments
 (0)
Failed to load comments.