Bug 1297530 - Add support for Unity unified menu. draft
authorChris Coulson <chris.coulson@canonical.com>
Fri, 11 Nov 2016 16:05:48 -0600
changeset 437940 ed3a811e6fe7853f3c6778cd24292d8b9ca9339e
parent 437939 b2f9a7cdcb514067018f23711c44f0906ed0e6d5
child 536760 1421de5ef643389aeaa8df13fd359bbf69d36892
push id35550
push usermozilla@kaply.com
push dateFri, 11 Nov 2016 22:06:28 +0000
bugs1297530
milestone52.0a1
Bug 1297530 - Add support for Unity unified menu. MozReview-Commit-ID: 4zFophtvO3j
browser/base/content/browser-menubar.inc
browser/base/content/browser.js
browser/components/places/content/places.xul
modules/libpref/init/all.js
toolkit/content/widgets/popup.xml
toolkit/content/xul.css
widget/gtk/moz.build
widget/gtk/nsScreenGtk.cpp
widget/gtk/nsWidgetFactory.cpp
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
widget/moz.build
xpfe/appshell/nsWebShellWindow.cpp
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -1,16 +1,20 @@
 # -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
        <menubar id="main-menubar"
                 onpopupshowing="if (event.target.parentNode.parentNode == this &amp;&amp;
+#ifdef MOZ_WIDGET_GTK
+                                    document.documentElement.getAttribute('shellshowingmenubar') != 'true')
+#else
                                     !('@mozilla.org/widget/nativemenuservice;1' in Cc))
