Bug 1705120 - Notify nsMenuX observers when a menu item is about to be activated, and supply the activated DOM element. r=harry

Differential Revision: https://phabricator.services.mozilla.com/D114434
This commit is contained in:
Markus Stange 2021-05-07 17:00:20 +00:00
parent 65997668d2
commit 607f63b94a
4 changed files with 58 additions and 4 deletions

View File

@ -49,6 +49,8 @@ class NativeMenuMac : public NativeMenu,
// nsMenuX::Observer
void OnMenuWillOpen(mozilla::dom::Element* aPopupElement) override;
void OnMenuDidOpen(mozilla::dom::Element* aPopupElement) override;
void OnMenuWillActivateItem(mozilla::dom::Element* aPopupElement,
mozilla::dom::Element* aMenuItemElement) override;
void OnMenuClosed(mozilla::dom::Element* aPopupElement) override;
NSMenu* NativeNSMenu() { return mMenu ? mMenu->NativeNSMenu() : nil; }

View File

@ -192,6 +192,9 @@ void NativeMenuMac::OnMenuDidOpen(dom::Element* aPopupElement) {
}
}
void NativeMenuMac::OnMenuWillActivateItem(mozilla::dom::Element* aPopupElement,
mozilla::dom::Element* aMenuItemElement) {}
void NativeMenuMac::OnMenuClosed(dom::Element* aPopupElement) {
// Our caller isn't keeping us alive, so make sure we stay alive throughout this function in case
// one of the observer notifications destroys us.
@ -309,14 +312,16 @@ void NativeMenuMac::ActivateItem(dom::Element* aItemElement, Modifiers aModifier
aRv.ThrowInvalidStateError("Menu containing menu item is not open");
return;
}
Maybe<nsMenuX::MenuChild> item = menu->GetItemForElement(aItemElement);
if (!item || !item->is<RefPtr<nsMenuItemX>>()) {
Maybe<nsMenuX::MenuChild> child = menu->GetItemForElement(aItemElement);
if (!child || !child->is<RefPtr<nsMenuItemX>>()) {
aRv.ThrowInvalidStateError("Could not find the supplied menu item");
return;
}
menu->ActivateItemAfterClosing(std::move(item->as<RefPtr<nsMenuItemX>>()),
ConvertModifierFlags(aModifiers), aButton);
RefPtr<nsMenuItemX> item = std::move(child->as<RefPtr<nsMenuItemX>>());
NSMenuItem* nativeItem = [item->NativeNSMenuItem() retain];
menu->ActivateItemAfterClosing(std::move(item), ConvertModifierFlags(aModifiers), aButton);
// Notify the entire menu structure that the menu is closing in response to ActivateItem.
mMenu->MenuClosed(true);
@ -328,6 +333,13 @@ void NativeMenuMac::ActivateItem(dom::Element* aItemElement, Modifiers aModifier
// handler, at least on macOS 11. However, the resulting MenuClosed call will not do anything
// because we already called MenuClosed above.
[mMenu->NativeNSMenu() cancelTrackingWithoutAnimation];
// Call OnWillActivateItem at the end, to match the order of calls that happen when a user
// activates a menu item in the real world: -[MenuDelegate menu:willActivateItem:] runs after
// menuDidClose.
menu->OnWillActivateItem(nativeItem);
[nativeItem release];
}
void NativeMenuMac::OpenSubmenu(dom::Element* aMenuElement) {

View File

@ -47,6 +47,10 @@ class nsMenuXObserver {
// No strong reference is held to the observer during the call.
virtual void OnMenuDidOpen(mozilla::dom::Element* aPopupElement) = 0;
// Called before a menu item is activated.
virtual void OnMenuWillActivateItem(mozilla::dom::Element* aPopupElement,
mozilla::dom::Element* aMenuItemElement) = 0;
// Called when a menu in this menu subtree closed, after popuphidden.
// No strong reference is held to the observer during the call.
virtual void OnMenuClosed(mozilla::dom::Element* aPopupElement) = 0;
@ -79,6 +83,8 @@ class nsMenuX final : public nsMenuParentX,
// nsMenuXObserver, to forward notifications from our children to our observer.
void OnMenuWillOpen(mozilla::dom::Element* aPopupElement) override;
void OnMenuDidOpen(mozilla::dom::Element* aPopupElement) override;
void OnMenuWillActivateItem(mozilla::dom::Element* aPopupElement,
mozilla::dom::Element* aMenuItemElement) override;
void OnMenuClosed(mozilla::dom::Element* aPopupElement) override;
bool IsVisible() const { return mVisible; }
@ -138,6 +144,10 @@ class nsMenuX final : public nsMenuParentX,
// mNativeMenu.
void OnHighlightedItemChanged(const mozilla::Maybe<uint32_t>& aNewHighlightedIndex);
// Called from the menu delegate before an item anywhere in this menu is activated.
// Called after MenuClosed().
void OnWillActivateItem(NSMenuItem* aItem);
void SetRebuild(bool aMenuEvent);
void SetupIcon();
nsIContent* Content() { return mContent; }

View File

@ -189,6 +189,13 @@ void nsMenuX::OnMenuDidOpen(dom::Element* aPopupElement) {
}
}
void nsMenuX::OnMenuWillActivateItem(dom::Element* aPopupElement, dom::Element* aMenuItemElement) {
RefPtr<nsMenuX> kungFuDeathGrip(this);
if (mObserver) {
mObserver->OnMenuWillActivateItem(aPopupElement, aMenuItemElement);
}
}
void nsMenuX::OnMenuClosed(dom::Element* aPopupElement) {
RefPtr<nsMenuX> kungFuDeathGrip(this);
if (mObserver) {
@ -681,6 +688,22 @@ void nsMenuX::OnHighlightedItemChanged(const Maybe<uint32_t>& aNewHighlightedInd
mHighlightedItemIndex = aNewHighlightedIndex;
}
void nsMenuX::OnWillActivateItem(NSMenuItem* aItem) {
if (!mIsOpenForGecko) {
return;
}
if (mMenuGroupOwner && mObserver) {
nsMenuItemX* item = mMenuGroupOwner->GetMenuItemForCommandID(uint32_t(aItem.tag));
if (item && item->Content()->IsElement()) {
RefPtr<dom::Element> itemElement = item->Content()->AsElement();
if (nsCOMPtr<nsIContent> popupContent = GetMenuPopupContent()) {
mObserver->OnMenuWillActivateItem(popupContent->AsElement(), itemElement);
}
}
}
}
void nsMenuX::RebuildMenu() {
MOZ_RELEASE_ASSERT(mNeedsRebuild);
gConstructingMenu = true;
@ -1159,6 +1182,13 @@ void nsMenuX::Dump(uint32_t aIndent) const {
// This is called after menuDidClose:.
- (void)menu:(NSMenu*)aMenu willActivateItem:(NSMenuItem*)aItem {
if (!mGeckoMenu) {
return;
}
// Hold a strong reference to mGeckoMenu while calling its methods.
RefPtr<nsMenuX> geckoMenu = mGeckoMenu;
geckoMenu->OnWillActivateItem(aItem);
}
@end