Browse Source

fix: use xib file to construct macOS Menu (#20670)

* fix: use xib file to construct macOS Menu

* add nib files back to zip manifests

* address final review comments
trop[bot] 5 years ago
parent
commit
d417bbdcfd

+ 8 - 0
BUILD.gn

@@ -26,6 +26,7 @@ if (is_mac) {
   import("//third_party/icu/config.gni")
   import("//ui/gl/features.gni")
   import("//v8/gni/v8.gni")
+  import("build/rules.gni")
 }
 
 if (is_linux) {
@@ -655,6 +656,12 @@ if (is_mac) {
   electron_framework_version = "A"
   electron_version = read_file("ELECTRON_VERSION", "trim string")
 
+  mac_xib_bundle_data("electron_xibs") {
+    sources = [
+      "shell/common/resources/mac/MainMenu.xib",
+    ]
+  }
+
   bundle_data("electron_framework_resources") {
     public_deps = [
       ":packed_resources",
@@ -769,6 +776,7 @@ if (is_mac) {
       ":electron_framework_libraries",
       ":electron_framework_resources",
       ":electron_swiftshader_library",
+      ":electron_xibs",
     ]
     if (!is_mas_build) {
       deps += [ ":electron_crashpad_helper" ]

+ 57 - 0
build/rules.gni

@@ -0,0 +1,57 @@
+import("//build/config/mac/mac_sdk.gni")
+
+# This is imported from /ios becuase this functionality was moved
+# after Chromium stopped using xib files for macOS menu functionality
+# See https://chromium-review.googlesource.com/c/chromium/src/+/1648695
+import("//build/config/ios/rules.gni")
+
+# Template is copied here from Chromium but was removed in
+# https://chromium-review.googlesource.com/c/chromium/src/+/1637981
+# Template to compile and package Mac XIB files as bundle data.
+# Arguments
+#     sources:
+#         list of string, sources to comiple
+#     output_path:
+#         (optional) string, the path to use for the outputs list in the
+#         bundle_data step. If unspecified, defaults to bundle_resources_dir.
+template("mac_xib_bundle_data") {
+  _target_name = target_name
+  _compile_target_name = _target_name + "_compile_ibtool"
+
+  compile_ib_files(_compile_target_name) {
+    forward_variables_from(invoker, [ "testonly" ])
+    visibility = [ ":$_target_name" ]
+    sources = invoker.sources
+    output_extension = "nib"
+    ibtool_flags = [
+      "--minimum-deployment-target",
+      mac_deployment_target,
+
+      # TODO(rsesek): Enable this once all the bots are on Xcode 7+.
+      # "--target-device",
+      # "mac",
+    ]
+  }
+
+  bundle_data(_target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "testonly",
+                             "visibility",
+                           ])
+
+    public_deps = [
+      ":$_compile_target_name",
+    ]
+    sources = get_target_outputs(":$_compile_target_name")
+
+    _output_path = "{{bundle_resources_dir}}"
+    if (defined(invoker.output_path)) {
+      _output_path = invoker.output_path
+    }
+
+    outputs = [
+      "$_output_path/{{source_file_part}}",
+    ]
+  }
+}

+ 1 - 0
script/zip_manifests/dist_zip.mac.x64.manifest

@@ -16,6 +16,7 @@ Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Librari
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libswiftshader_libGLESv2.dylib
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/Info.plist
+Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/MainMenu.nib
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/am.lproj/
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/am.lproj/locale.pak
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/ar.lproj/

+ 1 - 0
script/zip_manifests/dist_zip.mac_mas.x64.manifest

@@ -16,6 +16,7 @@ Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Librari
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libswiftshader_libGLESv2.dylib
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/Info.plist
+Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/MainMenu.nib
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/am.lproj/
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/am.lproj/locale.pak
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/ar.lproj/

+ 1 - 1
shell/browser/atom_browser_main_parts.cc

@@ -534,7 +534,7 @@ void AtomBrowserMainParts::PreMainMessageLoopStart() {
 
 void AtomBrowserMainParts::PreMainMessageLoopStartCommon() {
 #if defined(OS_MACOSX)
-  InitializeEmptyApplicationMenu();
+  InitializeMainNib();
   RegisterURLHandler();
 #endif
   media::SetLocalizedStringProvider(MediaStringProvider);

+ 1 - 1
shell/browser/atom_browser_main_parts.h

@@ -106,7 +106,7 @@ class AtomBrowserMainParts : public content::BrowserMainParts {
 #if defined(OS_MACOSX)
   void FreeAppDelegate();
   void RegisterURLHandler();
-  void InitializeEmptyApplicationMenu();
+  void InitializeMainNib();
 #endif
 
 #if defined(OS_MACOSX)

+ 35 - 47
shell/browser/atom_browser_main_parts_mac.mm

@@ -4,56 +4,16 @@
 
 #include "shell/browser/atom_browser_main_parts.h"
 
+#include "base/mac/bundle_locations.h"
+#include "base/mac/foundation_util.h"
+#include "base/path_service.h"
 #include "shell/browser/atom_paths.h"
 #import "shell/browser/mac/atom_application.h"
 #include "shell/browser/mac/atom_application_delegate.h"
+#include "ui/base/l10n/l10n_util_mac.h"
 
 namespace electron {
 
-namespace {
-
-base::scoped_nsobject<NSMenuItem> CreateMenuItem(NSString* title,
-                                                 SEL action,
-                                                 NSString* key_equivalent) {
-  return base::scoped_nsobject<NSMenuItem>([[NSMenuItem alloc]
-      initWithTitle:title
-             action:action
-      keyEquivalent:key_equivalent]);
-}
-
-// The App Menu refers to the dropdown titled "Electron".
-base::scoped_nsobject<NSMenu> BuildAppMenu() {
-  // The title is not used, as the title will always be the name of the App.
-  base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@""]);
-
-  NSString* app_name = [[[NSBundle mainBundle] infoDictionary]
-      objectForKey:(id)kCFBundleNameKey];
-
-  base::scoped_nsobject<NSMenuItem> item =
-      CreateMenuItem([NSString stringWithFormat:@"Quit %@", app_name],
-                     @selector(terminate:), @"q");
-  [menu addItem:item];
-
-  return menu;
-}
-
-base::scoped_nsobject<NSMenu> BuildEmptyMainMenu() {
-  base::scoped_nsobject<NSMenu> main_menu([[NSMenu alloc] initWithTitle:@""]);
-
-  using Builder = base::scoped_nsobject<NSMenu> (*)();
-  static const Builder kBuilderFuncs[] = {&BuildAppMenu};
-  for (auto* builder : kBuilderFuncs) {
-    NSMenuItem* item = [[[NSMenuItem alloc] initWithTitle:@""
-                                                   action:NULL
-                                            keyEquivalent:@""] autorelease];
-    item.submenu = builder();
-    [main_menu addItem:item];
-  }
-  return main_menu;
-}
-
-}  // namespace
-
 void AtomBrowserMainParts::PreMainMessageLoopStart() {
   // Set our own application delegate.
   AtomApplicationDelegate* delegate = [[AtomApplicationDelegate alloc] init];
@@ -77,9 +37,37 @@ void AtomBrowserMainParts::RegisterURLHandler() {
   [[AtomApplication sharedApplication] registerURLHandler];
 }
 
-void AtomBrowserMainParts::InitializeEmptyApplicationMenu() {
-  base::scoped_nsobject<NSMenu> main_menu = BuildEmptyMainMenu();
-  [[NSApplication sharedApplication] setMainMenu:main_menu];
+// Replicates NSApplicationMain, but doesn't start a run loop.
+void AtomBrowserMainParts::InitializeMainNib() {
+  auto infoDictionary = base::mac::OuterBundle().infoDictionary;
+
+  auto principalClass =
+      NSClassFromString([infoDictionary objectForKey:@"NSPrincipalClass"]);
+  auto application = [principalClass sharedApplication];
+
+  NSString* mainNibName = [infoDictionary objectForKey:@"NSMainNibFile"];
+
+  NSNib* mainNib;
+
+  @try {
+    mainNib = [[NSNib alloc] initWithNibNamed:mainNibName
+                                       bundle:base::mac::FrameworkBundle()];
+    // Handle failure of initWithNibNamed on SMB shares
+    // TODO(codebytere): Remove when
+    // https://bugs.chromium.org/p/chromium/issues/detail?id=932935 is fixed
+  } @catch (NSException* exception) {
+    NSString* nibPath =
+        [NSString stringWithFormat:@"Resources/%@.nib", mainNibName];
+    nibPath = [base::mac::FrameworkBundle().bundlePath
+        stringByAppendingPathComponent:nibPath];
+
+    NSData* data = [NSData dataWithContentsOfFile:nibPath];
+    mainNib = [[NSNib alloc] initWithNibData:data
+                                      bundle:base::mac::FrameworkBundle()];
+  }
+
+  [mainNib instantiateWithOwner:application topLevelObjects:nil];
+  [mainNib release];
 }
 
 }  // namespace electron

+ 2 - 0
shell/browser/resources/mac/Info.plist

@@ -24,6 +24,8 @@
     <string>public.app-category.developer-tools</string>
     <key>LSMinimumSystemVersion</key>
     <string>10.10.0</string>
+    <key>NSMainNibFile</key>
+    <string>MainMenu</string>
     <key>NSPrincipalClass</key>
     <string>AtomApplication</string>
     <key>NSSupportsAutomaticGraphicsSwitching</key>

+ 5 - 16
shell/browser/ui/cocoa/atom_menu_controller.mm

@@ -128,11 +128,9 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
 
   // Locate & retain the recent documents menu item
   if (!recentDocumentsMenuItem_) {
-    base::string16 title = base::ASCIIToUTF16("Open Recent");
-    NSString* openTitle = l10n_util::FixUpWindowsStyleLabel(title);
-
-    recentDocumentsMenuItem_.reset([[[[[NSApp mainMenu]
-        itemWithTitle:@"Electron"] submenu] itemWithTitle:openTitle] retain]);
+    recentDocumentsMenuItem_.reset(
+        [[[[[NSApp mainMenu] itemWithTitle:@"Electron"] submenu]
+            itemWithTitle:@"Open Recent"] retain]);
   }
 
   model_ = model;
@@ -195,17 +193,8 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
 // Replaces the item's submenu instance with the singleton recent documents
 // menu. Previously replaced menu items will be recovered.
 - (void)replaceSubmenuShowingRecentDocuments:(NSMenuItem*)item {
-  NSMenu* recentDocumentsMenu = [recentDocumentsMenuItem_ submenu];
-  if (!recentDocumentsMenu) {
-    base::string16 title = base::ASCIIToUTF16("Clear Menu");
-    NSString* clearTitle = l10n_util::FixUpWindowsStyleLabel(title);
-    recentDocumentsMenu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
-    [recentDocumentsMenu
-        addItem:[[[NSMenuItem alloc]
-                    initWithTitle:clearTitle
-                           action:@selector(clearRecentDocuments:)
-                    keyEquivalent:@""] autorelease]];
-  }
+  NSMenu* recentDocumentsMenu =
+      [[[recentDocumentsMenuItem_ submenu] retain] autorelease];
 
   // Remove menu items in recent documents back to swap menu
   [self moveMenuItems:recentDocumentsMenu to:recentDocumentsMenuSwap_];

+ 219 - 0
shell/common/resources/mac/MainMenu.xib

@@ -0,0 +1,219 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00">
+	<data>
+		<int key="IBDocument.SystemTarget">101000</int>
+		<string key="IBDocument.SystemVersion">14D136</string>
+		<string key="IBDocument.InterfaceBuilderVersion">7531</string>
+		<string key="IBDocument.AppKitVersion">1347.57</string>
+		<string key="IBDocument.HIToolboxVersion">758.70</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+			<string key="NS.object.0">7531</string>
+		</object>
+		<array key="IBDocument.IntegratedClassDependencies">
+			<string>NSCustomObject</string>
+			<string>NSMenu</string>
+			<string>NSMenuItem</string>
+		</array>
+		<array key="IBDocument.PluginDependencies">
+			<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+		</array>
+		<object class="NSMutableDictionary" key="IBDocument.Metadata">
+			<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+			<integer value="1" key="NS.object.0"/>
+		</object>
+		<array class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+			<object class="NSCustomObject" id="1021">
+				<string key="NSClassName">AtomApplication</string>
+			</object>
+			<object class="NSCustomObject" id="1014">
+				<string key="NSClassName">FirstResponder</string>
+			</object>
+			<object class="NSCustomObject" id="1050">
+				<string key="NSClassName">NSApplication</string>
+			</object>
+			<object class="NSCustomObject" id="903638069">
+				<string key="NSClassName">NSFontManager</string>
+			</object>
+			<object class="NSMenu" id="649796088">
+				<string key="NSTitle">Main Menu</string>
+				<array class="NSMutableArray" key="NSMenuItems">
+					<object class="NSMenuItem" id="694149608">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Electron</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<object class="NSCustomResource" key="NSOnImage" id="229763992">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuCheckmark</string>
+						</object>
+						<object class="NSCustomResource" key="NSMixedImage" id="909111550">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuMixedState</string>
+						</object>
+						<string key="NSAction">submenuAction:</string>
+						<reference key="NSTarget" ref="110575045"/>
+						<object class="NSMenu" key="NSSubmenu" id="110575045">
+							<string key="NSTitle">Electron</string>
+							<array class="NSMutableArray" key="NSMenuItems">
+								<object class="NSMenuItem" id="632727374">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Quit</string>
+									<string key="NSKeyEquiv">q</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="229763992"/>
+									<reference key="NSMixedImage" ref="909111550"/>
+								</object>
+								<object class="NSMenuItem" id="1025936716">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Open Recent</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<bool key="NSIsHidden">YES</bool>
+									<reference key="NSOnImage" ref="229763992"/>
+									<reference key="NSMixedImage" ref="909111550"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="1065607017">
+										<string key="NSTitle">Open Recent</string>
+										<array class="NSMutableArray" key="NSMenuItems"></array>
+										<string key="NSName">_NSRecentDocumentsMenu</string>
+									</object>
+								</object>
+							</array>
+							<string key="NSName">_NSAppleMenu</string>
+						</object>
+					</object>
+				</array>
+				<string key="NSName">_NSMainMenu</string>
+			</object>
+		</array>
+		<object class="IBObjectContainer" key="IBDocument.Objects">
+			<array key="connectionRecords">
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">terminate:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="632727374"/>
+					</object>
+					<int key="connectionID">807</int>
+				</object>
+			</array>
+			<object class="IBMutableOrderedSet" key="objectRecords">
+				<array key="orderedObjects">
+					<object class="IBObjectRecord">
+						<int key="objectID">0</int>
+						<array key="object" id="0"/>
+						<reference key="children" ref="1048"/>
+						<nil key="parent"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-2</int>
+						<reference key="object" ref="1021"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">File's Owner</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-1</int>
+						<reference key="object" ref="1014"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">First Responder</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-3</int>
+						<reference key="object" ref="1050"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">Application</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">371</int>
+						<reference key="object" ref="903638069"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<!-- NSMenu Main Menu -->
+					<object class="IBObjectRecord">
+						<int key="objectID">29</int>
+						<reference key="object" ref="649796088"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="694149608"/>
+						</array>
+						<reference key="parent" ref="0"/>
+					</object>
+					<!-- NSMenuItem Electron -->
+					<object class="IBObjectRecord">
+						<int key="objectID">56</int>
+						<reference key="object" ref="694149608"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="110575045"/>
+						</array>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<!-- NSMenu Electron -->
+					<object class="IBObjectRecord">
+						<int key="objectID">57</int>
+						<reference key="object" ref="110575045"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="632727374"/>
+							<reference ref="1025936716"/>
+						</array>
+						<reference key="parent" ref="694149608"/>
+					</object>
+					<!-- NSMenuItem Quit -->
+					<object class="IBObjectRecord">
+						<int key="objectID">136</int>
+						<reference key="object" ref="632727374"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<!-- NSMenuItem Open Recent -->
+					<object class="IBObjectRecord">
+						<int key="objectID">124</int>
+						<reference key="object" ref="1025936716"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="1065607017"/>
+						</array>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<!-- NSMenu Open Recent -->
+					<object class="IBObjectRecord">
+						<int key="objectID">125</int>
+						<reference key="object" ref="1065607017"/>
+						<array class="NSMutableArray" key="children"></array>
+						<reference key="parent" ref="1025936716"/>
+					</object>
+				</array>
+			</object>
+			<dictionary class="NSMutableDictionary" key="flattenedProperties">
+				<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="-3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="29.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="371.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="56.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="57.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="136.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="124.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="125.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+			</dictionary>
+			<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
+			<nil key="activeLocalization"/>
+			<dictionary class="NSMutableDictionary" key="localizations"/>
+			<nil key="sourceID"/>
+			<int key="maxID">807</int>
+		</object>
+		<int key="IBDocument.localizationMode">0</int>
+		<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+		<bool key="IBDocument.previouslyAttemptedUpgradeToXcode5">NO</bool>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+			<integer value="4600" key="NS.object.0"/>
+		</object>
+		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+		<int key="IBDocument.defaultPropertyAccessControl">3</int>
+		<dictionary class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+			<string key="NSMenuCheckmark">{12, 12}</string>
+			<string key="NSMenuMixedState">{10, 2}</string>
+		</dictionary>
+		<bool key="IBDocument.UseAutolayout">YES</bool>
+	</data>
+</archive>