+#endif
                                   this.setAttribute('openedwithkey',
                                                     event.target.parentNode.openedWithKey);"
                 style="border:0px;padding:0px;margin:0px;-moz-appearance:none">
             <menu id="file-menu" label="&fileMenu.label;"
                   accesskey="&fileMenu.accesskey;">
               <menupopup id="menu_FilePopup"
                          onpopupshowing="updateUserContextUIVisibility();">
                 <menuitem id="menu_newNavigatorTab"
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5045,16 +5045,18 @@ nsBrowserAccess.prototype = {
     return CanCloseWindow();
   },
 }
 
 function getTogglableToolbars() {
   let toolbarNodes = Array.slice(gNavToolbox.childNodes);
   toolbarNodes = toolbarNodes.concat(gNavToolbox.externalToolbars);
   toolbarNodes = toolbarNodes.filter(node => node.getAttribute("toolbarname"));
+  if (document.documentElement.getAttribute("shellshowingmenubar") == "true")
+    toolbarNodes = toolbarNodes.filter(node => node.id != "toolbar-menubar");
   return toolbarNodes;
 }
 
 function onViewToolbarsPopupShowing(aEvent, aInsertPoint) {
   var popup = aEvent.target;
   if (popup != aEvent.currentTarget)
     return;
 
--- a/browser/components/places/content/places.xul
+++ b/browser/components/places/content/places.xul
@@ -152,17 +152,17 @@
                      command="OrganizerCommand:Forward"
                      tooltiptext="&forwardButton.tooltip;"
                      disabled="true"/>
 
 #ifdef XP_MACOSX
         <toolbarbutton type="menu" class="tabbable"
               onpopupshowing="document.getElementById('placeContent').focus()"
 #else
-      <menubar id="placesMenu">
+      <menubar id="placesMenu" _moz-menubarkeeplocal="true">
         <menu accesskey="&organize.accesskey;" class="menu-iconic"
 #endif
               id="organizeButton" label="&organize.label;"
               tooltiptext="&organize.tooltip;">
           <menupopup id="organizeButtonPopup">
             <menuitem id="newbookmark"
                       command="placesCmd_new:bookmark"
                       label="&cmd.new_bookmark.label;"
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -223,16 +223,19 @@ pref("dom.url.getters_decode_hash", fals
 // significantly increase the number of compartments in the system.
 pref("dom.compartment_per_addon", true);
 
 // Fastback caching - if this pref is negative, then we calculate the number
 // of content viewers to cache based on the amount of available memory.
 pref("browser.sessionhistory.max_total_viewers", -1);
 
 pref("ui.use_native_colors", true);
+#ifdef MOZ_WIDGET_GTK
+pref("ui.use_unity_menubar", true);
+#endif
 pref("ui.click_hold_context_menus", false);
 // Duration of timeout of incremental search in menus (ms).  0 means infinite.
 pref("ui.menu.incremental_search.timeout", 1000);
 // If true, all popups won't hide automatically on blur
 pref("ui.popup.disable_autohide", false);
 
 pref("browser.display.use_document_fonts",  1);  // 0 = never, 1 = quick, 2 = always
 // 0 = default: always, except in high contrast mode
--- a/toolkit/content/widgets/popup.xml
+++ b/toolkit/content/widgets/popup.xml
@@ -20,18 +20,24 @@
       <property name="position" onget="return this.getAttribute('position');"
                                 onset="this.setAttribute('position', val); return val;"/>
       <property name="popupBoxObject">
         <getter>
           return this.boxObject;
         </getter>
       </property>
 
-      <property name="state" readonly="true"
-                onget="return this.popupBoxObject.popupState"/>
+      <property name="state" readonly="true">
+        <getter><![CDATA[
+          if (this.hasAttribute('_moz-menupopupstate'))
+            return this.getAttribute('_moz-menupopupstate');
+          else
+            return this.popupBoxObject.popupState;
+        ]]></getter>
+      </property>
 
       <property name="triggerNode" readonly="true"
                 onget="return this.popupBoxObject.triggerNode"/>
 
       <property name="anchorNode" readonly="true"
                 onget="return this.popupBoxObject.anchorNode"/>
 
       <method name="openPopup">
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -302,16 +302,28 @@ toolbar[type="menubar"][autohide="true"]
 toolbar[type="menubar"][autohide="true"][inactive="true"]:not([customizing="true"]) {
   min-height: 0 !important;
   height: 0 !important;
   -moz-appearance: none !important;
   border-style: none !important;
 }
 %endif
 
+%ifdef MOZ_WIDGET_GTK
+window[shellshowingmenubar="true"] menubar {
+  display: none !important;
+}
+
+window[shellshowingmenubar="true"]
+toolbar[type="menubar"]:not([customizing="true"]) {
+  min-height: 0 !important;
+  border: 0 !important;
+}
+%endif
+
 toolbarseparator {
   -moz-binding: url("chrome://global/content/bindings/toolbar.xml#toolbardecoration");
 }
 
 toolbarspacer {
   -moz-binding: url("chrome://global/content/bindings/toolbar.xml#toolbardecoration");
 }
 
--- a/widget/gtk/moz.build
+++ b/widget/gtk/moz.build
@@ -19,32 +19,42 @@ EXPORTS.mozilla += [
 
 UNIFIED_SOURCES += [
     'IMContextWrapper.cpp',
     'mozcontainer.c',
     'NativeKeyBindings.cpp',
     'nsAppShell.cpp',
     'nsBidiKeyboard.cpp',
     'nsColorPicker.cpp',
+    'nsDbusmenu.cpp',
     'nsFilePicker.cpp',
     'nsGtkKeyUtils.cpp',
     'nsImageToPixbuf.cpp',
     'nsLookAndFeel.cpp',
+    'nsMenuBar.cpp',
+    'nsMenuContainer.cpp',
+    'nsMenuItem.cpp',
+    'nsMenuObject.cpp',
+    'nsMenuSeparator.cpp',
+    'nsNativeMenuAtoms.cpp',
+    'nsNativeMenuDocListener.cpp',
     'nsNativeThemeGTK.cpp',
     'nsScreenGtk.cpp',
     'nsScreenManagerGtk.cpp',
     'nsSound.cpp',
     'nsToolkit.cpp',
     'nsWidgetFactory.cpp',
     'WakeLockListener.cpp',
     'WidgetTraceEvent.cpp',
     'WidgetUtilsGtk.cpp',
 ]
 
 SOURCES += [
+    'nsMenu.cpp', # conflicts with X11 headers
+    'nsNativeMenuService.cpp',
     'nsWindow.cpp', # conflicts with X11 headers
 ]
 
 if CONFIG['MOZ_X11']:
     UNIFIED_SOURCES += [
         'CompositorWidgetChild.cpp',
         'CompositorWidgetParent.cpp',
         'InProcessX11CompositorWidget.cpp',
@@ -99,16 +109,17 @@ else:
     ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/layout/generic',
+    '/layout/style',
     '/layout/xul',
     '/other-licenses/atk-1.0',
     '/widget',
 ]
 
 if CONFIG['MOZ_X11']:
     LOCAL_INCLUDES += [
         '/widget/x11',
--- a/widget/gtk/nsScreenGtk.cpp
+++ b/widget/gtk/nsScreenGtk.cpp
@@ -10,16 +10,17 @@
 #include <gdk/gdk.h>
 #ifdef MOZ_X11
 #include <gdk/gdkx.h>
 #include <X11/Xatom.h>
 #endif
 #include <gtk/gtk.h>
 #include <dlfcn.h>
 #include "gfxPlatformGtk.h"
+#include "nsIWidget.h"
 
 static uint32_t sScreenId = 0;
 
 
 nsScreenGtk :: nsScreenGtk (  )
   : mScreenNum(0),
     mRect(0, 0, 0, 0),
     mAvailRect(0, 0, 0, 0),
--- a/widget/gtk/nsWidgetFactory.cpp
+++ b/widget/gtk/nsWidgetFactory.cpp
@@ -44,16 +44,19 @@
 #include "nsImageToPixbuf.h"
 #include "nsPrintDialogGTK.h"
 
 #if defined(MOZ_X11)
 #include "nsIdleServiceGTK.h"
 #include "GfxInfoX11.h"
 #endif
 
+#include "nsNativeMenuService.h"
+#include "nsNativeMenuAtoms.h"
+
 #include "nsNativeThemeGTK.h"
 
 #include "nsIComponentRegistrar.h"
 #include "nsComponentManagerUtils.h"
 #include "mozilla/gfx/2D.h"
 #include <gtk/gtk.h>
 
 using namespace mozilla;
@@ -116,16 +119,19 @@ nsNativeThemeGTKConstructor(nsISupports 
 namespace mozilla {
 namespace widget {
 // This constructor should really be shared with all platforms.
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init)
 }
 }
 #endif
 
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsNativeMenuService,
+                                         nsNativeMenuService::GetInstance)
+
 #ifdef NS_PRINTING
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecGTK)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintOptionsGTK, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsPrinterEnumeratorGTK)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSession, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintDialogServiceGTK, Init)
 #endif
 
