Skip to content

Commit 81332ea

Browse files
fix: revert allow disabling all NSMenuItems, fix menu crash (#48800)
Revert "fix: allow disabling all `NSMenuItems` (#48598)" This reverts commit 0cb4fdd. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Keeley Hammond <[email protected]>
1 parent a06d00d commit 81332ea

File tree

2 files changed

+50
-82
lines changed

2 files changed

+50
-82
lines changed

lib/browser/api/menu.ts

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,11 @@ Menu.prototype._isCommandIdChecked = function (id) {
2525
};
2626

2727
Menu.prototype._isCommandIdEnabled = function (id) {
28-
const item = this.commandsMap[id];
29-
if (!item) return false;
30-
31-
const focusedWindow = BaseWindow.getFocusedWindow();
32-
33-
if (item.role === 'minimize' && focusedWindow) {
34-
return focusedWindow.isMinimizable();
35-
}
36-
37-
if (item.role === 'togglefullscreen' && focusedWindow) {
38-
return focusedWindow.isFullScreenable();
39-
}
40-
41-
if (item.role === 'close' && focusedWindow) {
42-
return focusedWindow.isClosable();
43-
}
44-
45-
return item.enabled;
28+
return this.commandsMap[id] ? this.commandsMap[id].enabled : false;
4629
};
47-
4830
Menu.prototype._shouldCommandIdWorkWhenHidden = function (id) {
4931
return this.commandsMap[id]?.acceleratorWorksWhenHidden ?? false;
5032
};
51-
5233
Menu.prototype._isCommandIdVisible = function (id) {
5334
return this.commandsMap[id]?.visible ?? false;
5435
};

shell/browser/ui/cocoa/electron_menu_controller.mm

Lines changed: 49 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ bool MenuHasVisibleItems(const electron::ElectronMenuModel* model) {
9090
// "(empty)" into the submenu. Matches Windows behavior.
9191
NSMenu* MakeEmptySubmenu() {
9292
NSMenu* submenu = [[NSMenu alloc] initWithTitle:@""];
93-
submenu.autoenablesItems = NO;
9493
NSString* empty_menu_title =
9594
l10n_util::GetNSString(IDS_APP_MENU_EMPTY_SUBMENU);
9695

@@ -232,9 +231,6 @@ - (void)cancel {
232231
// be invoked recursively.
233232
- (NSMenu*)menuFromModel:(electron::ElectronMenuModel*)model {
234233
NSMenu* menu = [[NSMenu alloc] initWithTitle:@""];
235-
// We manually manage enabled/disabled/hidden state for every item,
236-
// including Cocoa role-based selectors.
237-
menu.autoenablesItems = NO;
238234

239235
const int count = model->GetItemCount();
240236
for (int index = 0; index < count; index++) {
@@ -244,7 +240,6 @@ - (NSMenu*)menuFromModel:(electron::ElectronMenuModel*)model {
244240
[self addItemToMenu:menu atIndex:index fromModel:model];
245241
}
246242

247-
menu.delegate = self;
248243
return menu;
249244
}
250245

@@ -299,11 +294,9 @@ - (NSMenu*)createShareMenuForItem:(const SharingItem&)item {
299294
if ([items count] == 0)
300295
return MakeEmptySubmenu();
301296
NSMenu* menu = [[NSMenu alloc] init];
302-
menu.autoenablesItems = NO;
303297
NSArray* services = [NSSharingService sharingServicesForItems:items];
304298
for (NSSharingService* service in services)
305299
[menu addItem:[self menuItemForService:service withItems:items]];
306-
[menu setDelegate:self];
307300
return menu;
308301
}
309302

@@ -360,22 +353,27 @@ - (NSMenuItem*)makeMenuItemForIndex:(NSInteger)index
360353
std::u16string title = u"Services";
361354
NSString* sub_label = l10n_util::FixUpWindowsStyleLabel(title);
362355

363-
item.target = nil;
364-
item.action = nil;
356+
[item setTarget:nil];
357+
[item setAction:nil];
365358
NSMenu* submenu = [[NSMenu alloc] initWithTitle:sub_label];
366-
item.submenu = submenu;
359+
[item setSubmenu:submenu];
367360
[NSApp setServicesMenu:submenu];
368361
} else if (role == u"sharemenu") {
369362
SharingItem sharing_item;
370363
model->GetSharingItemAt(index, &sharing_item);
371-
item.target = nil;
372-
item.action = nil;
364+
[item setTarget:nil];
365+
[item setAction:nil];
373366
[item setSubmenu:[self createShareMenuForItem:sharing_item]];
374367
} else if (type == electron::ElectronMenuModel::TYPE_SUBMENU &&
375368
model->IsVisibleAt(index)) {
369+
// We need to specifically check that the submenu top-level item has been
370+
// enabled as it's not validated by validateUserInterfaceItem
371+
if (!model->IsEnabledAt(index))
372+
[item setEnabled:NO];
373+
376374
// Recursively build a submenu from the sub-model at this index.
377-
item.target = nil;
378-
item.action = nil;
375+
[item setTarget:nil];
376+
[item setAction:nil];
379377
electron::ElectronMenuModel* submenuModel =
380378
static_cast<electron::ElectronMenuModel*>(
381379
model->GetSubmenuModelAt(index));
@@ -390,12 +388,8 @@ - (NSMenuItem*)makeMenuItemForIndex:(NSInteger)index
390388
}
391389
}
392390

393-
submenu.title = item.title;
394-
item.submenu = submenu;
395-
item.tag = index;
396-
item.representedObject =
397-
[WeakPtrToElectronMenuModelAsNSObject weakPtrForModel:model];
398-
submenu.delegate = self;
391+
[submenu setTitle:[item title]];
392+
[item setSubmenu:submenu];
399393

400394
// Set submenu's role.
401395
if ((role == u"window" || role == u"windowmenu") && [submenu numberOfItems])
@@ -410,9 +404,9 @@ - (NSMenuItem*)makeMenuItemForIndex:(NSInteger)index
410404
// the model so hierarchical menus check the correct index in the correct
411405
// model. Setting the target to |self| allows this class to participate
412406
// in validation of the menu items.
413-
item.tag = index;
414-
item.representedObject =
415-
[WeakPtrToElectronMenuModelAsNSObject weakPtrForModel:model];
407+
[item setTag:index];
408+
[item setRepresentedObject:[WeakPtrToElectronMenuModelAsNSObject
409+
weakPtrForModel:model]];
416410
ui::Accelerator accelerator;
417411
if (model->GetAcceleratorAtWithParams(index, useDefaultAccelerator_,
418412
&accelerator)) {
@@ -440,20 +434,20 @@ - (NSMenuItem*)makeMenuItemForIndex:(NSInteger)index
440434
ui::MacKeyCodeForWindowsKeyCode(accelerator.key_code(), modifier_mask,
441435
nullptr, &character);
442436
}
443-
item.keyEquivalent = [NSString stringWithFormat:@"%C", character];
444-
item.keyEquivalentModifierMask = modifier_mask;
437+
[item setKeyEquivalent:[NSString stringWithFormat:@"%C", character]];
438+
[item setKeyEquivalentModifierMask:modifier_mask];
445439
}
446440

447441
[(id)item
448442
setAllowsKeyEquivalentWhenHidden:(model->WorksWhenHiddenAt(index))];
449443

450444
// Set menu item's role.
451-
item.target = self;
445+
[item setTarget:self];
452446
if (!role.empty()) {
453447
for (const Role& pair : kRolesMap) {
454448
if (role == base::ASCIIToUTF16(pair.role)) {
455-
item.target = nil;
456-
item.action = pair.selector;
449+
[item setTarget:nil];
450+
[item setAction:pair.selector];
457451
break;
458452
}
459453
}
@@ -463,37 +457,6 @@ - (NSMenuItem*)makeMenuItemForIndex:(NSInteger)index
463457
return item;
464458
}
465459

466-
- (void)applyStateToMenuItem:(NSMenuItem*)item {
467-
id represented = item.representedObject;
468-
if (!represented)
469-
return;
470-
471-
electron::ElectronMenuModel* model =
472-
[WeakPtrToElectronMenuModelAsNSObject getFrom:represented];
473-
if (!model)
474-
return;
475-
476-
NSInteger index = item.tag;
477-
int count = model->GetItemCount();
478-
if (index < 0 || index >= count)
479-
return;
480-
481-
item.hidden = !model->IsVisibleAt(index);
482-
item.enabled = model->IsEnabledAt(index);
483-
item.state = model->IsItemCheckedAt(index) ? NSControlStateValueOn
484-
: NSControlStateValueOff;
485-
}
486-
487-
// Recursively refreshes the menu tree starting from |menu|, applying the
488-
// model state to each menu item.
489-
- (void)refreshMenuTree:(NSMenu*)menu {
490-
for (NSMenuItem* item in menu.itemArray) {
491-
[self applyStateToMenuItem:item];
492-
if (item.submenu)
493-
[self refreshMenuTree:item.submenu];
494-
}
495-
}
496-
497460
// Adds an item or a hierarchical menu to the item at the |index|,
498461
// associated with the entry in the model identified by |modelIndex|.
499462
- (void)addItemToMenu:(NSMenu*)menu
@@ -503,6 +466,32 @@ - (void)addItemToMenu:(NSMenu*)menu
503466
atIndex:index];
504467
}
505468