@@ -218,16 +224,17 @@ NS_DEFINE_NAMED_CID(NS_PRINTSESSION_CID)
 NS_DEFINE_NAMED_CID(NS_DEVICE_CONTEXT_SPEC_CID);
 NS_DEFINE_NAMED_CID(NS_PRINTDIALOGSERVICE_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_IMAGE_TO_PIXBUF_CID);
 #if defined(MOZ_X11)
 NS_DEFINE_NAMED_CID(NS_IDLE_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_GFXINFO_CID);
 #endif
+NS_DEFINE_NAMED_CID(NS_NATIVEMENUSERVICE_CID);
 
 
 static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
     { &kNS_WINDOW_CID, false, nullptr, nsWindowConstructor },
     { &kNS_CHILD_CID, false, nullptr, nsChildWindowConstructor },
     { &kNS_APPSHELL_CID, false, nullptr, nsAppShellConstructor, Module::ALLOW_IN_GPU_PROCESS },
     { &kNS_COLORPICKER_CID, false, nullptr, nsColorPickerConstructor, Module::MAIN_PROCESS_ONLY },
     { &kNS_FILEPICKER_CID, false, nullptr, nsFilePickerConstructor, Module::MAIN_PROCESS_ONLY },
@@ -253,16 +260,17 @@ static const mozilla::Module::CIDEntry k
     { &kNS_DEVICE_CONTEXT_SPEC_CID, false, nullptr, nsDeviceContextSpecGTKConstructor },
     { &kNS_PRINTDIALOGSERVICE_CID, false, nullptr, nsPrintDialogServiceGTKConstructor },
 #endif
     { &kNS_IMAGE_TO_PIXBUF_CID, false, nullptr, nsImageToPixbufConstructor },
 #if defined(MOZ_X11)
     { &kNS_IDLE_SERVICE_CID, false, nullptr, nsIdleServiceGTKConstructor },
     { &kNS_GFXINFO_CID, false, nullptr, mozilla::widget::GfxInfoConstructor },
 #endif