469+
// Called before the menu is to be displayed to update the state (enabled,
470+
// radio, etc) of each item in the menu.
471+
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
472+
SEL action = [item action];
473+
if (action == @selector(performShare:))
474+
return YES;
475+
if (action != @selector(itemSelected:))
476+
return NO;
477+
478+
NSInteger modelIndex = [item tag];
479+
electron::ElectronMenuModel* model = [WeakPtrToElectronMenuModelAsNSObject
480+
getFrom:[(id)item representedObject]];
481+
DCHECK(model);
482+
if (model) {
483+
BOOL checked = model->IsItemCheckedAt(modelIndex);
484+
DCHECK([(id)item isKindOfClass:[NSMenuItem class]]);
485+
486+
[(id)item
487+
setState:(checked ? NSControlStateValueOn : NSControlStateValueOff)];
488+
[(id)item setHidden:(!model->IsVisibleAt(modelIndex))];
489+
490+
return model->IsEnabledAt(modelIndex);
491+
}
492+
return NO;
493+
}
494+
506495
// Called when the user chooses a particular menu item. |sender| is the menu
507496
// item chosen.
508497
- (void)itemSelected:(id)sender {
@@ -537,11 +526,10 @@ - (NSMenu*)menu {
537526
menu_ = menu;
538527
} else {
539528
menu_ = [[NSMenu alloc] initWithTitle:@""];
540-
menu_.autoenablesItems = NO;
541529
if (model_)
542530
[self populateWithModel:model_.get()];
543531
}
544-
menu_.delegate = self;
532+
[menu_ setDelegate:self];
545533
return menu_;
546534
}
547535

@@ -551,7 +539,6 @@ - (BOOL)isMenuOpen {
551539

552540
- (void)menuWillOpen:(NSMenu*)menu {
553541
isMenuOpen_ = YES;
554-
[self refreshMenuTree:menu];
555542
if (model_)
556543
model_->MenuWillShow();
557544
}

0 commit comments

Comments
 (0)