+    { &kNS_NATIVEMENUSERVICE_CID, true, NULL, nsNativeMenuServiceConstructor },
     { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
     { "@mozilla.org/widget/window/gtk;1", &kNS_WINDOW_CID },
     { "@mozilla.org/widgets/child_window/gtk;1", &kNS_CHILD_CID },
     { "@mozilla.org/widget/appshell/gtk;1", &kNS_APPSHELL_CID, Module::ALLOW_IN_GPU_PROCESS },
     { "@mozilla.org/colorpicker;1", &kNS_COLORPICKER_CID, Module::MAIN_PROCESS_ONLY },
@@ -290,19 +298,29 @@ static const mozilla::Module::ContractID
     { "@mozilla.org/gfx/devicecontextspec;1", &kNS_DEVICE_CONTEXT_SPEC_CID },
     { NS_PRINTDIALOGSERVICE_CONTRACTID, &kNS_PRINTDIALOGSERVICE_CID },
 #endif
     { "@mozilla.org/widget/image-to-gdk-pixbuf;1", &kNS_IMAGE_TO_PIXBUF_CID },
 #if defined(MOZ_X11)
     { "@mozilla.org/widget/idleservice;1", &kNS_IDLE_SERVICE_CID },
     { "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID },
 #endif
+    { "@mozilla.org/widget/nativemenuservice;1", &kNS_NATIVEMENUSERVICE_CID },
     { nullptr }
 };
 
+static nsresult
+nsWidgetGtk2ModuleCtor()
+{
+  nsAppShellInit();
+  nsNativeMenuAtoms::Init();
+
+  return NS_OK;
+}
+
 static void
 nsWidgetGtk2ModuleDtor()
 {
   // Shutdown all XP level widget classes.
   WidgetUtils::Shutdown();
 
   NativeKeyBindings::Shutdown();
   nsLookAndFeel::Shutdown();
@@ -318,14 +336,14 @@ nsWidgetGtk2ModuleDtor()
 }
 
 static const mozilla::Module kWidgetModule = {
     mozilla::Module::kVersion,
     kWidgetCIDs,
     kWidgetContracts,
     nullptr,
     nullptr,
-    nsAppShellInit,
+    nsWidgetGtk2ModuleCtor,
     nsWidgetGtk2ModuleDtor,
     Module::ALLOW_IN_GPU_PROCESS
 };
 
 NSMODULE_DEFN(nsWidgetGtk2Module) = &kWidgetModule;
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -5162,16 +5162,21 @@ nsWindow::HideWindowChrome(bool aShouldH
     XSync(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()) , False);
 #else
     gdk_flush ();
 #endif /* MOZ_X11 */
 
     return NS_OK;
 }
 
+void
+nsWindow::SetMenuBar(nsMenuBar *aMenuBar) {
+    mMenuBar.reset(aMenuBar);
+}
+
 bool
 nsWindow::CheckForRollup(gdouble aMouseX, gdouble aMouseY,
                          bool aIsWheel, bool aAlwaysRollup)
 {
     nsIRollupListener* rollupListener = GetActiveRollupListener();
     nsCOMPtr<nsIWidget> rollupWidget;
     if (rollupListener) {
         rollupWidget = rollupListener->GetRollupWidget();
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -30,16 +30,18 @@
 #ifdef ACCESSIBILITY
 #include "mozilla/a11y/Accessible.h"
 #endif
 #include "mozilla/EventForwards.h"
 #include "mozilla/TouchEvents.h"
 
 #include "IMContextWrapper.h"
 
+#include "nsMenuBar.h"
+
 #undef LOG
 #ifdef MOZ_LOGGING
 
 #include "mozilla/Logging.h"
 #include "nsTArray.h"
 #include "Units.h"
 
 extern PRLogModuleInfo *gWidgetLog;
@@ -157,16 +159,18 @@ public:
     virtual void PerformFullscreenTransition(FullscreenTransitionStage aStage,
                                              uint16_t aDuration,
                                              nsISupports* aData,
                                              nsIRunnable* aCallback) override;
     virtual nsresult   MakeFullScreen(bool aFullScreen,
                                       nsIScreen* aTargetScreen = nullptr) override;
     NS_IMETHOD         HideWindowChrome(bool aShouldHide) override;
 
+    void               SetMenuBar(nsMenuBar *aMenuBar);
+
     /**
      * GetLastUserInputTime returns a timestamp for the most recent user input
      * event.  This is intended for pointer grab requests (including drags).
      */
     static guint32     GetLastUserInputTime();
 
     // utility method, -1 if no change should be made, otherwise returns a
     // value that can be passed to gdk_window_set_decorations
@@ -564,16 +568,18 @@ private:
      * ancestor widget's instance.  So, one set of IM contexts is created for
      * all windows in a hierarchy.  If the children are released after the top
      * level window is released, the children still have a valid pointer,
      * however, IME doesn't work at that time.
      */
     RefPtr<mozilla::widget::IMContextWrapper> mIMContext;
 
     mozilla::UniquePtr<mozilla::CurrentX11TimeGetter> mCurrentTimeGetter;
+
+    mozilla::UniquePtr<nsMenuBar> mMenuBar;
 };
 
 class nsChildWindow : public nsWindow {
 public:
     nsChildWindow();
     ~nsChildWindow();
 };
 
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -32,20 +32,22 @@ elif toolkit == 'cocoa':
     XPIDL_SOURCES += [
         'nsIMacDockSupport.idl',
         'nsIMacWebAppUtils.idl',
         'nsIStandaloneNativeMenu.idl',
         'nsISystemStatusBar.idl',
         'nsITaskbarProgress.idl',
     ]
     EXPORTS += [
-        'nsINativeMenuService.h',
         'nsIPrintDialogService.h',
     ]
 
+if toolkit in ('cocoa', 'gtk2', 'gtk3'):
+    EXPORTS += ['nsINativeMenuService.h']
+
 TEST_DIRS += ['tests']
 
 # Don't build the DSO under the 'build' directory as windows does.
 #
 # The DSOs get built in the toolkit dir itself.  Do this so that
 # multiple implementations of widget can be built on the same
 # source tree.
 #
--- a/xpfe/appshell/nsWebShellWindow.cpp
+++ b/xpfe/appshell/nsWebShellWindow.cpp
@@ -53,32 +53,33 @@
 #include "nsIDocumentLoaderFactory.h"
 #include "nsIObserverService.h"
 #include "prprf.h"
 
 #include "nsIScreenManager.h"
 #include "nsIScreen.h"
 
 #include "nsIContent.h" // for menus
+#include "nsIAtom.h"
 #include "nsIScriptSecurityManager.h"
 
 // For calculating size
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 
 #include "nsIBaseWindow.h"
 #include "nsIDocShellTreeItem.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MouseEvents.h"
 
 #include "nsPIWindowRoot.h"
 
-#ifdef XP_MACOSX
+#if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
 #include "nsINativeMenuService.h"
 #define USE_NATIVE_MENUS
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 /* Define Class IDs */
@@ -493,16 +494,21 @@ static void LoadNativeMenus(nsIDOMDocume
                                   getter_AddRefs(menubarElements));
 
   nsCOMPtr<nsIDOMNode> menubarNode;
   if (menubarElements)
     menubarElements->Item(0, getter_AddRefs(menubarNode));
 
   if (menubarNode) {
     nsCOMPtr<nsIContent> menubarContent(do_QueryInterface(menubarNode));
+#ifdef MOZ_WIDGET_GTK
+    nsCOMPtr<nsIAtom> atom = NS_Atomize(NS_LITERAL_CSTRING("_moz-menubarkeeplocal"));
+    if (menubarContent->AttrValueIs(kNameSpaceID_None, atom, nsGkAtoms::_true, eCaseMatters))
+      return;
+#endif
     nms->CreateNativeMenuBar(aParentWindow, menubarContent);
   } else {
     nms->CreateNativeMenuBar(aParentWindow, nullptr);
   }
 }
 #endif
 
 namespace mozilla {