Advertisement
Guest User

Untitled

a guest
Apr 5th, 2015
472
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Diff 179.59 KB | None | 0 0
  1. Index: firefox-trunk-37.0~a1~hg20141208r218676/browser/base/content/browser-menubar.inc
  2. ===================================================================
  3. --- firefox-trunk-37.0~a1~hg20141208r218676.orig/browser/base/content/browser-menubar.inc   2014-12-08 19:19:07.486645126 +0000
  4. +++ firefox-trunk-37.0~a1~hg20141208r218676/browser/base/content/browser-menubar.inc    2014-12-08 19:19:07.474645107 +0000
  5. @@ -5,7 +5,11 @@
  6.  
  7.         <menubar id="main-menubar"
  8.                  onpopupshowing="if (event.target.parentNode.parentNode == this &amp;&amp;
  9. +#ifdef MOZ_WIDGET_GTK
  10. +                                    document.documentElement.getAttribute('shellshowingmenubar') != 'true')
  11. +#else
  12.                                      !('@mozilla.org/widget/nativemenuservice;1' in Cc))
  13. +#endif
  14.                                    this.setAttribute('openedwithkey',
  15.                                                      event.target.parentNode.openedWithKey);"
  16.                  style="border:0px;padding:0px;margin:0px;-moz-appearance:none">
  17. Index: firefox-trunk-37.0~a1~hg20141208r218676/browser/components/places/content/places.xul
  18. ===================================================================
  19. --- firefox-trunk-37.0~a1~hg20141208r218676.orig/browser/components/places/content/places.xul   2014-12-08 19:19:07.486645126 +0000
  20. +++ firefox-trunk-37.0~a1~hg20141208r218676/browser/components/places/content/places.xul    2014-12-08 19:19:07.474645107 +0000
  21. @@ -157,7 +157,7 @@
  22.          <toolbarbutton type="menu" class="tabbable"
  23.                onpopupshowing="document.getElementById('placeContent').focus()"
  24.  #else
  25. -      <menubar id="placesMenu">
  26. +      <menubar id="placesMenu" _moz-menubarkeeplocal="true">
  27.          <menu accesskey="&organize.accesskey;" class="menu-iconic"
  28.  #endif
  29.                id="organizeButton" label="&organize.label;"
  30. Index: firefox-trunk-37.0~a1~hg20141208r218676/toolkit/content/widgets/popup.xml
  31. ===================================================================
  32. --- firefox-trunk-37.0~a1~hg20141208r218676.orig/toolkit/content/widgets/popup.xml  2014-12-08 19:19:07.486645126 +0000
  33. +++ firefox-trunk-37.0~a1~hg20141208r218676/toolkit/content/widgets/popup.xml   2014-12-08 19:19:07.474645107 +0000
  34. @@ -25,8 +25,14 @@
  35.          </getter>
  36.        </property>
  37.  
  38. -      <property name="state" readonly="true"
  39. -                onget="return this.popupBoxObject.popupState"/>
  40. +      <property name="state" readonly="true">
  41. +        <getter><![CDATA[
  42. +          if (this.hasAttribute('_moz-menupopupstate'))
  43. +            return this.getAttribute('_moz-menupopupstate');
  44. +          else
  45. +            return this.popupBoxObject.popupState;
  46. +        ]]></getter>
  47. +      </property>
  48.  
  49.        <property name="triggerNode" readonly="true"
  50.                  onget="return this.popupBoxObject.triggerNode"/>
  51. Index: firefox-trunk-37.0~a1~hg20141208r218676/toolkit/content/xul.css
  52. ===================================================================
  53. --- firefox-trunk-37.0~a1~hg20141208r218676.orig/toolkit/content/xul.css    2014-12-08 19:19:07.486645126 +0000
  54. +++ firefox-trunk-37.0~a1~hg20141208r218676/toolkit/content/xul.css 2014-12-08 19:19:07.474645107 +0000
  55. @@ -284,6 +284,18 @@
  56.  }
  57.  %endif
  58.  
  59. +%ifdef MOZ_WIDGET_GTK
  60. +window[shellshowingmenubar="true"] menubar {
  61. +  display: none !important;
  62. +}
  63. +
  64. +window[shellshowingmenubar="true"]
  65. +toolbar[type="menubar"]:not([customizing="true"]) {
  66. +  min-height: 0 !important;
  67. +  border: 0 !important;
  68. +}
  69. +%endif
  70. +
  71.  toolbarseparator {
  72.    -moz-binding: url("chrome://global/content/bindings/toolbar.xml#toolbardecoration");
  73.  }
  74. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsDbusmenu.cpp
  75. ===================================================================
  76. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  77. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsDbusmenu.cpp   2014-12-08 19:19:07.474645107 +0000
  78. @@ -0,0 +1,59 @@
  79. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  80. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  81. + */
  82. +/* This Source Code Form is subject to the terms of the Mozilla Public
  83. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  84. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  85. +
  86. +#include "nsDbusmenu.h"
  87. +#include "prlink.h"
  88. +#include "mozilla/ArrayUtils.h"
  89. +
  90. +#define FUNC(name, type, params) \
  91. +nsDbusmenuFunctions::_##name##_fn nsDbusmenuFunctions::s_##name;
  92. +DBUSMENU_GLIB_FUNCTIONS
  93. +DBUSMENU_GTK_FUNCTIONS
  94. +#undef FUNC
  95. +
  96. +static PRLibrary *gDbusmenuGlib = nullptr;
  97. +static PRLibrary *gDbusmenuGtk = nullptr;
  98. +
  99. +typedef void (*nsDbusmenuFunc)();
  100. +struct nsDbusmenuDynamicFunction {
  101. +    const char *functionName;
  102. +    nsDbusmenuFunc *function;
  103. +};
  104. +
  105. +/* static */ nsresult
  106. +nsDbusmenuFunctions::Init()
  107. +{
  108. +#define FUNC(name, type, params) \
  109. +    { #name, (nsDbusmenuFunc *)&nsDbusmenuFunctions::s_##name },
  110. +    static const nsDbusmenuDynamicFunction kDbusmenuGlibSymbols[] = {
  111. +        DBUSMENU_GLIB_FUNCTIONS
  112. +    };
  113. +    static const nsDbusmenuDynamicFunction kDbusmenuGtkSymbols[] = {
  114. +        DBUSMENU_GTK_FUNCTIONS
  115. +    };
  116. +
  117. +#define LOAD_LIBRARY(symbol, name) \
  118. +    if (!g##symbol) { \
  119. +        g##symbol = PR_LoadLibrary(name); \
  120. +        if (!g##symbol) { \
  121. +            return NS_ERROR_FAILURE; \
  122. +        } \
  123. +    } \
  124. +    for (uint32_t i = 0; i < mozilla::ArrayLength(k##symbol##Symbols); ++i) { \
  125. +        *k##symbol##Symbols[i].function = \
  126. +            PR_FindFunctionSymbol(g##symbol, k##symbol##Symbols[i].functionName); \
  127. +        if (!*k##symbol##Symbols[i].function) { \
  128. +            return NS_ERROR_FAILURE; \
  129. +        } \
  130. +    }
  131. +
  132. +    LOAD_LIBRARY(DbusmenuGlib, "libdbusmenu-glib.so.4")
  133. +    LOAD_LIBRARY(DbusmenuGtk, "libdbusmenu-gtk.so.4")
  134. +#undef LOAD_LIBRARY
  135. +
  136. +    return NS_OK;
  137. +}
  138. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsDbusmenu.h
  139. ===================================================================
  140. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  141. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsDbusmenu.h 2014-12-08 19:19:07.474645107 +0000
  142. @@ -0,0 +1,99 @@
  143. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  144. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  145. + */
  146. +/* This Source Code Form is subject to the terms of the Mozilla Public
  147. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  148. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  149. +
  150. +#ifndef __nsDbusmenu_h__
  151. +#define __nsDbusmenu_h__
  152. +
  153. +#include "nsError.h"
  154. +
  155. +#include <glib.h>
  156. +#include <gdk/gdk.h>
  157. +
  158. +#define DBUSMENU_GLIB_FUNCTIONS \
  159. +    FUNC(dbusmenu_menuitem_child_add_position, gboolean, (DbusmenuMenuitem *mi, DbusmenuMenuitem *child, guint position)) \
  160. +    FUNC(dbusmenu_menuitem_child_append, gboolean, (DbusmenuMenuitem *mi, DbusmenuMenuitem *child)) \
  161. +    FUNC(dbusmenu_menuitem_child_delete, gboolean, (DbusmenuMenuitem *mi, DbusmenuMenuitem *child)) \
  162. +    FUNC(dbusmenu_menuitem_get_children, GList*, (DbusmenuMenuitem *mi)) \
  163. +    FUNC(dbusmenu_menuitem_new, DbusmenuMenuitem*, (void)) \
  164. +    FUNC(dbusmenu_menuitem_property_get, const gchar*, (DbusmenuMenuitem *mi, const gchar *property)) \
  165. +    FUNC(dbusmenu_menuitem_property_get_bool, gboolean, (DbusmenuMenuitem *mi, const gchar *property)) \
  166. +    FUNC(dbusmenu_menuitem_property_remove, void, (DbusmenuMenuitem *mi, const gchar *property)) \
  167. +    FUNC(dbusmenu_menuitem_property_set, gboolean, (DbusmenuMenuitem *mi, const gchar *property, const gchar *value)) \
  168. +    FUNC(dbusmenu_menuitem_property_set_bool, gboolean, (DbusmenuMenuitem *mi, const gchar *property, const gboolean value)) \
  169. +    FUNC(dbusmenu_menuitem_property_set_int, gboolean, (DbusmenuMenuitem *mi, const gchar *property, const gint value)) \
  170. +    FUNC(dbusmenu_menuitem_show_to_user, void, (DbusmenuMenuitem *mi, guint timestamp)) \
  171. +    FUNC(dbusmenu_menuitem_take_children, GList*, (DbusmenuMenuitem *mi)) \
  172. +    FUNC(dbusmenu_server_new, DbusmenuServer*, (const gchar *object)) \
  173. +    FUNC(dbusmenu_server_set_root, void, (DbusmenuServer *server, DbusmenuMenuitem *root)) \
  174. +    FUNC(dbusmenu_server_set_status, void, (DbusmenuServer *server, DbusmenuStatus status))
  175. +
  176. +#define DBUSMENU_GTK_FUNCTIONS \
  177. +    FUNC(dbusmenu_menuitem_property_set_image, gboolean, (DbusmenuMenuitem *menuitem, const gchar *property, const GdkPixbuf *data)) \
  178. +    FUNC(dbusmenu_menuitem_property_set_shortcut, gboolean, (DbusmenuMenuitem *menuitem, guint key, GdkModifierType modifier))
  179. +
  180. +typedef struct _DbusmenuMenuitem DbusmenuMenuitem;
  181. +typedef struct _DbusmenuServer DbusmenuServer;
  182. +
  183. +enum DbusmenuStatus {
  184. +    DBUSMENU_STATUS_NORMAL,
  185. +    DBUSMENU_STATUS_NOTICE
  186. +};
  187. +
  188. +#define DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU "submenu"
  189. +#define DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY "children-display"
  190. +#define DBUSMENU_MENUITEM_PROP_ENABLED "enabled"
  191. +#define DBUSMENU_MENUITEM_PROP_ICON_DATA "icon-data"
  192. +#define DBUSMENU_MENUITEM_PROP_LABEL "label"
  193. +#define DBUSMENU_MENUITEM_PROP_SHORTCUT "shortcut"
  194. +#define DBUSMENU_MENUITEM_PROP_TYPE "type"
  195. +#define DBUSMENU_MENUITEM_PROP_TOGGLE_STATE "toggle-state"
  196. +#define DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE "toggle-type"
  197. +#define DBUSMENU_MENUITEM_PROP_VISIBLE "visible"
  198. +#define DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW "about-to-show"
  199. +#define DBUSMENU_MENUITEM_SIGNAL_EVENT "event"
  200. +#define DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED "item-activated"
  201. +#define DBUSMENU_MENUITEM_TOGGLE_CHECK "checkmark"
  202. +#define DBUSMENU_MENUITEM_TOGGLE_RADIO "radio"
  203. +#define DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED 1
  204. +#define DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED 0
  205. +#define DBUSMENU_SERVER_PROP_DBUS_OBJECT "dbus-object"
  206. +
  207. +class nsDbusmenuFunctions
  208. +{
  209. +public:
  210. +    static nsresult Init();
  211. +
  212. +#define FUNC(name, type, params) \
  213. +    typedef type (*_##name##_fn) params; \
  214. +    static _##name##_fn s_##name;
  215. +    DBUSMENU_GLIB_FUNCTIONS
  216. +    DBUSMENU_GTK_FUNCTIONS
  217. +#undef FUNC
  218. +
  219. +};
  220. +
  221. +#define dbusmenu_menuitem_child_add_position nsDbusmenuFunctions::s_dbusmenu_menuitem_child_add_position
  222. +#define dbusmenu_menuitem_child_append nsDbusmenuFunctions::s_dbusmenu_menuitem_child_append
  223. +#define dbusmenu_menuitem_child_delete nsDbusmenuFunctions::s_dbusmenu_menuitem_child_delete
  224. +#define dbusmenu_menuitem_get_children nsDbusmenuFunctions::s_dbusmenu_menuitem_get_children
  225. +#define dbusmenu_menuitem_new nsDbusmenuFunctions::s_dbusmenu_menuitem_new
  226. +#define dbusmenu_menuitem_property_get nsDbusmenuFunctions::s_dbusmenu_menuitem_property_get
  227. +#define dbusmenu_menuitem_property_get_bool nsDbusmenuFunctions::s_dbusmenu_menuitem_property_get_bool
  228. +#define dbusmenu_menuitem_property_remove nsDbusmenuFunctions::s_dbusmenu_menuitem_property_remove
  229. +#define dbusmenu_menuitem_property_set nsDbusmenuFunctions::s_dbusmenu_menuitem_property_set
  230. +#define dbusmenu_menuitem_property_set_bool nsDbusmenuFunctions::s_dbusmenu_menuitem_property_set_bool
  231. +#define dbusmenu_menuitem_property_set_int nsDbusmenuFunctions::s_dbusmenu_menuitem_property_set_int
  232. +#define dbusmenu_menuitem_show_to_user nsDbusmenuFunctions::s_dbusmenu_menuitem_show_to_user
  233. +#define dbusmenu_menuitem_take_children nsDbusmenuFunctions::s_dbusmenu_menuitem_take_children
  234. +#define dbusmenu_server_new nsDbusmenuFunctions::s_dbusmenu_server_new
  235. +#define dbusmenu_server_set_root nsDbusmenuFunctions::s_dbusmenu_server_set_root
  236. +#define dbusmenu_server_set_status nsDbusmenuFunctions::s_dbusmenu_server_set_status
  237. +
  238. +#define dbusmenu_menuitem_property_set_image nsDbusmenuFunctions::s_dbusmenu_menuitem_property_set_image
  239. +#define dbusmenu_menuitem_property_set_shortcut nsDbusmenuFunctions::s_dbusmenu_menuitem_property_set_shortcut
  240. +
  241. +#endif /* __nsDbusmenu_h__ */
  242. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenu.cpp
  243. ===================================================================
  244. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  245. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenu.cpp   2014-12-08 19:19:07.474645107 +0000
  246. @@ -0,0 +1,866 @@
  247. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  248. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  249. + */
  250. +/* This Source Code Form is subject to the terms of the Mozilla Public
  251. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  252. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  253. +
  254. +#define _IMPL_NS_LAYOUT
  255. +
  256. +#include "mozilla/GuardObjects.h"
  257. +#include "mozilla/MouseEvents.h"
  258. +#include "nsAutoPtr.h"
  259. +#include "nsBindingManager.h"
  260. +#include "nsComponentManagerUtils.h"
  261. +#include "nsContentUtils.h"
  262. +#include "nsCSSValue.h"
  263. +#include "nsGkAtoms.h"
  264. +#include "nsGtkUtils.h"
  265. +#include "nsIAtom.h"
  266. +#include "nsIContent.h"
  267. +#include "nsIDocument.h"
  268. +#include "nsIPresShell.h"
  269. +#include "nsIRunnable.h"
  270. +#include "nsITimer.h"
  271. +#include "nsString.h"
  272. +#include "nsStyleContext.h"
  273. +#include "nsStyleSet.h"
  274. +#include "nsStyleStruct.h"
  275. +#include "nsXBLBinding.h"
  276. +#include "nsXBLService.h"
  277. +
  278. +#include "nsNativeMenuAtoms.h"
  279. +#include "nsNativeMenuDocListener.h"
  280. +
  281. +#include <glib-object.h>
  282. +
  283. +#include "nsMenu.h"
  284. +
  285. +using namespace mozilla;
  286. +
  287. +class MOZ_STACK_CLASS nsMenuUpdateBatch
  288. +{
  289. +public:
  290. +    nsMenuUpdateBatch(nsMenu *aMenu MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
  291. +        mMenu(aMenu)
  292. +    {
  293. +        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
  294. +        mMenu->BeginUpdateBatchInternal();
  295. +    }
  296. +
  297. +    ~nsMenuUpdateBatch()
  298. +    {
  299. +        mMenu->EndUpdateBatch();
  300. +    }
  301. +
  302. +private:
  303. +    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
  304. +    nsMenu *mMenu;
  305. +};
  306. +
  307. +class nsSetAttrRunnableNoNotify : public nsRunnable
  308. +{
  309. +public:
  310. +    nsSetAttrRunnableNoNotify(nsIContent *aContent, nsIAtom *aAttribute,
  311. +                              nsAString& aValue) :
  312. +        mContent(aContent), mAttribute(aAttribute), mValue(aValue) { };
  313. +
  314. +    NS_IMETHODIMP Run()
  315. +    {
  316. +        return mContent->SetAttr(kNameSpaceID_None, mAttribute, mValue, false);
  317. +    }
  318. +
  319. +private:
  320. +    nsCOMPtr<nsIContent> mContent;
  321. +    nsCOMPtr<nsIAtom> mAttribute;
  322. +    nsAutoString mValue;
  323. +};
  324. +
  325. +class nsUnsetAttrRunnableNoNotify : public nsRunnable
  326. +{
  327. +public:
  328. +    nsUnsetAttrRunnableNoNotify(nsIContent *aContent, nsIAtom *aAttribute) :
  329. +        mContent(aContent), mAttribute(aAttribute) { };
  330. +
  331. +    NS_IMETHODIMP Run()
  332. +    {
  333. +        return mContent->UnsetAttr(kNameSpaceID_None, mAttribute, false);
  334. +    }
  335. +
  336. +private:
  337. +    nsCOMPtr<nsIContent> mContent;
  338. +    nsCOMPtr<nsIAtom> mAttribute;
  339. +};
  340. +
  341. +static void
  342. +AttachXBLBindings(nsIContent *aContent)
  343. +{
  344. +    nsIDocument *doc = aContent->OwnerDoc();
  345. +    nsIPresShell *shell = doc->GetShell();
  346. +    if (!shell) {
  347. +        return;
  348. +    }
  349. +
  350. +    nsRefPtr<nsStyleContext> sc =
  351. +        shell->StyleSet()->ResolveStyleFor(aContent->AsElement(),
  352. +                                           nullptr);
  353. +    if (!sc) {
  354. +        return;
  355. +    }
  356. +
  357. +    const nsStyleDisplay* display = sc->StyleDisplay();
  358. +    if (!display->mBinding) {
  359. +        return;
  360. +    }
  361. +
  362. +    nsXBLService* xbl = nsXBLService::GetInstance();
  363. +    if (!xbl) {
  364. +        return;
  365. +    }
  366. +
  367. +    nsRefPtr<nsXBLBinding> binding;
  368. +    bool dummy;
  369. +    nsresult rv = xbl->LoadBindings(aContent, display->mBinding->GetURI(),
  370. +                                    display->mBinding->mOriginPrincipal,
  371. +                                    getter_AddRefs(binding), &dummy);
  372. +    if ((NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED) || !binding) {
  373. +        return;
  374. +    }
  375. +
  376. +    doc->BindingManager()->AddToAttachedQueue(binding);
  377. +}
  378. +
  379. +void
  380. +nsMenu::SetPopupState(EPopupState aState)
  381. +{
  382. +    ClearFlags(((1U << NSMENU_NUMBER_OF_POPUPSTATE_BITS) - 1U) << NSMENU_NUMBER_OF_FLAGS);
  383. +    SetFlags(aState << NSMENU_NUMBER_OF_FLAGS);
  384. +
  385. +    if (!mPopupContent) {
  386. +        return;
  387. +    }
  388. +
  389. +    nsAutoString state;
  390. +    switch (aState) {
  391. +        case ePopupState_Showing:
  392. +            state.Assign(NS_LITERAL_STRING("showing"));
  393. +            break;
  394. +        case ePopupState_Open:
  395. +            state.Assign(NS_LITERAL_STRING("open"));
  396. +            break;
  397. +        case ePopupState_Hiding:
  398. +            state.Assign(NS_LITERAL_STRING("hiding"));
  399. +            break;
  400. +        default:
  401. +            break;
  402. +    }
  403. +
  404. +    if (nsContentUtils::IsSafeToRunScript()) {
  405. +        if (state.IsEmpty()) {
  406. +            mPopupContent->UnsetAttr(kNameSpaceID_None,
  407. +                                     nsNativeMenuAtoms::_moz_menupopupstate,
  408. +                                     false);
  409. +        } else {
  410. +            mPopupContent->SetAttr(kNameSpaceID_None,
  411. +                                   nsNativeMenuAtoms::_moz_menupopupstate,
  412. +                                   state, false);
  413. +        }
  414. +    } else {
  415. +        nsCOMPtr<nsIRunnable> r;
  416. +        if (state.IsEmpty()) {
  417. +            r = new nsUnsetAttrRunnableNoNotify(
  418. +                        mPopupContent, nsNativeMenuAtoms::_moz_menupopupstate);
  419. +        } else {
  420. +            r = new nsSetAttrRunnableNoNotify(
  421. +                        mPopupContent, nsNativeMenuAtoms::_moz_menupopupstate,
  422. +                        state);
  423. +        }
  424. +        nsContentUtils::AddScriptRunner(r);
  425. +    }
  426. +}
  427. +
  428. +/* static */ void
  429. +nsMenu::menu_event_cb(DbusmenuMenuitem *menu,
  430. +                      const gchar *name,
  431. +                      GVariant *value,
  432. +                      guint timestamp,
  433. +                      gpointer user_data)
  434. +{
  435. +    nsMenu *self = static_cast<nsMenu *>(user_data);
  436. +
  437. +    nsAutoCString event(name);
  438. +
  439. +    if (event.Equals(NS_LITERAL_CSTRING("closed"))) {
  440. +        self->OnClose();
  441. +        return;
  442. +    }
  443. +
  444. +    if (event.Equals(NS_LITERAL_CSTRING("opened"))) {
  445. +        self->OnOpen();
  446. +        return;
  447. +    }
  448. +}
  449. +
  450. +void
  451. +nsMenu::MaybeAddPlaceholderItem()
  452. +{
  453. +    NS_ASSERTION(!IsInUpdateBatch(),
  454. +                 "Shouldn't be modifying the native menu structure now");
  455. +
  456. +    GList *children = dbusmenu_menuitem_get_children(GetNativeData());
  457. +    if (!children) {
  458. +        NS_ASSERTION(!HasPlaceholderItem(), "Huh?");
  459. +
  460. +        DbusmenuMenuitem *ph = dbusmenu_menuitem_new();
  461. +        if (!ph) {
  462. +            return;
  463. +        }
  464. +
  465. +        dbusmenu_menuitem_property_set_bool(
  466. +            ph, DBUSMENU_MENUITEM_PROP_VISIBLE, false);
  467. +
  468. +        if (!dbusmenu_menuitem_child_append(GetNativeData(), ph)) {
  469. +            NS_WARNING("Failed to create placeholder item");
  470. +            g_object_unref(ph);
  471. +            return;
  472. +        }
  473. +
  474. +        g_object_unref(ph);
  475. +
  476. +        SetHasPlaceholderItem(true);
  477. +    }
  478. +}
  479. +
  480. +bool
  481. +nsMenu::EnsureNoPlaceholderItem()
  482. +{
  483. +    NS_ASSERTION(!IsInUpdateBatch(),
  484. +                 "Shouldn't be modifying the native menu structure now");
  485. +
  486. +    if (HasPlaceholderItem()) {
  487. +        GList *children = dbusmenu_menuitem_get_children(GetNativeData());
  488. +
  489. +        NS_ASSERTION(g_list_length(children) == 1,
  490. +                     "Unexpected number of children in native menu (should be 1!)");
  491. +
  492. +        SetHasPlaceholderItem(false);
  493. +
  494. +        if (!children) {
  495. +            return true;
  496. +        }
  497. +
  498. +        if (!dbusmenu_menuitem_child_delete(
  499. +                GetNativeData(), static_cast<DbusmenuMenuitem *>(children->data))) {
  500. +            NS_ERROR("Failed to remove placeholder item");
  501. +            return false;
  502. +        }
  503. +    }
  504. +
  505. +    return true;
  506. +}
  507. +
  508. +static void
  509. +DispatchMouseEvent(nsIContent *aTarget, uint32_t aMsg)
  510. +{
  511. +    if (!aTarget) {
  512. +        return;
  513. +    }
  514. +
  515. +    WidgetMouseEvent event(true, aMsg, nullptr, WidgetMouseEvent::eReal);
  516. +    aTarget->DispatchDOMEvent(&event, nullptr, nullptr, nullptr);
  517. +}
  518. +
  519. +void
  520. +nsMenu::OnOpen()
  521. +{
  522. +    if (NeedsRebuild()) {
  523. +        Build();
  524. +    }
  525. +
  526. +    nsWeakMenuObject<nsMenu> self(this);
  527. +    nsCOMPtr<nsIContent> origPopupContent(mPopupContent);
  528. +    {
  529. +        nsNativeMenuAutoUpdateBatch batch;
  530. +
  531. +        SetPopupState(ePopupState_Showing);
  532. +        DispatchMouseEvent(mPopupContent, NS_XUL_POPUP_SHOWING);
  533. +
  534. +        ContentNode()->SetAttr(kNameSpaceID_None, nsGkAtoms::open,
  535. +                               NS_LITERAL_STRING("true"), true);
  536. +    }
  537. +
  538. +    if (!self) {
  539. +        // We were deleted!
  540. +        return;
  541. +    }
  542. +
  543. +    // I guess that the popup could have changed
  544. +    if (origPopupContent != mPopupContent) {
  545. +        return;
  546. +    }
  547. +
  548. +    nsNativeMenuAutoUpdateBatch batch;
  549. +
  550. +    size_t count = ChildCount();
  551. +    for (size_t i = 0; i < count; ++i) {
  552. +        ChildAt(i)->ContainerIsOpening();
  553. +    }
  554. +
  555. +    SetPopupState(ePopupState_Open);
  556. +    DispatchMouseEvent(mPopupContent, NS_XUL_POPUP_SHOWN);
  557. +}
  558. +
  559. +void
  560. +nsMenu::Build()
  561. +{
  562. +    nsMenuUpdateBatch batch(this);
  563. +
  564. +    SetNeedsRebuild(false);
  565. +
  566. +    while (ChildCount() > 0) {
  567. +        RemoveChildAt(0);
  568. +    }
  569. +
  570. +    InitializePopup();
  571. +
  572. +    if (!mPopupContent) {
  573. +        return;
  574. +    }
  575. +
  576. +    uint32_t count = mPopupContent->GetChildCount();
  577. +    for (uint32_t i = 0; i < count; ++i) {
  578. +        nsIContent *childContent = mPopupContent->GetChildAt(i);
  579. +
  580. +        nsresult rv;
  581. +        nsMenuObject *child = CreateChild(childContent, &rv);
  582. +
  583. +        if (child) {
  584. +            rv = AppendChild(child);
  585. +        }
  586. +
  587. +        if (NS_FAILED(rv)) {
  588. +            NS_ERROR("Menu build failed");
  589. +            SetNeedsRebuild(true);
  590. +            return;
  591. +        }
  592. +    }
  593. +}
  594. +
  595. +void
  596. +nsMenu::InitializeNativeData()
  597. +{
  598. +    // Dbusmenu provides an "about-to-show" signal, and also "opened" and
  599. +    // "closed" events. However, Unity is the only thing that sends
  600. +    // both "about-to-show" and "opened" events. Unity 2D and the HUD only
  601. +    // send "opened" events, so we ignore "about-to-show" (I don't think
  602. +    // there's any real difference between them anyway).
  603. +    // To complicate things, there are certain conditions where we don't
  604. +    // get a "closed" event, so we need to be able to handle this :/
  605. +    g_signal_connect(G_OBJECT(GetNativeData()), "event",
  606. +                     G_CALLBACK(menu_event_cb), this);
  607. +
  608. +    UpdateLabel();
  609. +    UpdateSensitivity();
  610. +
  611. +    SetNeedsRebuild(true);
  612. +    MaybeAddPlaceholderItem();
  613. +
  614. +    AttachXBLBindings(ContentNode());
  615. +}
  616. +
  617. +void
  618. +nsMenu::Update(nsStyleContext *aStyleContext)
  619. +{
  620. +    UpdateVisibility(aStyleContext);
  621. +    UpdateIcon(aStyleContext);
  622. +}
  623. +
  624. +void
  625. +nsMenu::InitializePopup()
  626. +{
  627. +    nsCOMPtr<nsIContent> oldPopupContent;
  628. +    oldPopupContent.swap(mPopupContent);
  629. +
  630. +    for (uint32_t i = 0; i < ContentNode()->GetChildCount(); ++i) {
  631. +        nsIContent *child = ContentNode()->GetChildAt(i);
  632. +
  633. +        int32_t dummy;
  634. +        nsCOMPtr<nsIAtom> tag = child->OwnerDoc()->BindingManager()->ResolveTag(child, &dummy);
  635. +        if (tag == nsGkAtoms::menupopup) {
  636. +            mPopupContent = child;
  637. +            break;
  638. +        }
  639. +    }
  640. +
  641. +    if (oldPopupContent == mPopupContent) {
  642. +        return;
  643. +    }
  644. +
  645. +    // The popup has changed
  646. +
  647. +    if (oldPopupContent) {
  648. +        DocListener()->UnregisterForContentChanges(oldPopupContent);
  649. +    }
  650. +
  651. +    SetPopupState(ePopupState_Closed);
  652. +
  653. +    if (!mPopupContent) {
  654. +        return;
  655. +    }
  656. +
  657. +    AttachXBLBindings(mPopupContent);
  658. +
  659. +    DocListener()->RegisterForContentChanges(mPopupContent, this);
  660. +}
  661. +
  662. +void
  663. +nsMenu::BeginUpdateBatchInternal()
  664. +{
  665. +    NS_ASSERTION(!IsInUpdateBatch(), "Already in an update batch!");
  666. +
  667. +    SetIsInUpdateBatch(true);
  668. +    SetDidStructureMutate(false);
  669. +}
  670. +
  671. +nsresult
  672. +nsMenu::RemoveChildAt(size_t aIndex)
  673. +{
  674. +    NS_ASSERTION(IsInUpdateBatch() || !HasPlaceholderItem(),
  675. +                 "Shouldn't have a placeholder menuitem");
  676. +
  677. +    SetDidStructureMutate(true);
  678. +
  679. +    nsresult rv = nsMenuContainer::RemoveChildAt(aIndex, !IsInUpdateBatch());
  680. +
  681. +    if (!IsInUpdateBatch()) {
  682. +        MaybeAddPlaceholderItem();
  683. +    }
  684. +
  685. +    return rv;
  686. +}
  687. +
  688. +nsresult
  689. +nsMenu::RemoveChild(nsIContent *aChild)
  690. +{
  691. +    size_t index = IndexOf(aChild);
  692. +    if (index == NoIndex) {
  693. +        return NS_ERROR_INVALID_ARG;
  694. +    }
  695. +
  696. +    return RemoveChildAt(index);
  697. +}
  698. +
  699. +nsresult
  700. +nsMenu::InsertChildAfter(nsMenuObject *aChild, nsIContent *aPrevSibling)
  701. +{
  702. +    if (!IsInUpdateBatch() && !EnsureNoPlaceholderItem()) {
  703. +        return NS_ERROR_FAILURE;
  704. +    }
  705. +
  706. +    SetDidStructureMutate(true);
  707. +
  708. +    return nsMenuContainer::InsertChildAfter(aChild, aPrevSibling,
  709. +                                             !IsInUpdateBatch());
  710. +}
  711. +
  712. +nsresult
  713. +nsMenu::AppendChild(nsMenuObject *aChild)
  714. +{
  715. +    if (!IsInUpdateBatch() && !EnsureNoPlaceholderItem()) {
  716. +        return NS_ERROR_FAILURE;
  717. +    }
  718. +
  719. +    SetDidStructureMutate(true);
  720. +
  721. +    return nsMenuContainer::AppendChild(aChild, !IsInUpdateBatch());
  722. +}
  723. +
  724. +bool
  725. +nsMenu::CanOpen() const
  726. +{
  727. +    bool isVisible = dbusmenu_menuitem_property_get_bool(GetNativeData(),
  728. +                                                         DBUSMENU_MENUITEM_PROP_VISIBLE);
  729. +    bool isDisabled = ContentNode()->AttrValueIs(kNameSpaceID_None,
  730. +                                                 nsGkAtoms::disabled,
  731. +                                                 nsGkAtoms::_true,
  732. +                                                 eCaseMatters);
  733. +
  734. +    return (isVisible && !isDisabled);
  735. +}
  736. +
  737. +nsMenuObject::PropertyFlags
  738. +nsMenu::SupportedProperties() const
  739. +{
  740. +    return static_cast<nsMenuObject::PropertyFlags>(
  741. +        nsMenuObject::ePropLabel |
  742. +        nsMenuObject::ePropEnabled |
  743. +        nsMenuObject::ePropVisible |
  744. +        nsMenuObject::ePropIconData |
  745. +        nsMenuObject::ePropChildDisplay
  746. +    );
  747. +}
  748. +
  749. +nsMenu::nsMenu() :
  750. +    nsMenuContainer()
  751. +{
  752. +    MOZ_COUNT_CTOR(nsMenu);
  753. +}
  754. +
  755. +nsMenu::~nsMenu()
  756. +{
  757. +    if (IsInUpdateBatch()) {
  758. +        EndUpdateBatch();
  759. +    }
  760. +
  761. +    // Although nsTArray will take care of this in its destructor,
  762. +    // we have to manually ensure children are removed from our native menu
  763. +    // item, just in case our parent recycles us
  764. +    while (ChildCount() > 0) {
  765. +        RemoveChildAt(0);
  766. +    }
  767. +
  768. +    EnsureNoPlaceholderItem();
  769. +
  770. +    if (DocListener() && mPopupContent) {
  771. +        DocListener()->UnregisterForContentChanges(mPopupContent);
  772. +    }
  773. +
  774. +    if (GetNativeData()) {
  775. +        g_signal_handlers_disconnect_by_func(GetNativeData(),
  776. +                                             FuncToGpointer(menu_event_cb),
  777. +                                             this);
  778. +    }
  779. +
  780. +    MOZ_COUNT_DTOR(nsMenu);
  781. +}
  782. +
  783. +/* static */ nsMenuObject*
  784. +nsMenu::Create(nsMenuContainer *aParent, nsIContent *aContent)
  785. +{
  786. +    nsAutoPtr<nsMenu> menu(new nsMenu());
  787. +    if (NS_FAILED(menu->Init(aParent, aContent))) {
  788. +        return nullptr;
  789. +    }
  790. +
  791. +    return menu.forget();
  792. +}
  793. +
  794. +static void
  795. +DoOpen(nsITimer *aTimer, void *aClosure)
  796. +{
  797. +    nsAutoWeakMenuObject<nsMenu> weakMenu(
  798. +        static_cast<nsWeakMenuObject<nsMenu> *>(aClosure));
  799. +
  800. +    if (weakMenu) {
  801. +        dbusmenu_menuitem_show_to_user(weakMenu->GetNativeData(), 0);
  802. +    }
  803. +
  804. +    NS_RELEASE(aTimer);
  805. +}
  806. +
  807. +nsMenuObject::EType
  808. +nsMenu::Type() const
  809. +{
  810. +    return nsMenuObject::eType_Menu;
  811. +}
  812. +
  813. +bool
  814. +nsMenu::IsBeingDisplayed() const
  815. +{
  816. +    return PopupState() == ePopupState_Open;
  817. +}
  818. +
  819. +bool
  820. +nsMenu::NeedsRebuild() const
  821. +{
  822. +    return HasFlags(eFlag_NeedsRebuild);
  823. +}
  824. +
  825. +void
  826. +nsMenu::OpenMenu()
  827. +{
  828. +    if (!CanOpen()) {
  829. +        return;
  830. +    }
  831. +
  832. +    // Here, we synchronously fire popupshowing and popupshown events and then
  833. +    // open the menu after a short delay. This allows the menu to refresh before
  834. +    // it's shown, and avoids an issue where keyboard focus is not on the first
  835. +    // item of the history menu in Firefox when opening it with the keyboard,
  836. +    // because extra items to appear at the top of the menu
  837. +
  838. +    OnOpen();
  839. +
  840. +    nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
  841. +    if (!timer) {
  842. +        return;
  843. +    }
  844. +
  845. +    nsAutoWeakMenuObject<nsMenu> weakMenu(this);
  846. +
  847. +    if (NS_FAILED(timer->InitWithFuncCallback(DoOpen, weakMenu.getWeakPtr(),
  848. +                                              100, nsITimer::TYPE_ONE_SHOT))) {
  849. +        return;
  850. +    }
  851. +
  852. +    timer.forget();
  853. +    weakMenu.forget();
  854. +}
  855. +
  856. +void
  857. +nsMenu::OnClose()
  858. +{
  859. +    if (PopupState() == ePopupState_Closed) {
  860. +        return;
  861. +    }
  862. +
  863. +    // We do this to avoid mutating our view of the menu until
  864. +    // after we have finished
  865. +    nsNativeMenuAutoUpdateBatch batch;
  866. +
  867. +    SetPopupState(ePopupState_Hiding);
  868. +    DispatchMouseEvent(mPopupContent, NS_XUL_POPUP_HIDING);
  869. +
  870. +    // Sigh, make sure all of our descendants are closed, as we don't
  871. +    // always get closed events for submenus when scrubbing quickly through
  872. +    // the menu
  873. +    size_t count = ChildCount();
  874. +    for (size_t i = 0; i < count; ++i) {
  875. +        if (ChildAt(i)->Type() == nsMenuObject::eType_Menu) {
  876. +            static_cast<nsMenu *>(ChildAt(i))->OnClose();
  877. +        }
  878. +    }
  879. +
  880. +    SetPopupState(ePopupState_Closed);
  881. +    DispatchMouseEvent(mPopupContent, NS_XUL_POPUP_HIDDEN);
  882. +
  883. +    ContentNode()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::open, true);
  884. +}
  885. +
  886. +void
  887. +nsMenu::OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute)
  888. +{
  889. +    NS_ASSERTION(aContent == ContentNode() || aContent == mPopupContent,
  890. +                 "Received an event that wasn't meant for us!");
  891. +
  892. +    if (aAttribute == nsGkAtoms::open) {
  893. +        return;
  894. +    }
  895. +
  896. +    if (Parent()->NeedsRebuild()) {
  897. +        return;
  898. +    }
  899. +
  900. +    if (aContent == ContentNode()) {
  901. +        if (aAttribute == nsGkAtoms::disabled) {
  902. +            UpdateSensitivity();
  903. +        } else if (aAttribute == nsGkAtoms::label ||
  904. +                   aAttribute == nsGkAtoms::accesskey ||
  905. +                   aAttribute == nsGkAtoms::crop) {
  906. +            UpdateLabel();
  907. +        }
  908. +    }
  909. +
  910. +    if (!Parent()->IsBeingDisplayed() || aContent != ContentNode()) {
  911. +        return;
  912. +    }
  913. +
  914. +    if (aAttribute == nsGkAtoms::hidden ||
  915. +        aAttribute == nsGkAtoms::collapsed) {
  916. +        nsRefPtr<nsStyleContext> sc = GetStyleContext();
  917. +        UpdateVisibility(sc);
  918. +    } else if (aAttribute == nsGkAtoms::image) {
  919. +        nsRefPtr<nsStyleContext> sc = GetStyleContext();
  920. +        UpdateIcon(sc);
  921. +    }
  922. +}
  923. +
  924. +void
  925. +nsMenu::OnContentInserted(nsIContent *aContainer, nsIContent *aChild,
  926. +                          nsIContent *aPrevSibling)
  927. +{
  928. +    NS_ASSERTION(aContainer == ContentNode() || aContainer == mPopupContent,
  929. +                 "Received an event that wasn't meant for us!");
  930. +
  931. +    if (NeedsRebuild()) {
  932. +        return;
  933. +    }
  934. +
  935. +    if (PopupState() == ePopupState_Closed) {
  936. +        SetNeedsRebuild(true);
  937. +        return;
  938. +    }
  939. +
  940. +    if (aContainer == mPopupContent) {
  941. +        nsresult rv;
  942. +        nsMenuObject *child = CreateChild(aChild, &rv);
  943. +
  944. +        if (child) {
  945. +            rv = InsertChildAfter(child, aPrevSibling);
  946. +        }
  947. +        if (NS_FAILED(rv)) {
  948. +            NS_ERROR("OnContentInserted() failed");
  949. +            SetNeedsRebuild(true);
  950. +        }
  951. +    } else {
  952. +        Build();
  953. +    }
  954. +}
  955. +
  956. +void
  957. +nsMenu::OnContentRemoved(nsIContent *aContainer, nsIContent *aChild)
  958. +{
  959. +    NS_ASSERTION(aContainer == ContentNode() || aContainer == mPopupContent,
  960. +                 "Received an event that wasn't meant for us!");
  961. +
  962. +    if (NeedsRebuild()) {
  963. +        return;
  964. +    }
  965. +
  966. +    if (PopupState() == ePopupState_Closed) {
  967. +        SetNeedsRebuild(true);
  968. +        return;
  969. +    }
  970. +
  971. +    if (aContainer == mPopupContent) {
  972. +        if (NS_FAILED(RemoveChild(aChild))) {
  973. +            NS_ERROR("OnContentRemoved() failed");
  974. +            SetNeedsRebuild(true);
  975. +        }
  976. +    } else {
  977. +        Build();
  978. +    }
  979. +}
  980. +
  981. +/*
  982. + * Some menus (eg, the History menu in Firefox) refresh themselves on
  983. + * opening by removing all children and then re-adding new ones. As this
  984. + * happens whilst the menu is opening in Unity, it causes some flickering
  985. + * as the menu popup is resized multiple times. To avoid this, we try to
  986. + * reuse native menu items when the menu structure changes during a
  987. + * batched update. If we can handle menu structure changes from Gecko
  988. + * just by updating properties of native menu items (rather than destroying
  989. + * and creating new ones), then we eliminate any flickering that occurs as
  990. + * the menu is opened. To do this, we don't modify any native menu items
  991. + * until the end of the update batch.
  992. + */
  993. +
  994. +void
  995. +nsMenu::BeginUpdateBatch(nsIContent *aContent)
  996. +{
  997. +    NS_ASSERTION(aContent == ContentNode() || aContent == mPopupContent,
  998. +                 "Received an event that wasn't meant for us!");
  999. +
  1000. +    if (aContent == mPopupContent) {
  1001. +        BeginUpdateBatchInternal();
  1002. +    }
  1003. +}
  1004. +
  1005. +void
  1006. +nsMenu::EndUpdateBatch()
  1007. +{
  1008. +    NS_ASSERTION(IsInUpdateBatch(), "Not in an update batch");
  1009. +
  1010. +    SetIsInUpdateBatch(false);
  1011. +
  1012. +    /* Optimize for the case where we only had attribute changes */
  1013. +    if (!DidStructureMutate()) {
  1014. +        return;
  1015. +    }
  1016. +
  1017. +    if (!EnsureNoPlaceholderItem()) {
  1018. +        SetNeedsRebuild(true);
  1019. +        return;
  1020. +    }
  1021. +
  1022. +    GList *nextNativeChild = dbusmenu_menuitem_get_children(GetNativeData());
  1023. +    DbusmenuMenuitem *nextOwnedNativeChild = nullptr;
  1024. +
  1025. +    size_t count = ChildCount();
  1026. +
  1027. +    // Find the first native menu item that is `owned` by a corresponding
  1028. +    // Gecko menuitem
  1029. +    for (size_t i = 0; i < count; ++i) {
  1030. +        if (ChildAt(i)->GetNativeData()) {
  1031. +            nextOwnedNativeChild = ChildAt(i)->GetNativeData();
  1032. +            break;
  1033. +        }
  1034. +    }
  1035. +
  1036. +    // Now iterate over all Gecko menuitems
  1037. +    for (size_t i = 0; i < count; ++i) {
  1038. +        nsMenuObject *child = ChildAt(i);
  1039. +
  1040. +        if (child->GetNativeData()) {
  1041. +            // This child already has a corresponding native menuitem.
  1042. +            // Remove all preceding orphaned native items. At this point, we
  1043. +            // modify the native menu structure.
  1044. +            while (nextNativeChild &&
  1045. +                   nextNativeChild->data != nextOwnedNativeChild) {
  1046. +
  1047. +                DbusmenuMenuitem *data =
  1048. +                    static_cast<DbusmenuMenuitem *>(nextNativeChild->data);
  1049. +                nextNativeChild = nextNativeChild->next;
  1050. +
  1051. +                if (!dbusmenu_menuitem_child_delete(GetNativeData(), data)) {
  1052. +                    NS_ERROR("Failed to remove orphaned native item from menu");
  1053. +                    SetNeedsRebuild(true);
  1054. +                    return;
  1055. +                }
  1056. +            }
  1057. +
  1058. +            if (nextNativeChild) {
  1059. +                nextNativeChild = nextNativeChild->next;
  1060. +            }
  1061. +
  1062. +            // Now find the next native menu item that is `owned`
  1063. +            nextOwnedNativeChild = nullptr;
  1064. +            for (size_t j = i + 1; j < count; ++j) {
  1065. +                if (ChildAt(j)->GetNativeData()) {
  1066. +                    nextOwnedNativeChild = ChildAt(j)->GetNativeData();
  1067. +                    break;
  1068. +                }
  1069. +            }
  1070. +        } else {
  1071. +            // This child is new, and doesn't have a native menu item. Find one!
  1072. +            if (nextNativeChild &&
  1073. +                nextNativeChild->data != nextOwnedNativeChild) {
  1074. +
  1075. +                DbusmenuMenuitem *data =
  1076. +                    static_cast<DbusmenuMenuitem *>(nextNativeChild->data);
  1077. +
  1078. +                if (NS_SUCCEEDED(child->AdoptNativeData(data))) {
  1079. +                    nextNativeChild = nextNativeChild->next;
  1080. +                }
  1081. +            }
  1082. +
  1083. +            // There wasn't a suitable one available, so create a new one.
  1084. +            // At this point, we modify the native menu structure.
  1085. +            if (!child->GetNativeData()) {
  1086. +                child->CreateNativeData();
  1087. +                if (!dbusmenu_menuitem_child_add_position(GetNativeData(),
  1088. +                                                          child->GetNativeData(),
  1089. +                                                          i)) {
  1090. +                    NS_ERROR("Failed to add new native item");
  1091. +                    SetNeedsRebuild(true);
  1092. +                    return;
  1093. +                }
  1094. +            }
  1095. +        }
  1096. +    }
  1097. +
  1098. +    while (nextNativeChild) {
  1099. +
  1100. +        DbusmenuMenuitem *data =
  1101. +            static_cast<DbusmenuMenuitem *>(nextNativeChild->data);
  1102. +        nextNativeChild = nextNativeChild->next;
  1103. +
  1104. +        if (!dbusmenu_menuitem_child_delete(GetNativeData(), data)) {
  1105. +            NS_ERROR("Failed to remove orphaned native item from menu");
  1106. +            SetNeedsRebuild(true);
  1107. +            return;
  1108. +        }
  1109. +    }
  1110. +
  1111. +    MaybeAddPlaceholderItem();
  1112. +}
  1113. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenu.h
  1114. ===================================================================
  1115. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  1116. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenu.h 2014-12-08 19:19:07.474645107 +0000
  1117. @@ -0,0 +1,166 @@
  1118. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  1119. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  1120. + */
  1121. +/* This Source Code Form is subject to the terms of the Mozilla Public
  1122. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  1123. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  1124. +
  1125. +#ifndef __nsMenu_h__
  1126. +#define __nsMenu_h__
  1127. +
  1128. +#include "mozilla/Attributes.h"
  1129. +#include "nsCOMPtr.h"
  1130. +
  1131. +#include "nsDbusmenu.h"
  1132. +#include "nsMenuContainer.h"
  1133. +#include "nsMenuObject.h"
  1134. +
  1135. +#include <glib.h>
  1136. +
  1137. +class nsIAtom;
  1138. +class nsIContent;
  1139. +class nsStyleContext;
  1140. +
  1141. +#define NSMENU_NUMBER_OF_POPUPSTATE_BITS 2U
  1142. +#define NSMENU_NUMBER_OF_FLAGS           4U
  1143. +
  1144. +// This class represents a menu
  1145. +class nsMenu MOZ_FINAL : public nsMenuContainer
  1146. +{
  1147. +public:
  1148. +    ~nsMenu();
  1149. +
  1150. +    static nsMenuObject* Create(nsMenuContainer *aParent,
  1151. +                                nsIContent *aContent);
  1152. +
  1153. +    nsMenuObject::EType Type() const;
  1154. +
  1155. +    bool IsBeingDisplayed() const;
  1156. +    bool NeedsRebuild() const;
  1157. +
  1158. +    // Tell the desktop shell to display this menu
  1159. +    void OpenMenu();
  1160. +
  1161. +    // Normally called via the shell, but it's public so that child
  1162. +    // menuitems can do the shells work. Sigh....
  1163. +    void OnClose();
  1164. +
  1165. +    void OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute);
  1166. +    void OnContentInserted(nsIContent *aContainer, nsIContent *aChild,
  1167. +                           nsIContent *aPrevSibling);
  1168. +    void OnContentRemoved(nsIContent *aContainer, nsIContent *aChild);
  1169. +    void BeginUpdateBatch(nsIContent *aContent);
  1170. +    void EndUpdateBatch();
  1171. +
  1172. +private:
  1173. +    friend class nsMenuUpdateBatch;
  1174. +
  1175. +    enum {
  1176. +        // This menu needs rebuilding the next time it is opened
  1177. +        eFlag_NeedsRebuild = 1 << 0,
  1178. +
  1179. +        // This menu contains a placeholder
  1180. +        eFlag_HasPlaceholderItem = 1 << 1,
  1181. +
  1182. +        // This menu is currently receiving a batch of updates, and
  1183. +        // the native structure should not be modified
  1184. +        eFlag_InUpdateBatch = 1 << 2,
  1185. +
  1186. +        // Children were added to / removed from this menu (only valid
  1187. +        // when eFlag_InUpdateBatch is set)
  1188. +        eFlag_StructureMutated = 1 << 3
  1189. +    };
  1190. +
  1191. +    enum EPopupState {
  1192. +        ePopupState_Closed,
  1193. +        ePopupState_Showing,
  1194. +        ePopupState_Open,
  1195. +        ePopupState_Hiding
  1196. +    };
  1197. +
  1198. +    nsMenu();
  1199. +
  1200. +    void SetNeedsRebuild(bool aValue)
  1201. +    {
  1202. +        if (aValue) {
  1203. +            SetFlags(eFlag_NeedsRebuild);
  1204. +        } else {
  1205. +            ClearFlags(eFlag_NeedsRebuild);
  1206. +        }
  1207. +    }
  1208. +    bool HasPlaceholderItem() const
  1209. +    {
  1210. +        return HasFlags(eFlag_HasPlaceholderItem);
  1211. +    }
  1212. +    void SetHasPlaceholderItem(bool aValue)
  1213. +    {
  1214. +        if (aValue) {
  1215. +            SetFlags(eFlag_HasPlaceholderItem);
  1216. +        } else {
  1217. +            ClearFlags(eFlag_HasPlaceholderItem);
  1218. +        }
  1219. +    }
  1220. +
  1221. +    bool IsInUpdateBatch() const
  1222. +    {
  1223. +        return HasFlags(eFlag_InUpdateBatch);
  1224. +    }
  1225. +    void SetIsInUpdateBatch(bool aValue)
  1226. +    {
  1227. +        if (aValue) {
  1228. +            SetFlags(eFlag_InUpdateBatch);
  1229. +        } else {
  1230. +            ClearFlags(eFlag_InUpdateBatch);
  1231. +        }
  1232. +    }
  1233. +
  1234. +    bool DidStructureMutate() const
  1235. +    {
  1236. +        return HasFlags(eFlag_StructureMutated);
  1237. +    }
  1238. +    void SetDidStructureMutate(bool aValue)
  1239. +    {
  1240. +        if (aValue) {
  1241. +            SetFlags(eFlag_StructureMutated);
  1242. +        } else {
  1243. +            ClearFlags(eFlag_StructureMutated);
  1244. +        }
  1245. +    }
  1246. +
  1247. +    EPopupState PopupState() const
  1248. +    {
  1249. +        return static_cast<EPopupState>(
  1250. +            (GetFlags() &
  1251. +             (((1U << NSMENU_NUMBER_OF_POPUPSTATE_BITS) - 1U)
  1252. +              << NSMENU_NUMBER_OF_FLAGS)) >> NSMENU_NUMBER_OF_FLAGS);
  1253. +    };
  1254. +    void SetPopupState(EPopupState aState);
  1255. +
  1256. +    static void menu_event_cb(DbusmenuMenuitem *menu,
  1257. +                              const gchar *name,
  1258. +                              GVariant *value,
  1259. +                              guint timestamp,
  1260. +                              gpointer user_data);
  1261. +
  1262. +    // We add a placeholder item to empty menus so that Unity actually treats
  1263. +    // us as a proper menu, rather than a menuitem without a submenu
  1264. +    void MaybeAddPlaceholderItem();
  1265. +    bool EnsureNoPlaceholderItem();
  1266. +
  1267. +    void OnOpen();
  1268. +    void Build();
  1269. +    void InitializeNativeData();
  1270. +    void Update(nsStyleContext *aStyleContext);
  1271. +    void InitializePopup();
  1272. +    void BeginUpdateBatchInternal();
  1273. +    nsresult RemoveChildAt(size_t aIndex);
  1274. +    nsresult RemoveChild(nsIContent *aChild);
  1275. +    nsresult InsertChildAfter(nsMenuObject *aChild, nsIContent *aPrevSibling);
  1276. +    nsresult AppendChild(nsMenuObject *aChild);
  1277. +    bool CanOpen() const;
  1278. +    nsMenuObject::PropertyFlags SupportedProperties() const;
  1279. +
  1280. +    nsCOMPtr<nsIContent> mPopupContent;
  1281. +};
  1282. +
  1283. +#endif /* __nsMenu_h__ */
  1284. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuBar.cpp
  1285. ===================================================================
  1286. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  1287. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuBar.cpp    2014-12-08 19:19:07.474645107 +0000
  1288. @@ -0,0 +1,545 @@
  1289. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  1290. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  1291. + */
  1292. +/* This Source Code Form is subject to the terms of the Mozilla Public
  1293. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  1294. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  1295. +
  1296. +#include "mozilla/DebugOnly.h"
  1297. +#include "mozilla/dom/Element.h"
  1298. +#include "mozilla/Preferences.h"
  1299. +#include "nsAutoPtr.h"
  1300. +#include "nsIDocument.h"
  1301. +#include "nsIDOMEvent.h"
  1302. +#include "nsIDOMEventListener.h"
  1303. +#include "nsIDOMEventTarget.h"
  1304. +#include "nsIDOMKeyEvent.h"
  1305. +#include "nsIWidget.h"
  1306. +#include "nsTArray.h"
  1307. +#include "nsUnicharUtils.h"
  1308. +
  1309. +#include "nsMenu.h"
  1310. +#include "nsNativeMenuAtoms.h"
  1311. +#include "nsNativeMenuService.h"
  1312. +
  1313. +#include <gdk/gdk.h>
  1314. +#include <gdk/gdkx.h>
  1315. +#include <glib.h>
  1316. +#include <glib-object.h>
  1317. +
  1318. +#include "nsMenuBar.h"
  1319. +
  1320. +using namespace mozilla;
  1321. +
  1322. +class nsMenuBarDocEventListener MOZ_FINAL : public nsIDOMEventListener
  1323. +{
  1324. +public:
  1325. +    NS_DECL_ISUPPORTS
  1326. +    NS_DECL_NSIDOMEVENTLISTENER
  1327. +
  1328. +    nsMenuBarDocEventListener(nsMenuBar *aOwner) : mOwner(aOwner) { };
  1329. +
  1330. +private:
  1331. +    ~nsMenuBarDocEventListener() { };
  1332. +
  1333. +    nsMenuBar *mOwner;
  1334. +};
  1335. +
  1336. +NS_IMPL_ISUPPORTS(nsMenuBarDocEventListener, nsIDOMEventListener)
  1337. +
  1338. +NS_IMETHODIMP
  1339. +nsMenuBarDocEventListener::HandleEvent(nsIDOMEvent *aEvent)
  1340. +{
  1341. +    nsAutoString type;
  1342. +    nsresult rv = aEvent->GetType(type);
  1343. +    if (NS_FAILED(rv)) {
  1344. +        NS_WARNING("Failed to determine event type");
  1345. +        return rv;
  1346. +    }
  1347. +
  1348. +    if (type.Equals(NS_LITERAL_STRING("focus"))) {
  1349. +        mOwner->Focus();
  1350. +    } else if (type.Equals(NS_LITERAL_STRING("blur"))) {
  1351. +        mOwner->Blur();
  1352. +    } else if (type.Equals(NS_LITERAL_STRING("keypress"))) {
  1353. +        rv = mOwner->Keypress(aEvent);
  1354. +    } else if (type.Equals(NS_LITERAL_STRING("keydown"))) {
  1355. +        rv = mOwner->KeyDown(aEvent);
  1356. +    } else if (type.Equals(NS_LITERAL_STRING("keyup"))) {
  1357. +        rv = mOwner->KeyUp(aEvent);
  1358. +    }
  1359. +
  1360. +    return rv;
  1361. +}
  1362. +
  1363. +void
  1364. +nsMenuBar::Build()
  1365. +{
  1366. +    uint32_t count = ContentNode()->GetChildCount();
  1367. +    for (uint32_t i = 0; i < count; ++i) {
  1368. +        nsIContent *childContent = ContentNode()->GetChildAt(i);
  1369. +
  1370. +        nsresult rv;
  1371. +        nsMenuObject *child = CreateChild(childContent, &rv);
  1372. +
  1373. +        if (child) {
  1374. +            rv = AppendChild(child);
  1375. +        }
  1376. +
  1377. +        if (NS_FAILED(rv)) {
  1378. +            NS_ERROR("Failed to build menubar");
  1379. +            return;
  1380. +        }
  1381. +    }
  1382. +}
  1383. +
  1384. +void
  1385. +nsMenuBar::DisconnectDocumentEventListeners()
  1386. +{
  1387. +    mDocument->RemoveEventListener(NS_LITERAL_STRING("focus"),
  1388. +                                   mEventListener,
  1389. +                                   true);
  1390. +    mDocument->RemoveEventListener(NS_LITERAL_STRING("blur"),
  1391. +                                   mEventListener,
  1392. +                                   true);
  1393. +    mDocument->RemoveEventListener(NS_LITERAL_STRING("keypress"),
  1394. +                                   mEventListener,
  1395. +                                   false);
  1396. +    mDocument->RemoveEventListener(NS_LITERAL_STRING("keydown"),
  1397. +                                   mEventListener,
  1398. +                                   false);
  1399. +    mDocument->RemoveEventListener(NS_LITERAL_STRING("keyup"),
  1400. +                                   mEventListener,
  1401. +                                   false);
  1402. +}
  1403. +
  1404. +void
  1405. +nsMenuBar::SetShellShowingMenuBar(bool aShowing)
  1406. +{
  1407. +    ContentNode()->OwnerDoc()->GetRootElement()->SetAttr(
  1408. +        kNameSpaceID_None, nsNativeMenuAtoms::shellshowingmenubar,
  1409. +        aShowing ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false"),
  1410. +        true);
  1411. +}
  1412. +
  1413. +void
  1414. +nsMenuBar::Focus()
  1415. +{
  1416. +    ContentNode()->SetAttr(kNameSpaceID_None, nsNativeMenuAtoms::openedwithkey,
  1417. +                           NS_LITERAL_STRING("false"), true);
  1418. +}
  1419. +
  1420. +void
  1421. +nsMenuBar::Blur()
  1422. +{
  1423. +    // We do this here in case we lose focus before getting the
  1424. +    // keyup event, which leaves the menubar state looking like
  1425. +    // the alt key is stuck down
  1426. +    dbusmenu_server_set_status(mServer, DBUSMENU_STATUS_NORMAL);
  1427. +}
  1428. +
  1429. +static bool
  1430. +ShouldHandleKeyEvent(nsIDOMEvent *aEvent)
  1431. +{
  1432. +    bool handled, trusted = false;
  1433. +    aEvent->GetPreventDefault(&handled);
  1434. +    aEvent->GetIsTrusted(&trusted);
  1435. +
  1436. +    if (handled || !trusted) {
  1437. +        return false;
  1438. +    }
  1439. +
  1440. +    return true;
  1441. +}
  1442. +
  1443. +nsMenuBar::ModifierFlags
  1444. +nsMenuBar::GetModifiersFromEvent(nsIDOMKeyEvent *aEvent)
  1445. +{
  1446. +    ModifierFlags modifiers = static_cast<ModifierFlags>(0);
  1447. +    bool modifier;
  1448. +
  1449. +    aEvent->GetAltKey(&modifier);
  1450. +    if (modifier) {
  1451. +        modifiers = static_cast<ModifierFlags>(modifiers | eModifierAlt);
  1452. +    }
  1453. +
  1454. +    aEvent->GetShiftKey(&modifier);
  1455. +    if (modifier) {
  1456. +        modifiers = static_cast<ModifierFlags>(modifiers | eModifierShift);
  1457. +    }
  1458. +
  1459. +    aEvent->GetCtrlKey(&modifier);
  1460. +    if (modifier) {
  1461. +        modifiers = static_cast<ModifierFlags>(modifiers | eModifierCtrl);
  1462. +    }
  1463. +
  1464. +    aEvent->GetMetaKey(&modifier);
  1465. +    if (modifier) {
  1466. +        modifiers = static_cast<ModifierFlags>(modifiers | eModifierMeta);
  1467. +    }
  1468. +
  1469. +    return modifiers;
  1470. +}
  1471. +
  1472. +nsresult
  1473. +nsMenuBar::Keypress(nsIDOMEvent *aEvent)
  1474. +{
  1475. +    if (!ShouldHandleKeyEvent(aEvent)) {
  1476. +        return NS_OK;
  1477. +    }
  1478. +
  1479. +    nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
  1480. +    if (!keyEvent) {
  1481. +        return NS_OK;
  1482. +    }
  1483. +
  1484. +    ModifierFlags modifiers = GetModifiersFromEvent(keyEvent);
  1485. +    if (((modifiers & mAccessKeyMask) == 0) ||
  1486. +        ((modifiers & ~mAccessKeyMask) != 0)) {
  1487. +        return NS_OK;
  1488. +    }
  1489. +
  1490. +    uint32_t charCode;
  1491. +    keyEvent->GetCharCode(&charCode);
  1492. +    if (charCode == 0) {
  1493. +        return NS_OK;
  1494. +    }
  1495. +
  1496. +    char16_t ch = char16_t(charCode);
  1497. +    char16_t chl = ToLowerCase(ch);
  1498. +    char16_t chu = ToUpperCase(ch);
  1499. +
  1500. +    nsMenuObject *found = nullptr;
  1501. +    uint32_t count = ChildCount();
  1502. +    for (uint32_t i = 0; i < count; ++i) {
  1503. +        nsAutoString accesskey;
  1504. +        ChildAt(i)->ContentNode()->GetAttr(kNameSpaceID_None,
  1505. +                                           nsGkAtoms::accesskey,
  1506. +                                           accesskey);
  1507. +        const nsAutoString::char_type *key = accesskey.BeginReading();
  1508. +        if (*key == chu || *key == chl) {
  1509. +            found = ChildAt(i);
  1510. +            break;
  1511. +        }
  1512. +    }
  1513. +
  1514. +    if (!found || found->Type() != nsMenuObject::eType_Menu) {
  1515. +        return NS_OK;
  1516. +    }
  1517. +
  1518. +    ContentNode()->SetAttr(kNameSpaceID_None, nsNativeMenuAtoms::openedwithkey,
  1519. +                           NS_LITERAL_STRING("true"), true);
  1520. +    static_cast<nsMenu *>(found)->OpenMenu();
  1521. +
  1522. +    aEvent->StopPropagation();
  1523. +    aEvent->PreventDefault();
  1524. +
  1525. +    return NS_OK;
  1526. +}
  1527. +
  1528. +nsresult
  1529. +nsMenuBar::KeyDown(nsIDOMEvent *aEvent)
  1530. +{
  1531. +    if (!ShouldHandleKeyEvent(aEvent)) {
  1532. +        return NS_OK;
  1533. +    }
  1534. +
  1535. +    nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
  1536. +    if (!keyEvent) {
  1537. +        return NS_OK;
  1538. +    }
  1539. +
  1540. +    uint32_t keyCode;
  1541. +    keyEvent->GetKeyCode(&keyCode);
  1542. +    ModifierFlags modifiers = GetModifiersFromEvent(keyEvent);
  1543. +    if ((keyCode != mAccessKey) || ((modifiers & ~mAccessKeyMask) != 0)) {
  1544. +        return NS_OK;
  1545. +    }
  1546. +
  1547. +    dbusmenu_server_set_status(mServer, DBUSMENU_STATUS_NOTICE);
  1548. +
  1549. +    return NS_OK;
  1550. +}
  1551. +
  1552. +nsresult
  1553. +nsMenuBar::KeyUp(nsIDOMEvent *aEvent)
  1554. +{
  1555. +    if (!ShouldHandleKeyEvent(aEvent)) {
  1556. +        return NS_OK;
  1557. +    }
  1558. +
  1559. +    nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
  1560. +    if (!keyEvent) {
  1561. +        return NS_OK;
  1562. +    }
  1563. +
  1564. +    uint32_t keyCode;
  1565. +    keyEvent->GetKeyCode(&keyCode);
  1566. +    if (keyCode == mAccessKey) {
  1567. +        dbusmenu_server_set_status(mServer, DBUSMENU_STATUS_NORMAL);
  1568. +    }
  1569. +
  1570. +    return NS_OK;
  1571. +}
  1572. +
  1573. +nsMenuBar::nsMenuBar() :
  1574. +    nsMenuContainer(),
  1575. +    mTopLevel(nullptr),
  1576. +    mServer(nullptr),
  1577. +    mIsActive(false)
  1578. +{
  1579. +    MOZ_COUNT_CTOR(nsMenuBar);
  1580. +}
  1581. +
  1582. +nsresult
  1583. +nsMenuBar::Init(nsIWidget *aParent, nsIContent *aMenuBarNode)
  1584. +{
  1585. +    NS_ENSURE_ARG(aParent);
  1586. +    NS_ENSURE_ARG(aMenuBarNode);
  1587. +
  1588. +    GdkWindow *gdkWin = static_cast<GdkWindow *>(
  1589. +        aParent->GetNativeData(NS_NATIVE_WINDOW));
  1590. +    if (!gdkWin) {
  1591. +        return NS_ERROR_FAILURE;
  1592. +    }
  1593. +
  1594. +    gpointer user_data = nullptr;
  1595. +    gdk_window_get_user_data(gdkWin, &user_data);
  1596. +    if (!user_data || !GTK_IS_CONTAINER(user_data)) {
  1597. +        return NS_ERROR_FAILURE;
  1598. +    }
  1599. +
  1600. +    mTopLevel = gtk_widget_get_toplevel(GTK_WIDGET(user_data));
  1601. +    if (!mTopLevel) {
  1602. +        return NS_ERROR_FAILURE;
  1603. +    }
  1604. +
  1605. +    g_object_ref(mTopLevel);
  1606. +
  1607. +    nsRefPtr<nsNativeMenuDocListener> listener =
  1608. +        nsNativeMenuDocListener::Create(aMenuBarNode);
  1609. +    if (!listener) {
  1610. +        return NS_ERROR_FAILURE;
  1611. +    }
  1612. +
  1613. +    nsMenuObject::Init(listener, aMenuBarNode);
  1614. +
  1615. +    nsAutoCString path;
  1616. +    path.Append(NS_LITERAL_CSTRING("/com/canonical/menu/"));
  1617. +    char xid[10];
  1618. +    sprintf(xid, "%X", static_cast<uint32_t>(
  1619. +        GDK_WINDOW_XID(gtk_widget_get_window(mTopLevel))));
  1620. +    path.Append(xid);
  1621. +
  1622. +    mServer = dbusmenu_server_new(path.get());
  1623. +    if (!mServer) {
  1624. +        return NS_ERROR_FAILURE;
  1625. +    }
  1626. +
  1627. +    CreateNativeData();
  1628. +    if (!GetNativeData()) {
  1629. +        return NS_ERROR_FAILURE;
  1630. +    }
  1631. +
  1632. +    dbusmenu_server_set_root(mServer, GetNativeData());
  1633. +
  1634. +    mEventListener = new nsMenuBarDocEventListener(this);
  1635. +
  1636. +    mDocument = do_QueryInterface(ContentNode()->OwnerDoc());
  1637. +
  1638. +    mAccessKey = Preferences::GetInt("ui.key.menuAccessKey");
  1639. +    if (mAccessKey == nsIDOMKeyEvent::DOM_VK_SHIFT) {
  1640. +        mAccessKeyMask = eModifierShift;
  1641. +    } else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_CONTROL) {
  1642. +        mAccessKeyMask = eModifierCtrl;
  1643. +    } else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_ALT) {
  1644. +        mAccessKeyMask = eModifierAlt;
  1645. +    } else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_META) {
  1646. +        mAccessKeyMask = eModifierMeta;
  1647. +    } else {
  1648. +        mAccessKeyMask = eModifierAlt;
  1649. +    }
  1650. +
  1651. +    return NS_OK;
  1652. +}
  1653. +
  1654. +nsMenuBar::~nsMenuBar()
  1655. +{
  1656. +    nsNativeMenuService *service = nsNativeMenuService::GetSingleton();
  1657. +    if (service) {
  1658. +        service->NotifyNativeMenuBarDestroyed(this);
  1659. +    }
  1660. +
  1661. +    if (ContentNode()) {
  1662. +        SetShellShowingMenuBar(false);
  1663. +    }
  1664. +
  1665. +    // We want to destroy all children before dropping our reference
  1666. +    // to the doc listener
  1667. +    while (ChildCount() > 0) {
  1668. +        RemoveChildAt(0);
  1669. +    }
  1670. +
  1671. +    if (mTopLevel) {
  1672. +        g_object_unref(mTopLevel);
  1673. +    }
  1674. +
  1675. +    if (DocListener()) {
  1676. +        DocListener()->Stop();
  1677. +    }
  1678. +
  1679. +    if (mDocument) {
  1680. +        DisconnectDocumentEventListeners();
  1681. +    }
  1682. +
  1683. +    if (mServer) {
  1684. +        g_object_unref(mServer);
  1685. +    }
  1686. +
  1687. +    MOZ_COUNT_DTOR(nsMenuBar);
  1688. +}
  1689. +
  1690. +/* static */ nsMenuBar*
  1691. +nsMenuBar::Create(nsIWidget *aParent, nsIContent *aMenuBarNode)
  1692. +{
  1693. +    nsAutoPtr<nsMenuBar> menubar(new nsMenuBar());
  1694. +    if (NS_FAILED(menubar->Init(aParent, aMenuBarNode))) {
  1695. +        return nullptr;
  1696. +    }
  1697. +
  1698. +    return menubar.forget();
  1699. +}
  1700. +
  1701. +nsMenuObject::EType
  1702. +nsMenuBar::Type() const
  1703. +{
  1704. +    return nsMenuObject::eType_MenuBar;
  1705. +}
  1706. +
  1707. +bool
  1708. +nsMenuBar::IsBeingDisplayed() const
  1709. +{
  1710. +    return true;
  1711. +}
  1712. +
  1713. +uint32_t
  1714. +nsMenuBar::WindowId() const
  1715. +{
  1716. +    return static_cast<uint32_t>(GDK_WINDOW_XID(gtk_widget_get_window(mTopLevel)));
  1717. +}
  1718. +
  1719. +nsAdoptingCString
  1720. +nsMenuBar::ObjectPath() const
  1721. +{
  1722. +    gchar *tmp;
  1723. +    g_object_get(mServer, DBUSMENU_SERVER_PROP_DBUS_OBJECT, &tmp, NULL);
  1724. +    nsAdoptingCString result(tmp);
  1725. +
  1726. +    return result;
  1727. +}
  1728. +
  1729. +nsNativeMenuGIORequest&
  1730. +nsMenuBar::BeginRegisterRequest()
  1731. +{
  1732. +    mRegisterRequestCanceller.Start();
  1733. +    return mRegisterRequestCanceller;
  1734. +}
  1735. +
  1736. +void
  1737. +nsMenuBar::EndRegisterRequest()
  1738. +{
  1739. +    NS_ASSERTION(RegisterRequestInProgress(), "No request in progress");
  1740. +    mRegisterRequestCanceller.Finish();
  1741. +}
  1742. +
  1743. +bool
  1744. +nsMenuBar::RegisterRequestInProgress() const
  1745. +{
  1746. +    return mRegisterRequestCanceller.InProgress();
  1747. +}
  1748. +
  1749. +void
  1750. +nsMenuBar::Activate()
  1751. +{
  1752. +    if (mIsActive) {
  1753. +        return;
  1754. +    }
  1755. +
  1756. +    mIsActive = true;
  1757. +
  1758. +    mDocument->AddEventListener(NS_LITERAL_STRING("focus"),
  1759. +                                mEventListener,
  1760. +                                true);
  1761. +    mDocument->AddEventListener(NS_LITERAL_STRING("blur"),
  1762. +                                mEventListener,
  1763. +                                true);
  1764. +    mDocument->AddEventListener(NS_LITERAL_STRING("keypress"),
  1765. +                                mEventListener,
  1766. +                                false);
  1767. +    mDocument->AddEventListener(NS_LITERAL_STRING("keydown"),
  1768. +                                mEventListener,
  1769. +                                false);
  1770. +    mDocument->AddEventListener(NS_LITERAL_STRING("keyup"),
  1771. +                                mEventListener,
  1772. +                                false);
  1773. +
  1774. +    // Clear this. Not sure if we really need to though
  1775. +    ContentNode()->SetAttr(kNameSpaceID_None, nsNativeMenuAtoms::openedwithkey,
  1776. +                           NS_LITERAL_STRING("false"), true);
  1777. +
  1778. +    DocListener()->Start();
  1779. +    Build();
  1780. +    SetShellShowingMenuBar(true);
  1781. +}
  1782. +
  1783. +void
  1784. +nsMenuBar::Deactivate()
  1785. +{
  1786. +    if (!mIsActive) {
  1787. +        return;
  1788. +    }
  1789. +
  1790. +    mIsActive = false;
  1791. +
  1792. +    mRegisterRequestCanceller.Cancel();
  1793. +
  1794. +    SetShellShowingMenuBar(false);
  1795. +    while (ChildCount() > 0) {
  1796. +        RemoveChildAt(0);
  1797. +    }
  1798. +    DocListener()->Stop();
  1799. +    DisconnectDocumentEventListeners();
  1800. +}
  1801. +
  1802. +void
  1803. +nsMenuBar::OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute)
  1804. +{
  1805. +
  1806. +}
  1807. +
  1808. +void
  1809. +nsMenuBar::OnContentInserted(nsIContent *aContainer, nsIContent *aChild,
  1810. +                             nsIContent *aPrevSibling)
  1811. +{
  1812. +    NS_ASSERTION(aContainer == ContentNode(),
  1813. +                 "Received an event that wasn't meant for us");
  1814. +
  1815. +    nsresult rv;
  1816. +    nsMenuObject *child = CreateChild(aChild, &rv);
  1817. +
  1818. +    if (child) {
  1819. +        rv = InsertChildAfter(child, aPrevSibling);
  1820. +    }
  1821. +
  1822. +    NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to insert item in to menubar");
  1823. +}
  1824. +
  1825. +void
  1826. +nsMenuBar::OnContentRemoved(nsIContent *aContainer, nsIContent *aChild)
  1827. +{
  1828. +    NS_ASSERTION(aContainer == ContentNode(),
  1829. +                 "Received an event that wasn't meant for us");
  1830. +
  1831. +    DebugOnly<nsresult> rv = RemoveChild(aChild);
  1832. +    NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to remove item from menubar");
  1833. +}
  1834. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuBar.h
  1835. ===================================================================
  1836. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  1837. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuBar.h  2014-12-08 19:19:07.474645107 +0000
  1838. @@ -0,0 +1,112 @@
  1839. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  1840. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  1841. + */
  1842. +/* This Source Code Form is subject to the terms of the Mozilla Public
  1843. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  1844. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  1845. +
  1846. +#ifndef __nsMenuBar_h__
  1847. +#define __nsMenuBar_h__
  1848. +
  1849. +#include "mozilla/Attributes.h"
  1850. +#include "nsCOMPtr.h"
  1851. +#include "nsString.h"
  1852. +
  1853. +#include "nsDbusmenu.h"
  1854. +#include "nsMenuContainer.h"
  1855. +#include "nsMenuObject.h"
  1856. +#include "nsNativeMenuUtils.h"
  1857. +
  1858. +#include <gtk/gtk.h>
  1859. +
  1860. +class nsIAtom;
  1861. +class nsIContent;
  1862. +class nsIDOMEvent;
  1863. +class nsIDOMKeyEvent;
  1864. +class nsIWidget;
  1865. +class nsMenuBarDocEventListener;
  1866. +
  1867. +/*
  1868. + * The menubar class. There is one of these per window (and the window
  1869. + * owns its menubar). Each menubar has an object path, and the service is
  1870. + * responsible for telling the desktop shell which object path corresponds
  1871. + * to a particular window. A menubar and its hierarchy also own a
  1872. + * nsNativeMenuDocListener.
  1873. + */
  1874. +class nsMenuBar MOZ_FINAL : public nsMenuContainer
  1875. +{
  1876. +public:
  1877. +    ~nsMenuBar();
  1878. +
  1879. +    static nsMenuBar* Create(nsIWidget *aParent,
  1880. +                             nsIContent *aMenuBarNode);
  1881. +
  1882. +    nsMenuObject::EType Type() const;
  1883. +
  1884. +    bool IsBeingDisplayed() const;
  1885. +
  1886. +    // Get the native window ID for this menubar
  1887. +    uint32_t WindowId() const;
  1888. +
  1889. +    // Get the object path for this menubar
  1890. +    nsAdoptingCString ObjectPath() const;
  1891. +
  1892. +    // Initializes and returns a cancellable request object, used
  1893. +    // by the menuservice when registering this menubar
  1894. +    nsNativeMenuGIORequest& BeginRegisterRequest();
  1895. +
  1896. +    // Finishes the current request to register the menubar
  1897. +    void EndRegisterRequest();
  1898. +
  1899. +    bool RegisterRequestInProgress() const;
  1900. +
  1901. +    // Get the top-level GtkWindow handle
  1902. +    GtkWidget* TopLevelWindow() { return mTopLevel; }
  1903. +
  1904. +    // Called from the menuservice when the menubar is about to be registered.
  1905. +    // Causes the native menubar to be created, and the XUL menubar to be hidden
  1906. +    void Activate();
  1907. +
  1908. +    // Called from the menuservice when the menubar is no longer registered
  1909. +    // with the desktop shell. Will cause the XUL menubar to be shown again
  1910. +    void Deactivate();
  1911. +
  1912. +    void OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute);
  1913. +    void OnContentInserted(nsIContent *aContainer, nsIContent *aChild,
  1914. +                           nsIContent *aPrevSibling);
  1915. +    void OnContentRemoved(nsIContent *aContainer, nsIContent *aChild);
  1916. +
  1917. +private:
  1918. +    friend class nsMenuBarDocEventListener;
  1919. +
  1920. +    enum ModifierFlags {
  1921. +        eModifierShift = (1 << 0),
  1922. +        eModifierCtrl = (1 << 1),
  1923. +        eModifierAlt = (1 << 2),
  1924. +        eModifierMeta = (1 << 3)
  1925. +    };
  1926. +
  1927. +    nsMenuBar();
  1928. +    nsresult Init(nsIWidget *aParent, nsIContent *aMenuBarNode);
  1929. +    void Build();
  1930. +    void DisconnectDocumentEventListeners();
  1931. +    void SetShellShowingMenuBar(bool aShowing);
  1932. +    void Focus();
  1933. +    void Blur();
  1934. +    ModifierFlags GetModifiersFromEvent(nsIDOMKeyEvent *aEvent);
  1935. +    nsresult Keypress(nsIDOMEvent *aEvent);
  1936. +    nsresult KeyDown(nsIDOMEvent *aEvent);
  1937. +    nsresult KeyUp(nsIDOMEvent *aEvent);
  1938. +
  1939. +    GtkWidget *mTopLevel;
  1940. +    DbusmenuServer *mServer;
  1941. +    nsCOMPtr<nsIDOMEventTarget> mDocument;
  1942. +    nsNativeMenuGIORequest mRegisterRequestCanceller;
  1943. +    nsRefPtr<nsMenuBarDocEventListener> mEventListener;
  1944. +
  1945. +    uint32_t mAccessKey;
  1946. +    ModifierFlags mAccessKeyMask;
  1947. +    bool mIsActive;
  1948. +};
  1949. +
  1950. +#endif /* __nsMenuBar_h__ */
  1951. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuContainer.cpp
  1952. ===================================================================
  1953. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  1954. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuContainer.cpp  2014-12-08 19:19:07.474645107 +0000
  1955. @@ -0,0 +1,179 @@
  1956. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  1957. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  1958. + */
  1959. +/* This Source Code Form is subject to the terms of the Mozilla Public
  1960. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  1961. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  1962. +
  1963. +#include "nsGkAtoms.h"
  1964. +#include "nsIAtom.h"
  1965. +#include "nsIContent.h"
  1966. +
  1967. +#include "nsDbusmenu.h"
  1968. +#include "nsMenu.h"
  1969. +#include "nsMenuItem.h"
  1970. +#include "nsMenuSeparator.h"
  1971. +
  1972. +#include "nsMenuContainer.h"
  1973. +
  1974. +const nsMenuContainer::ChildTArray::index_type nsMenuContainer::NoIndex = nsMenuContainer::ChildTArray::NoIndex;
  1975. +
  1976. +typedef nsMenuObject* (*nsMenuObjectConstructor)(nsMenuContainer*,
  1977. +                                                 nsIContent*);
  1978. +static nsMenuObjectConstructor
  1979. +GetMenuObjectConstructor(nsIContent *aContent)
  1980. +{
  1981. +    if (!aContent->IsXUL()) {
  1982. +        return nullptr;
  1983. +    }
  1984. +
  1985. +    nsIAtom *tag = aContent->Tag();
  1986. +    if (tag == nsGkAtoms::menuitem) {
  1987. +        return nsMenuItem::Create;
  1988. +    } else if (tag == nsGkAtoms::menu) {
  1989. +        return nsMenu::Create;
  1990. +    } else if (tag == nsGkAtoms::menuseparator) {
  1991. +        return nsMenuSeparator::Create;
  1992. +    }
  1993. +
  1994. +    return nullptr;
  1995. +}
  1996. +
  1997. +static bool
  1998. +ContentIsSupported(nsIContent *aContent)
  1999. +{
  2000. +    return GetMenuObjectConstructor(aContent) ? true : false;
  2001. +}
  2002. +
  2003. +nsMenuObject*
  2004. +nsMenuContainer::CreateChild(nsIContent *aContent, nsresult *aRv)
  2005. +{
  2006. +    nsMenuObjectConstructor ctor = GetMenuObjectConstructor(aContent);
  2007. +    if (!ctor) {
  2008. +        // There are plenty of node types we might stumble across that
  2009. +        // aren't supported. This isn't an error though
  2010. +        if (aRv) {
  2011. +            *aRv = NS_OK;
  2012. +        }
  2013. +        return nullptr;
  2014. +    }
  2015. +
  2016. +    nsMenuObject *res = ctor(this, aContent);
  2017. +    if (!res) {
  2018. +        if (aRv) {
  2019. +            *aRv = NS_ERROR_FAILURE;
  2020. +        }
  2021. +        return nullptr;
  2022. +    }
  2023. +
  2024. +    if (aRv) {
  2025. +        *aRv = NS_OK;
  2026. +    }
  2027. +    return res;
  2028. +}
  2029. +
  2030. +size_t
  2031. +nsMenuContainer::IndexOf(nsIContent *aChild) const
  2032. +{
  2033. +    if (!aChild) {
  2034. +        return NoIndex;
  2035. +    }
  2036. +
  2037. +    size_t count = ChildCount();
  2038. +    for (size_t i = 0; i < count; ++i) {
  2039. +        if (ChildAt(i)->ContentNode() == aChild) {
  2040. +            return i;
  2041. +        }
  2042. +    }
  2043. +
  2044. +    return NoIndex;
  2045. +}
  2046. +
  2047. +nsresult
  2048. +nsMenuContainer::RemoveChildAt(size_t aIndex, bool aUpdateNative)
  2049. +{
  2050. +    if (aIndex >= ChildCount()) {
  2051. +        return NS_ERROR_INVALID_ARG;
  2052. +    }
  2053. +
  2054. +    if (aUpdateNative) {
  2055. +        if (!dbusmenu_menuitem_child_delete(GetNativeData(),
  2056. +                                            ChildAt(aIndex)->GetNativeData())) {
  2057. +            return NS_ERROR_FAILURE;
  2058. +        }
  2059. +    }
  2060. +
  2061. +    mChildren.RemoveElementAt(aIndex);
  2062. +
  2063. +    return NS_OK;
  2064. +}
  2065. +
  2066. +nsresult
  2067. +nsMenuContainer::RemoveChild(nsIContent *aChild, bool aUpdateNative)
  2068. +{
  2069. +    size_t index = IndexOf(aChild);
  2070. +    if (index == NoIndex) {
  2071. +        return NS_ERROR_INVALID_ARG;
  2072. +    }
  2073. +
  2074. +    return RemoveChildAt(index, aUpdateNative);
  2075. +}
  2076. +
  2077. +nsresult
  2078. +nsMenuContainer::InsertChildAfter(nsMenuObject *aChild,
  2079. +                                  nsIContent *aPrevSibling,
  2080. +                                  bool aUpdateNative)
  2081. +{
  2082. +    size_t index = IndexOf(aPrevSibling);
  2083. +    if (index == NoIndex && aPrevSibling) {
  2084. +        return NS_ERROR_INVALID_ARG;
  2085. +    }
  2086. +
  2087. +    ++index;
  2088. +
  2089. +    if (aUpdateNative) {
  2090. +        aChild->CreateNativeData();
  2091. +        if (!dbusmenu_menuitem_child_add_position(GetNativeData(),
  2092. +                                                  aChild->GetNativeData(),
  2093. +                                                  index)) {
  2094. +            return NS_ERROR_FAILURE;
  2095. +        }
  2096. +    }
  2097. +
  2098. +    return mChildren.InsertElementAt(index, aChild) ? NS_OK : NS_ERROR_FAILURE;
  2099. +}
  2100. +
  2101. +nsresult
  2102. +nsMenuContainer::AppendChild(nsMenuObject *aChild, bool aUpdateNative)
  2103. +{
  2104. +    if (aUpdateNative) {
  2105. +        aChild->CreateNativeData();
  2106. +        if (!dbusmenu_menuitem_child_append(GetNativeData(),
  2107. +                                            aChild->GetNativeData())) {
  2108. +            return NS_ERROR_FAILURE;
  2109. +        }
  2110. +    }
  2111. +
  2112. +    return mChildren.AppendElement(aChild) ? NS_OK : NS_ERROR_FAILURE;
  2113. +}
  2114. +
  2115. +nsMenuContainer::nsMenuContainer() :
  2116. +    nsMenuObject()
  2117. +{
  2118. +}
  2119. +
  2120. +bool
  2121. +nsMenuContainer::NeedsRebuild() const
  2122. +{
  2123. +    return false;
  2124. +}
  2125. +
  2126. +/* static */ nsIContent*
  2127. +nsMenuContainer::GetPreviousSupportedSibling(nsIContent *aContent)
  2128. +{
  2129. +    do {
  2130. +        aContent = aContent->GetPreviousSibling();
  2131. +    } while (aContent && !ContentIsSupported(aContent));
  2132. +
  2133. +    return aContent;
  2134. +}
  2135. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuContainer.h
  2136. ===================================================================
  2137. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  2138. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuContainer.h    2014-12-08 19:19:07.478645113 +0000
  2139. @@ -0,0 +1,66 @@
  2140. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2141. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  2142. + */
  2143. +/* This Source Code Form is subject to the terms of the Mozilla Public
  2144. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  2145. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  2146. +
  2147. +#ifndef __nsMenuContainer_h__
  2148. +#define __nsMenuContainer_h__
  2149. +
  2150. +#include "nsAutoPtr.h"
  2151. +#include "nsTArray.h"
  2152. +
  2153. +#include "nsMenuObject.h"
  2154. +
  2155. +class nsIContent;
  2156. +
  2157. +// Base class for containers (menus and menubars)
  2158. +class nsMenuContainer : public nsMenuObject
  2159. +{
  2160. +public:
  2161. +    typedef nsTArray<nsAutoPtr<nsMenuObject> > ChildTArray;
  2162. +
  2163. +    // Determine if this container is being displayed on screen. Must be
  2164. +    // implemented by subclasses. Must return true if the container is
  2165. +    // in the fully open state, or false otherwise
  2166. +    virtual bool IsBeingDisplayed() const = 0;
  2167. +
  2168. +    // Determine if this container will be rebuilt the next time it opens.
  2169. +    // Returns false by default but can be overridden by subclasses
  2170. +    virtual bool NeedsRebuild() const;
  2171. +
  2172. +    // Return the first previous sibling that is of a type supported by the
  2173. +    // menu system
  2174. +    static nsIContent* GetPreviousSupportedSibling(nsIContent *aContent);
  2175. +
  2176. +    static const ChildTArray::index_type NoIndex;
  2177. +
  2178. +protected:
  2179. +    nsMenuContainer();
  2180. +
  2181. +    // Create a new child element for the specified content node
  2182. +    nsMenuObject* CreateChild(nsIContent *aContent, nsresult *aRv);
  2183. +
  2184. +    // Return the index of the child for the specified content node
  2185. +    size_t IndexOf(nsIContent *aChild) const;
  2186. +
  2187. +    size_t ChildCount() const { return mChildren.Length(); }
  2188. +    nsMenuObject* ChildAt(size_t aIndex) const { return mChildren[aIndex]; }
  2189. +
  2190. +    nsresult RemoveChildAt(size_t aIndex, bool aUpdateNative = true);
  2191. +
  2192. +    // Remove the child that owns the specified content node
  2193. +    nsresult RemoveChild(nsIContent *aChild, bool aUpdateNative = true);
  2194. +
  2195. +    // Insert a new child after the child that owns the specified content node
  2196. +    nsresult InsertChildAfter(nsMenuObject *aChild, nsIContent *aPrevSibling,
  2197. +                              bool aUpdateNative = true);
  2198. +
  2199. +    nsresult AppendChild(nsMenuObject *aChild, bool aUpdateNative = true);
  2200. +
  2201. +private:
  2202. +    ChildTArray mChildren;
  2203. +};
  2204. +
  2205. +#endif /* __nsMenuContainer_h__ */
  2206. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuItem.cpp
  2207. ===================================================================
  2208. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  2209. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuItem.cpp   2014-12-08 19:19:07.478645113 +0000
  2210. @@ -0,0 +1,739 @@
  2211. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2212. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  2213. + */
  2214. +/* This Source Code Form is subject to the terms of the Mozilla Public
  2215. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  2216. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  2217. +
  2218. +#include "mozilla/ArrayUtils.h"
  2219. +#include "mozilla/dom/Element.h"
  2220. +#include "mozilla/Preferences.h"
  2221. +#include "mozilla/TextEvents.h"
  2222. +#include "nsAutoPtr.h"
  2223. +#include "nsContentUtils.h"
  2224. +#include "nsCRT.h"
  2225. +#include "nsGkAtoms.h"
  2226. +#include "nsGtkUtils.h"
  2227. +#include "nsIContent.h"
  2228. +#include "nsIDocument.h"
  2229. +#include "nsIDOMDocument.h"
  2230. +#include "nsIDOMEvent.h"
  2231. +#include "nsIDOMEventTarget.h"
  2232. +#include "nsIDOMKeyEvent.h"
  2233. +#include "nsIDOMXULCommandEvent.h"
  2234. +#include "nsIRunnable.h"
  2235. +#include "nsReadableUtils.h"
  2236. +#include "nsString.h"
  2237. +#include "nsStyleContext.h"
  2238. +#include "nsThreadUtils.h"
  2239. +
  2240. +#include "nsMenu.h"
  2241. +#include "nsMenuBar.h"
  2242. +#include "nsMenuContainer.h"
  2243. +#include "nsNativeMenuDocListener.h"
  2244. +
  2245. +#include <gdk/gdk.h>
  2246. +#include <gdk/gdkkeysyms.h>
  2247. +#include <gdk/gdkx.h>
  2248. +#include <gtk/gtk.h>
  2249. +
  2250. +#include "nsMenuItem.h"
  2251. +
  2252. +using namespace mozilla;
  2253. +using namespace mozilla::widget;
  2254. +
  2255. +struct KeyCodeData {
  2256. +    const char* str;
  2257. +    size_t strlength;
  2258. +    uint32_t keycode;
  2259. +};
  2260. +
  2261. +static struct KeyCodeData gKeyCodes[] = {
  2262. +#define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \
  2263. +  { #aDOMKeyName, sizeof(#aDOMKeyName) - 1, aDOMKeyCode }
  2264. +#include "mozilla/VirtualKeyCodeList.h"
  2265. +#undef NS_DEFINE_VK
  2266. +};
  2267. +
  2268. +struct KeyPair {
  2269. +    uint32_t DOMKeyCode;
  2270. +    guint GDKKeyval;
  2271. +};
  2272. +
  2273. +//
  2274. +// Netscape keycodes are defined in widget/public/nsGUIEvent.h
  2275. +// GTK keycodes are defined in <gdk/gdkkeysyms.h>
  2276. +//
  2277. +static const KeyPair gKeyPairs[] = {
  2278. +    { NS_VK_CANCEL,     GDK_Cancel },
  2279. +    { NS_VK_BACK,       GDK_BackSpace },
  2280. +    { NS_VK_TAB,        GDK_Tab },
  2281. +    { NS_VK_TAB,        GDK_ISO_Left_Tab },
  2282. +    { NS_VK_CLEAR,      GDK_Clear },
  2283. +    { NS_VK_RETURN,     GDK_Return },
  2284. +    { NS_VK_SHIFT,      GDK_Shift_L },
  2285. +    { NS_VK_SHIFT,      GDK_Shift_R },
  2286. +    { NS_VK_SHIFT,      GDK_Shift_Lock },
  2287. +    { NS_VK_CONTROL,    GDK_Control_L },
  2288. +    { NS_VK_CONTROL,    GDK_Control_R },
  2289. +    { NS_VK_ALT,        GDK_Alt_L },
  2290. +    { NS_VK_ALT,        GDK_Alt_R },
  2291. +    { NS_VK_META,       GDK_Meta_L },
  2292. +    { NS_VK_META,       GDK_Meta_R },
  2293. +
  2294. +    // Assume that Super or Hyper is always mapped to physical Win key.
  2295. +    { NS_VK_WIN,        GDK_Super_L },
  2296. +    { NS_VK_WIN,        GDK_Super_R },
  2297. +    { NS_VK_WIN,        GDK_Hyper_L },
  2298. +    { NS_VK_WIN,        GDK_Hyper_R },
  2299. +
  2300. +    // GTK's AltGraph key is similar to Mac's Option (Alt) key.  However,
  2301. +    // unfortunately, browsers on Mac are using NS_VK_ALT for it even though
  2302. +    // it's really different from Alt key on Windows.
  2303. +    // On the other hand, GTK's AltGrapsh keys are really different from
  2304. +    // Alt key.  However, there is no AltGrapsh key on Windows.  On Windows,
  2305. +    // both Ctrl and Alt keys are pressed internally when AltGr key is pressed.
  2306. +    // For some languages' users, AltGraph key is important, so, web
  2307. +    // applications on such locale may want to know AltGraph key press.
  2308. +    // Therefore, we should map AltGr keycode for them only on GTK.
  2309. +    { NS_VK_ALTGR,      GDK_ISO_Level3_Shift },
  2310. +    { NS_VK_ALTGR,      GDK_ISO_Level5_Shift },
  2311. +    // We assume that Mode_switch is always used for level3 shift.
  2312. +    { NS_VK_ALTGR,      GDK_Mode_switch },
  2313. +
  2314. +    { NS_VK_PAUSE,      GDK_Pause },
  2315. +    { NS_VK_CAPS_LOCK,  GDK_Caps_Lock },
  2316. +    { NS_VK_KANA,       GDK_Kana_Lock },
  2317. +    { NS_VK_KANA,       GDK_Kana_Shift },
  2318. +    { NS_VK_HANGUL,     GDK_Hangul },
  2319. +    // { NS_VK_JUNJA,      GDK_XXX },
  2320. +    // { NS_VK_FINAL,      GDK_XXX },
  2321. +    { NS_VK_HANJA,      GDK_Hangul_Hanja },
  2322. +    { NS_VK_KANJI,      GDK_Kanji },
  2323. +    { NS_VK_ESCAPE,     GDK_Escape },
  2324. +    { NS_VK_CONVERT,    GDK_Henkan },
  2325. +    { NS_VK_NONCONVERT, GDK_Muhenkan },
  2326. +    // { NS_VK_ACCEPT,     GDK_XXX },
  2327. +    // { NS_VK_MODECHANGE, GDK_XXX },
  2328. +    { NS_VK_SPACE,      GDK_space },
  2329. +    { NS_VK_PAGE_UP,    GDK_Page_Up },
  2330. +    { NS_VK_PAGE_DOWN,  GDK_Page_Down },
  2331. +    { NS_VK_END,        GDK_End },
  2332. +    { NS_VK_HOME,       GDK_Home },
  2333. +    { NS_VK_LEFT,       GDK_Left },
  2334. +    { NS_VK_UP,         GDK_Up },
  2335. +    { NS_VK_RIGHT,      GDK_Right },
  2336. +    { NS_VK_DOWN,       GDK_Down },
  2337. +    { NS_VK_SELECT,     GDK_Select },
  2338. +    { NS_VK_PRINT,      GDK_Print },
  2339. +    { NS_VK_EXECUTE,    GDK_Execute },
  2340. +    { NS_VK_PRINTSCREEN, GDK_Print },
  2341. +    { NS_VK_INSERT,     GDK_Insert },
  2342. +    { NS_VK_DELETE,     GDK_Delete },
  2343. +    { NS_VK_HELP,       GDK_Help },
  2344. +
  2345. +    // keypad keys
  2346. +    { NS_VK_LEFT,       GDK_KP_Left },
  2347. +    { NS_VK_RIGHT,      GDK_KP_Right },
  2348. +    { NS_VK_UP,         GDK_KP_Up },
  2349. +    { NS_VK_DOWN,       GDK_KP_Down },
  2350. +    { NS_VK_PAGE_UP,    GDK_KP_Page_Up },
  2351. +    // Not sure what these are
  2352. +    //{ NS_VK_,       GDK_KP_Prior },
  2353. +    //{ NS_VK_,        GDK_KP_Next },
  2354. +    { NS_VK_CLEAR,      GDK_KP_Begin }, // Num-unlocked 5
  2355. +    { NS_VK_PAGE_DOWN,  GDK_KP_Page_Down },
  2356. +    { NS_VK_HOME,       GDK_KP_Home },
  2357. +    { NS_VK_END,        GDK_KP_End },
  2358. +    { NS_VK_INSERT,     GDK_KP_Insert },
  2359. +    { NS_VK_DELETE,     GDK_KP_Delete },
  2360. +    { NS_VK_RETURN,     GDK_KP_Enter },
  2361. +
  2362. +    { NS_VK_NUM_LOCK,   GDK_Num_Lock },
  2363. +    { NS_VK_SCROLL_LOCK,GDK_Scroll_Lock },
  2364. +
  2365. +    // Function keys
  2366. +    { NS_VK_F1,         GDK_F1 },
  2367. +    { NS_VK_F2,         GDK_F2 },
  2368. +    { NS_VK_F3,         GDK_F3 },
  2369. +    { NS_VK_F4,         GDK_F4 },
  2370. +    { NS_VK_F5,         GDK_F5 },
  2371. +    { NS_VK_F6,         GDK_F6 },
  2372. +    { NS_VK_F7,         GDK_F7 },
  2373. +    { NS_VK_F8,         GDK_F8 },
  2374. +    { NS_VK_F9,         GDK_F9 },
  2375. +    { NS_VK_F10,        GDK_F10 },
  2376. +    { NS_VK_F11,        GDK_F11 },
  2377. +    { NS_VK_F12,        GDK_F12 },
  2378. +    { NS_VK_F13,        GDK_F13 },
  2379. +    { NS_VK_F14,        GDK_F14 },
  2380. +    { NS_VK_F15,        GDK_F15 },
  2381. +    { NS_VK_F16,        GDK_F16 },
  2382. +    { NS_VK_F17,        GDK_F17 },
  2383. +    { NS_VK_F18,        GDK_F18 },
  2384. +    { NS_VK_F19,        GDK_F19 },
  2385. +    { NS_VK_F20,        GDK_F20 },
  2386. +    { NS_VK_F21,        GDK_F21 },
  2387. +    { NS_VK_F22,        GDK_F22 },
  2388. +    { NS_VK_F23,        GDK_F23 },
  2389. +    { NS_VK_F24,        GDK_F24 },
  2390. +
  2391. +    // context menu key, keysym 0xff67, typically keycode 117 on 105-key (Microsoft)
  2392. +    // x86 keyboards, located between right 'Windows' key and right Ctrl key
  2393. +    { NS_VK_CONTEXT_MENU, GDK_Menu },
  2394. +    { NS_VK_SLEEP,      GDK_Sleep },
  2395. +
  2396. +    { NS_VK_ATTN,       GDK_3270_Attn },
  2397. +    { NS_VK_CRSEL,      GDK_3270_CursorSelect },
  2398. +    { NS_VK_EXSEL,      GDK_3270_ExSelect },
  2399. +    { NS_VK_EREOF,      GDK_3270_EraseEOF },
  2400. +    { NS_VK_PLAY,       GDK_3270_Play },
  2401. +    //{ NS_VK_ZOOM,       GDK_XXX },
  2402. +    { NS_VK_PA1,        GDK_3270_PA1 },
  2403. +};
  2404. +
  2405. +static guint
  2406. +ConvertGeckoKeyNameToGDKKeyval(nsAString& aKeyName)
  2407. +{
  2408. +    NS_ConvertUTF16toUTF8 keyName(aKeyName);
  2409. +    ToUpperCase(keyName); // We want case-insensitive comparison with data
  2410. +                          // stored as uppercase.
  2411. +
  2412. +    uint32_t keyCode = 0;
  2413. +
  2414. +    uint32_t keyNameLength = keyName.Length();
  2415. +    const char* keyNameStr = keyName.get();
  2416. +    for (uint16_t i = 0; i < ArrayLength(gKeyCodes); ++i) {
  2417. +        if (keyNameLength == gKeyCodes[i].strlength &&
  2418. +            !nsCRT::strcmp(gKeyCodes[i].str, keyNameStr)) {
  2419. +            keyCode = gKeyCodes[i].keycode;
  2420. +            break;
  2421. +        }
  2422. +    }
  2423. +
  2424. +    // First, try to handle alphanumeric input, not listed in nsKeycodes:
  2425. +    // most likely, more letters will be getting typed in than things in
  2426. +    // the key list, so we will look through these first.
  2427. +
  2428. +    if (keyCode >= NS_VK_A && keyCode <= NS_VK_Z) {
  2429. +        // gdk and DOM both use the ASCII codes for these keys.
  2430. +        return keyCode;
  2431. +    }
  2432. +
  2433. +    // numbers
  2434. +    if (keyCode >= NS_VK_0 && keyCode <= NS_VK_9) {
  2435. +        // gdk and DOM both use the ASCII codes for these keys.
  2436. +        return keyCode - NS_VK_0 + GDK_0;
  2437. +    }
  2438. +
  2439. +    switch (keyCode) {
  2440. +        // keys in numpad
  2441. +        case NS_VK_MULTIPLY:  return GDK_KP_Multiply;
  2442. +        case NS_VK_ADD:       return GDK_KP_Add;
  2443. +        case NS_VK_SEPARATOR: return GDK_KP_Separator;
  2444. +        case NS_VK_SUBTRACT:  return GDK_KP_Subtract;
  2445. +        case NS_VK_DECIMAL:   return GDK_KP_Decimal;
  2446. +        case NS_VK_DIVIDE:    return GDK_KP_Divide;
  2447. +        case NS_VK_NUMPAD0:   return GDK_KP_0;
  2448. +        case NS_VK_NUMPAD1:   return GDK_KP_1;
  2449. +        case NS_VK_NUMPAD2:   return GDK_KP_2;
  2450. +        case NS_VK_NUMPAD3:   return GDK_KP_3;
  2451. +        case NS_VK_NUMPAD4:   return GDK_KP_4;
  2452. +        case NS_VK_NUMPAD5:   return GDK_KP_5;
  2453. +        case NS_VK_NUMPAD6:   return GDK_KP_6;
  2454. +        case NS_VK_NUMPAD7:   return GDK_KP_7;
  2455. +        case NS_VK_NUMPAD8:   return GDK_KP_8;
  2456. +        case NS_VK_NUMPAD9:   return GDK_KP_9;
  2457. +        // other prinable keys
  2458. +        case NS_VK_SPACE:               return GDK_space;
  2459. +        case NS_VK_COLON:               return GDK_colon;
  2460. +        case NS_VK_SEMICOLON:           return GDK_semicolon;
  2461. +        case NS_VK_LESS_THAN:           return GDK_less;
  2462. +        case NS_VK_EQUALS:              return GDK_equal;
  2463. +        case NS_VK_GREATER_THAN:        return GDK_greater;
  2464. +        case NS_VK_QUESTION_MARK:       return GDK_question;
  2465. +        case NS_VK_AT:                  return GDK_at;
  2466. +        case NS_VK_CIRCUMFLEX:          return GDK_asciicircum;
  2467. +        case NS_VK_EXCLAMATION:         return GDK_exclam;
  2468. +        case NS_VK_DOUBLE_QUOTE:        return GDK_quotedbl;
  2469. +        case NS_VK_HASH:                return GDK_numbersign;
  2470. +        case NS_VK_DOLLAR:              return GDK_dollar;
  2471. +        case NS_VK_PERCENT:             return GDK_percent;
  2472. +        case NS_VK_AMPERSAND:           return GDK_ampersand;
  2473. +        case NS_VK_UNDERSCORE:          return GDK_underscore;
  2474. +        case NS_VK_OPEN_PAREN:          return GDK_parenleft;
  2475. +        case NS_VK_CLOSE_PAREN:         return GDK_parenright;
  2476. +        case NS_VK_ASTERISK:            return GDK_asterisk;
  2477. +        case NS_VK_PLUS:                return GDK_plus;
  2478. +        case NS_VK_PIPE:                return GDK_bar;
  2479. +        case NS_VK_HYPHEN_MINUS:        return GDK_minus;
  2480. +        case NS_VK_OPEN_CURLY_BRACKET:  return GDK_braceleft;
  2481. +        case NS_VK_CLOSE_CURLY_BRACKET: return GDK_braceright;
  2482. +        case NS_VK_TILDE:               return GDK_asciitilde;
  2483. +        case NS_VK_COMMA:               return GDK_comma;
  2484. +        case NS_VK_PERIOD:              return GDK_period;
  2485. +        case NS_VK_SLASH:               return GDK_slash;
  2486. +        case NS_VK_BACK_QUOTE:          return GDK_grave;
  2487. +        case NS_VK_OPEN_BRACKET:        return GDK_bracketleft;
  2488. +        case NS_VK_BACK_SLASH:          return GDK_backslash;
  2489. +        case NS_VK_CLOSE_BRACKET:       return GDK_bracketright;
  2490. +        case NS_VK_QUOTE:               return GDK_apostrophe;
  2491. +    }
  2492. +
  2493. +    // misc other things
  2494. +    for (uint32_t i = 0; i < ArrayLength(gKeyPairs); ++i) {
  2495. +        if (gKeyPairs[i].DOMKeyCode == keyCode) {
  2496. +            return gKeyPairs[i].GDKKeyval;
  2497. +        }
  2498. +    }
  2499. +
  2500. +    return 0;
  2501. +}
  2502. +
  2503. +class nsMenuItemUncheckSiblingsRunnable MOZ_FINAL : public nsRunnable
  2504. +{
  2505. +public:
  2506. +    NS_IMETHODIMP Run()
  2507. +    {
  2508. +        if (mMenuItem) {
  2509. +            mMenuItem->UncheckSiblings();
  2510. +        }
  2511. +        return NS_OK;
  2512. +    }
  2513. +
  2514. +    nsMenuItemUncheckSiblingsRunnable(nsMenuItem *aMenuItem) :
  2515. +        mMenuItem(aMenuItem) { };
  2516. +
  2517. +private:
  2518. +    nsWeakMenuObject<nsMenuItem> mMenuItem;
  2519. +};
  2520. +
  2521. +bool
  2522. +nsMenuItem::IsCheckboxOrRadioItem() const
  2523. +{
  2524. +    return MenuItemType() == eMenuItemType_Radio ||
  2525. +           MenuItemType() == eMenuItemType_CheckBox;
  2526. +}
  2527. +
  2528. +/* static */ void
  2529. +nsMenuItem::item_activated_cb(DbusmenuMenuitem *menuitem,
  2530. +                              guint timestamp,
  2531. +                              gpointer user_data)
  2532. +{
  2533. +    nsMenuItem *item = static_cast<nsMenuItem *>(user_data);
  2534. +    item->Activate(timestamp);
  2535. +}
  2536. +
  2537. +void
  2538. +nsMenuItem::Activate(uint32_t aTimestamp)
  2539. +{
  2540. +    GdkWindow *window = gtk_widget_get_window(MenuBar()->TopLevelWindow());
  2541. +    gdk_x11_window_set_user_time(
  2542. +        window, std::min(aTimestamp, gdk_x11_get_server_time(window)));
  2543. +
  2544. +    // We do this to avoid mutating our view of the menu until
  2545. +    // after we have finished
  2546. +    nsNativeMenuAutoUpdateBatch batch;
  2547. +
  2548. +    if (!ContentNode()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocheck,
  2549. +                                    nsGkAtoms::_false, eCaseMatters) &&
  2550. +        (MenuItemType() == eMenuItemType_CheckBox ||
  2551. +         (MenuItemType() == eMenuItemType_Radio && !IsChecked()))) {
  2552. +        ContentNode()->SetAttr(kNameSpaceID_None, nsGkAtoms::checked,
  2553. +                               IsChecked() ?
  2554. +                               NS_LITERAL_STRING("false") :  NS_LITERAL_STRING("true"),
  2555. +                               true);
  2556. +    }
  2557. +
  2558. +    nsIDocument *doc = ContentNode()->OwnerDoc();
  2559. +    nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(ContentNode());
  2560. +    nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
  2561. +    if (domDoc && target) {
  2562. +        nsCOMPtr<nsIDOMEvent> event;
  2563. +        domDoc->CreateEvent(NS_LITERAL_STRING("xulcommandevent"),
  2564. +                            getter_AddRefs(event));
  2565. +        nsCOMPtr<nsIDOMXULCommandEvent> command = do_QueryInterface(event);
  2566. +        if (command) {
  2567. +            command->InitCommandEvent(NS_LITERAL_STRING("command"),
  2568. +                                      true, true, doc->GetWindow(), 0,
  2569. +                                      false, false, false, false, nullptr);
  2570. +
  2571. +            event->SetTrusted(true);
  2572. +            bool dummy;
  2573. +            target->DispatchEvent(event, &dummy);
  2574. +        }
  2575. +    }
  2576. +
  2577. +    // This kinda sucks, but Unity doesn't send a closed event
  2578. +    // after activating a menuitem
  2579. +    nsMenuObject *ancestor = Parent();
  2580. +    while (ancestor && ancestor->Type() == eType_Menu) {
  2581. +        static_cast<nsMenu *>(ancestor)->OnClose();
  2582. +        ancestor = ancestor->Parent();
  2583. +    }
  2584. +}
  2585. +
  2586. +void
  2587. +nsMenuItem::CopyAttrFromNodeIfExists(nsIContent *aContent, nsIAtom *aAttribute)
  2588. +{
  2589. +    nsAutoString value;
  2590. +    if (aContent->GetAttr(kNameSpaceID_None, aAttribute, value)) {
  2591. +        ContentNode()->SetAttr(kNameSpaceID_None, aAttribute, value, true);
  2592. +    }
  2593. +}
  2594. +
  2595. +void
  2596. +nsMenuItem::UpdateState()
  2597. +{
  2598. +    if (!IsCheckboxOrRadioItem()) {
  2599. +        return;
  2600. +    }
  2601. +
  2602. +    SetCheckState(ContentNode()->AttrValueIs(kNameSpaceID_None,
  2603. +                                             nsGkAtoms::checked,
  2604. +                                             nsGkAtoms::_true,
  2605. +                                             eCaseMatters));
  2606. +    dbusmenu_menuitem_property_set_int(GetNativeData(),
  2607. +                                       DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
  2608. +                                       IsChecked() ?
  2609. +                                         DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED :
  2610. +                                         DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED);
  2611. +}
  2612. +
  2613. +void
  2614. +nsMenuItem::UpdateTypeAndState()
  2615. +{
  2616. +    static nsIContent::AttrValuesArray attrs[] =
  2617. +        { &nsGkAtoms::checkbox, &nsGkAtoms::radio, nullptr };
  2618. +    int32_t type = ContentNode()->FindAttrValueIn(kNameSpaceID_None,
  2619. +                                                  nsGkAtoms::type,
  2620. +                                                  attrs, eCaseMatters);
  2621. +
  2622. +    if (type >= 0 && type < 2) {
  2623. +        if (type == 0) {
  2624. +            dbusmenu_menuitem_property_set(GetNativeData(),
  2625. +                                           DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE,
  2626. +                                           DBUSMENU_MENUITEM_TOGGLE_CHECK);
  2627. +            SetMenuItemType(eMenuItemType_CheckBox);
  2628. +        } else if (type == 1) {
  2629. +            dbusmenu_menuitem_property_set(GetNativeData(),
  2630. +                                           DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE,
  2631. +                                           DBUSMENU_MENUITEM_TOGGLE_RADIO);
  2632. +            SetMenuItemType(eMenuItemType_Radio);
  2633. +        }
  2634. +
  2635. +        UpdateState();
  2636. +    } else {
  2637. +        dbusmenu_menuitem_property_remove(GetNativeData(),
  2638. +                                          DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE);
  2639. +        dbusmenu_menuitem_property_remove(GetNativeData(),
  2640. +                                          DBUSMENU_MENUITEM_PROP_TOGGLE_STATE);
  2641. +        SetMenuItemType(eMenuItemType_Normal);
  2642. +    }
  2643. +}
  2644. +
  2645. +void
  2646. +nsMenuItem::UpdateAccel()
  2647. +{
  2648. +    nsIDocument *doc = ContentNode()->GetCurrentDoc();
  2649. +    if (doc) {
  2650. +        nsCOMPtr<nsIContent> oldKeyContent;
  2651. +        oldKeyContent.swap(mKeyContent);
  2652. +
  2653. +        nsAutoString key;
  2654. +        ContentNode()->GetAttr(kNameSpaceID_None, nsGkAtoms::key, key);
  2655. +        if (!key.IsEmpty()) {
  2656. +            mKeyContent = doc->GetElementById(key);
  2657. +        }
  2658. +
  2659. +        if (mKeyContent != oldKeyContent) {
  2660. +            if (oldKeyContent) {
  2661. +                DocListener()->UnregisterForContentChanges(oldKeyContent);
  2662. +            }
  2663. +            if (mKeyContent) {
  2664. +                DocListener()->RegisterForContentChanges(mKeyContent, this);
  2665. +            }
  2666. +        }
  2667. +    }
  2668. +
  2669. +    if (!mKeyContent) {
  2670. +        dbusmenu_menuitem_property_remove(GetNativeData(),
  2671. +                                          DBUSMENU_MENUITEM_PROP_SHORTCUT);
  2672. +        return;
  2673. +    }
  2674. +
  2675. +    nsAutoString modifiers;
  2676. +    mKeyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiers);
  2677. +
  2678. +    uint32_t modifier = 0;
  2679. +
  2680. +    if (!modifiers.IsEmpty()) {
  2681. +        char* str = ToNewUTF8String(modifiers);
  2682. +        char *token = strtok(str, ", \t");
  2683. +        while(token) {
  2684. +            if (nsCRT::strcmp(token, "shift") == 0) {
  2685. +                modifier |= GDK_SHIFT_MASK;
  2686. +            } else if (nsCRT::strcmp(token, "alt") == 0) {
  2687. +                modifier |= GDK_MOD1_MASK;
  2688. +            } else if (nsCRT::strcmp(token, "meta") == 0) {
  2689. +                modifier |= GDK_META_MASK;
  2690. +            } else if (nsCRT::strcmp(token, "control") == 0) {
  2691. +                modifier |= GDK_CONTROL_MASK;
  2692. +            } else if (nsCRT::strcmp(token, "accel") == 0) {
  2693. +                int32_t accel = Preferences::GetInt("ui.key.accelKey");
  2694. +                if (accel == nsIDOMKeyEvent::DOM_VK_META) {
  2695. +                    modifier |= GDK_META_MASK;
  2696. +                } else if (accel == nsIDOMKeyEvent::DOM_VK_ALT) {
  2697. +                    modifier |= GDK_MOD1_MASK;
  2698. +                } else {
  2699. +                    modifier |= GDK_CONTROL_MASK;
  2700. +                }
  2701. +            }
  2702. +
  2703. +            token = strtok(nullptr, ", \t");
  2704. +        }
  2705. +
  2706. +        nsMemory::Free(str);
  2707. +    }
  2708. +
  2709. +    nsAutoString keyStr;
  2710. +    mKeyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyStr);
  2711. +
  2712. +    guint key = 0;
  2713. +    if (!keyStr.IsEmpty()) {
  2714. +        key = gdk_unicode_to_keyval(*keyStr.BeginReading());
  2715. +    }
  2716. +
  2717. +    if (key == 0) {
  2718. +        mKeyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, keyStr);
  2719. +        if (!keyStr.IsEmpty()) {
  2720. +            key = ConvertGeckoKeyNameToGDKKeyval(keyStr);
  2721. +        }
  2722. +    }
  2723. +
  2724. +    if (key == 0) {
  2725. +        key = GDK_VoidSymbol;
  2726. +    }
  2727. +
  2728. +    if (key != GDK_VoidSymbol) {
  2729. +        dbusmenu_menuitem_property_set_shortcut(GetNativeData(), key,
  2730. +                                                static_cast<GdkModifierType>(modifier));
  2731. +    } else {
  2732. +        dbusmenu_menuitem_property_remove(GetNativeData(),
  2733. +                                          DBUSMENU_MENUITEM_PROP_SHORTCUT);
  2734. +    }
  2735. +}
  2736. +
  2737. +void
  2738. +nsMenuItem::InitializeNativeData()
  2739. +{
  2740. +    g_signal_connect(G_OBJECT(GetNativeData()),
  2741. +                     DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
  2742. +                     G_CALLBACK(item_activated_cb), this);
  2743. +
  2744. +    UpdateTypeAndState();
  2745. +    UpdateAccel();
  2746. +    UpdateLabel();
  2747. +    UpdateSensitivity();
  2748. +}
  2749. +
  2750. +void
  2751. +nsMenuItem::UpdateContentAttributes()
  2752. +{
  2753. +    nsIDocument *doc = ContentNode()->GetCurrentDoc();
  2754. +    if (!doc) {
  2755. +        return;
  2756. +    }
  2757. +
  2758. +    nsAutoString command;
  2759. +    ContentNode()->GetAttr(kNameSpaceID_None, nsGkAtoms::command, command);
  2760. +    if (command.IsEmpty()) {
  2761. +        return;
  2762. +    }
  2763. +
  2764. +    nsCOMPtr<nsIContent> commandContent = doc->GetElementById(command);
  2765. +    if (!commandContent) {
  2766. +        return;
  2767. +    }
  2768. +
  2769. +    if (commandContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
  2770. +                                    nsGkAtoms::_true, eCaseMatters)) {
  2771. +        ContentNode()->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled,
  2772. +                               NS_LITERAL_STRING("true"), true);
  2773. +    } else {
  2774. +        ContentNode()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
  2775. +    }
  2776. +
  2777. +    CopyAttrFromNodeIfExists(commandContent, nsGkAtoms::checked);
  2778. +    CopyAttrFromNodeIfExists(commandContent, nsGkAtoms::accesskey);
  2779. +    CopyAttrFromNodeIfExists(commandContent, nsGkAtoms::label);
  2780. +    CopyAttrFromNodeIfExists(commandContent, nsGkAtoms::hidden);
  2781. +}
  2782. +
  2783. +void
  2784. +nsMenuItem::Update(nsStyleContext *aStyleContext)
  2785. +{
  2786. +    UpdateVisibility(aStyleContext);
  2787. +    UpdateIcon(aStyleContext);
  2788. +}
  2789. +
  2790. +void
  2791. +nsMenuItem::UncheckSiblings()
  2792. +{
  2793. +    if (!ContentNode()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
  2794. +                                    nsGkAtoms::radio, eCaseMatters)) {
  2795. +        // If we're not a radio button, we don't care
  2796. +        return;
  2797. +    }
  2798. +
  2799. +    nsAutoString name;
  2800. +    ContentNode()->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
  2801. +
  2802. +    nsIContent *parent = ContentNode()->GetParent();
  2803. +    if (!parent) {
  2804. +        return;
  2805. +    }
  2806. +
  2807. +    uint32_t count = parent->GetChildCount();
  2808. +    for (uint32_t i = 0; i < count; ++i) {
  2809. +        nsIContent *sibling = parent->GetChildAt(i);
  2810. +
  2811. +        nsAutoString otherName;
  2812. +        sibling->GetAttr(kNameSpaceID_None, nsGkAtoms::name, otherName);
  2813. +
  2814. +        if (sibling != ContentNode() && otherName == name &&
  2815. +            sibling->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
  2816. +                                 nsGkAtoms::radio, eCaseMatters)) {
  2817. +            sibling->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked, true);
  2818. +        }
  2819. +    }
  2820. +}
  2821. +
  2822. +bool
  2823. +nsMenuItem::IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const
  2824. +{
  2825. +    return nsCRT::strcmp(dbusmenu_menuitem_property_get(aNativeData,
  2826. +                                                        DBUSMENU_MENUITEM_PROP_TYPE),
  2827. +                         "separator") != 0;
  2828. +}
  2829. +
  2830. +nsMenuBar*
  2831. +nsMenuItem::MenuBar()
  2832. +{
  2833. +    nsMenuObject *tmp = this;
  2834. +    while (tmp->Parent()) {
  2835. +        tmp = tmp->Parent();
  2836. +    }
  2837. +
  2838. +    MOZ_ASSERT(tmp->Type() == eType_MenuBar, "The top-level should be a menubar");
  2839. +
  2840. +    return static_cast<nsMenuBar *>(tmp);
  2841. +}
  2842. +
  2843. +nsMenuObject::PropertyFlags
  2844. +nsMenuItem::SupportedProperties() const
  2845. +{
  2846. +    return static_cast<nsMenuObject::PropertyFlags>(
  2847. +        nsMenuObject::ePropLabel |
  2848. +        nsMenuObject::ePropEnabled |
  2849. +        nsMenuObject::ePropVisible |
  2850. +        nsMenuObject::ePropIconData |
  2851. +        nsMenuObject::ePropShortcut |
  2852. +        nsMenuObject::ePropToggleType |
  2853. +        nsMenuObject::ePropToggleState
  2854. +    );
  2855. +}
  2856. +
  2857. +nsMenuItem::nsMenuItem() :
  2858. +    nsMenuObject()
  2859. +{
  2860. +    MOZ_COUNT_CTOR(nsMenuItem);
  2861. +}
  2862. +
  2863. +nsMenuItem::~nsMenuItem()
  2864. +{
  2865. +    if (DocListener() && mKeyContent) {
  2866. +        DocListener()->UnregisterForContentChanges(mKeyContent);
  2867. +    }
  2868. +
  2869. +    if (GetNativeData()) {
  2870. +        g_signal_handlers_disconnect_by_func(GetNativeData(),
  2871. +                                             FuncToGpointer(item_activated_cb),
  2872. +                                             this);
  2873. +    }
  2874. +
  2875. +    MOZ_COUNT_DTOR(nsMenuItem);
  2876. +}
  2877. +
  2878. +nsMenuObject::EType
  2879. +nsMenuItem::Type() const
  2880. +{
  2881. +    return nsMenuObject::eType_MenuItem;
  2882. +}
  2883. +
  2884. +/* static */ nsMenuObject*
  2885. +nsMenuItem::Create(nsMenuContainer *aParent, nsIContent *aContent)
  2886. +{
  2887. +    nsAutoPtr<nsMenuItem> menuitem(new nsMenuItem());
  2888. +    if (NS_FAILED(menuitem->Init(aParent, aContent))) {
  2889. +        return nullptr;
  2890. +    }
  2891. +
  2892. +    return menuitem.forget();
  2893. +}
  2894. +
  2895. +void
  2896. +nsMenuItem::OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute)
  2897. +{
  2898. +    NS_ASSERTION(aContent == ContentNode() || aContent == mKeyContent,
  2899. +                 "Received an event that wasn't meant for us!");
  2900. +
  2901. +    if (Parent()->NeedsRebuild()) {
  2902. +        return;
  2903. +    }
  2904. +
  2905. +    if (aContent == ContentNode() && aAttribute == nsGkAtoms::checked &&
  2906. +        aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked,
  2907. +                              nsGkAtoms::_true, eCaseMatters)) {
  2908. +        if (nsContentUtils::IsSafeToRunScript()) {
  2909. +            UncheckSiblings();
  2910. +        } else {
  2911. +            nsContentUtils::AddScriptRunner(
  2912. +                new nsMenuItemUncheckSiblingsRunnable(this));
  2913. +        }
  2914. +    }
  2915. +
  2916. +    if (aContent == ContentNode()) {
  2917. +        if (aAttribute == nsGkAtoms::key) {
  2918. +            UpdateAccel();
  2919. +        } else if (aAttribute == nsGkAtoms::label ||
  2920. +                   aAttribute == nsGkAtoms::accesskey ||
  2921. +                   aAttribute == nsGkAtoms::crop) {
  2922. +            UpdateLabel();
  2923. +        } else if (aAttribute == nsGkAtoms::disabled) {
  2924. +            UpdateSensitivity();
  2925. +        } else if (aAttribute == nsGkAtoms::type) {
  2926. +            UpdateTypeAndState();
  2927. +        } else if (aAttribute == nsGkAtoms::checked) {
  2928. +            UpdateState();
  2929. +        }
  2930. +    } else if (aContent == mKeyContent &&
  2931. +               (aAttribute == nsGkAtoms::key ||
  2932. +                aAttribute == nsGkAtoms::keycode ||
  2933. +                aAttribute == nsGkAtoms::modifiers)) {
  2934. +        UpdateAccel();
  2935. +    }
  2936. +
  2937. +    if (!Parent()->IsBeingDisplayed() || aContent != ContentNode()) {
  2938. +        return;
  2939. +    }
  2940. +
  2941. +    if (aAttribute == nsGkAtoms::hidden ||
  2942. +        aAttribute == nsGkAtoms::collapsed) {
  2943. +        nsRefPtr<nsStyleContext> sc = GetStyleContext();
  2944. +        UpdateVisibility(sc);
  2945. +    } else if (aAttribute == nsGkAtoms::image) {
  2946. +        nsRefPtr<nsStyleContext> sc = GetStyleContext();
  2947. +        UpdateIcon(sc);
  2948. +    }
  2949. +}
  2950. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuItem.h
  2951. ===================================================================
  2952. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  2953. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuItem.h 2014-12-08 19:19:07.478645113 +0000
  2954. @@ -0,0 +1,107 @@
  2955. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2956. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  2957. + */
  2958. +/* This Source Code Form is subject to the terms of the Mozilla Public
  2959. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  2960. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  2961. +
  2962. +#ifndef __nsMenuItem_h__
  2963. +#define __nsMenuItem_h__
  2964. +
  2965. +#include "mozilla/Attributes.h"
  2966. +#include "nsCOMPtr.h"
  2967. +
  2968. +#include "nsDbusmenu.h"
  2969. +#include "nsMenuObject.h"
  2970. +
  2971. +#include <glib.h>
  2972. +
  2973. +#define NSMENUITEM_NUMBER_OF_TYPE_BITS 2U
  2974. +#define NSMENUITEM_NUMBER_OF_FLAGS     1U
  2975. +
  2976. +class nsIAtom;
  2977. +class nsIContent;
  2978. +class nsStyleContext;
  2979. +class nsMenuBar;
  2980. +class nsMenuContainer;
  2981. +
  2982. +/*
  2983. + * This class represents 3 main classes of menuitems: labels, checkboxes and
  2984. + * radio buttons (with/without an icon)
  2985. + */
  2986. +class nsMenuItem MOZ_FINAL : public nsMenuObject
  2987. +{
  2988. +public:
  2989. +    ~nsMenuItem();
  2990. +
  2991. +    nsMenuObject::EType Type() const;
  2992. +
  2993. +    static nsMenuObject* Create(nsMenuContainer *aParent,
  2994. +                                nsIContent *aContent);
  2995. +
  2996. +    void OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute);
  2997. +
  2998. +private:
  2999. +    friend class nsMenuItemUncheckSiblingsRunnable;
  3000. +
  3001. +    enum {
  3002. +        eMenuItemFlag_ToggleState = (1 << 0)
  3003. +    };
  3004. +
  3005. +    enum EMenuItemType {
  3006. +        eMenuItemType_Normal,
  3007. +        eMenuItemType_Radio,
  3008. +        eMenuItemType_CheckBox
  3009. +    };
  3010. +
  3011. +    nsMenuItem();
  3012. +
  3013. +    EMenuItemType MenuItemType() const
  3014. +    {
  3015. +        return static_cast<EMenuItemType>(
  3016. +            (GetFlags() &
  3017. +             (((1U << NSMENUITEM_NUMBER_OF_TYPE_BITS) - 1U)
  3018. +              << NSMENUITEM_NUMBER_OF_FLAGS)) >> NSMENUITEM_NUMBER_OF_FLAGS);
  3019. +    }
  3020. +    void SetMenuItemType(EMenuItemType aType)
  3021. +    {
  3022. +        ClearFlags(((1U << NSMENUITEM_NUMBER_OF_TYPE_BITS) - 1U) << NSMENUITEM_NUMBER_OF_FLAGS);
  3023. +        SetFlags(aType << NSMENUITEM_NUMBER_OF_FLAGS);
  3024. +    }
  3025. +    bool IsCheckboxOrRadioItem() const;
  3026. +
  3027. +    bool IsChecked() const
  3028. +    {
  3029. +        return HasFlags(eMenuItemFlag_ToggleState);
  3030. +    }
  3031. +    void SetCheckState(bool aState)
  3032. +    {
  3033. +        if (aState) {
  3034. +            SetFlags(eMenuItemFlag_ToggleState);
  3035. +        } else {
  3036. +            ClearFlags(eMenuItemFlag_ToggleState);
  3037. +        }
  3038. +    }
  3039. +
  3040. +    static void item_activated_cb(DbusmenuMenuitem *menuitem,
  3041. +                                  guint timestamp,
  3042. +                                  gpointer user_data);
  3043. +    void Activate(uint32_t aTimestamp);
  3044. +
  3045. +    void CopyAttrFromNodeIfExists(nsIContent *aContent, nsIAtom *aAtom);
  3046. +    void UpdateState();
  3047. +    void UpdateTypeAndState();
  3048. +    void UpdateAccel();
  3049. +
  3050. +    void InitializeNativeData();
  3051. +    void UpdateContentAttributes();
  3052. +    void Update(nsStyleContext *aStyleContext);
  3053. +    void UncheckSiblings();
  3054. +    bool IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const;
  3055. +    nsMenuBar* MenuBar();
  3056. +    nsMenuObject::PropertyFlags SupportedProperties() const;
  3057. +
  3058. +    nsCOMPtr<nsIContent> mKeyContent;
  3059. +};
  3060. +
  3061. +#endif /* __nsMenuItem_h__ */
  3062. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuObject.cpp
  3063. ===================================================================
  3064. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  3065. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuObject.cpp 2014-12-08 19:19:07.478645113 +0000
  3066. @@ -0,0 +1,707 @@
  3067. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  3068. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  3069. + */
  3070. +/* This Source Code Form is subject to the terms of the Mozilla Public
  3071. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  3072. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  3073. +
  3074. +#include "ImageOps.h"
  3075. +#include "imgIContainer.h"
  3076. +#include "imgINotificationObserver.h"
  3077. +#include "imgLoader.h"
  3078. +#include "imgRequestProxy.h"
  3079. +#include "mozilla/ArrayUtils.h"
  3080. +#include "mozilla/dom/Element.h"
  3081. +#include "mozilla/LookAndFeel.h"
  3082. +#include "mozilla/Preferences.h"
  3083. +#include "nsAttrValue.h"
  3084. +#include "nsComputedDOMStyle.h"
  3085. +#include "nsContentUtils.h"
  3086. +#include "nsGkAtoms.h"
  3087. +#include "nsIContent.h"
  3088. +#include "nsIContentPolicy.h"
  3089. +#include "nsIDocument.h"
  3090. +#include "nsILoadGroup.h"
  3091. +#include "nsImageToPixbuf.h"
  3092. +#include "nsIPresShell.h"
  3093. +#include "nsIURI.h"
  3094. +#include "nsNetUtil.h"
  3095. +#include "nsPresContext.h"
  3096. +#include "nsRect.h"
  3097. +#include "nsServiceManagerUtils.h"
  3098. +#include "nsString.h"
  3099. +#include "nsStyleConsts.h"
  3100. +#include "nsStyleContext.h"
  3101. +#include "nsStyleStruct.h"
  3102. +#include "nsThreadUtils.h"
  3103. +#include "nsUnicharUtils.h"
  3104. +
  3105. +#include "nsMenuContainer.h"
  3106. +#include "nsNativeMenuAtoms.h"
  3107. +#include "nsNativeMenuDocListener.h"
  3108. +#include "nsNativeMenuUtils.h"
  3109. +
  3110. +#include <gdk/gdk.h>
  3111. +#include <glib-object.h>
  3112. +#include <pango/pango.h>
  3113. +
  3114. +#include "nsMenuObject.h"
  3115. +
  3116. +using namespace mozilla;
  3117. +using mozilla::image::ImageOps;
  3118. +
  3119. +#define MAX_WIDTH 350000
  3120. +
  3121. +const char *gPropertyStrings[] = {
  3122. +#define DBUSMENU_PROPERTY(e, s, b) s,
  3123. +    DBUSMENU_PROPERTIES
  3124. +#undef DBUSMENU_PROPERTY
  3125. +    nullptr
  3126. +};
  3127. +
  3128. +nsWeakMenuObjectBase* nsWeakMenuObjectBase::sHead;
  3129. +PangoLayout* gPangoLayout = nullptr;
  3130. +
  3131. +class nsMenuObjectContainerOpeningRunnable : public nsRunnable
  3132. +{
  3133. +public:
  3134. +    NS_IMETHODIMP Run()
  3135. +    {
  3136. +        if (mMenuObject) {
  3137. +            mMenuObject->ContainerIsOpening();
  3138. +        }
  3139. +        return NS_OK;
  3140. +    }
  3141. +
  3142. +    nsMenuObjectContainerOpeningRunnable(nsMenuObject *aMenuObject) :
  3143. +        mMenuObject(aMenuObject) { };
  3144. +
  3145. +private:
  3146. +    nsWeakMenuObject<nsMenuObject> mMenuObject;
  3147. +};
  3148. +
  3149. +class nsMenuObjectIconLoader MOZ_FINAL : public imgINotificationObserver
  3150. +{
  3151. +public:
  3152. +    NS_DECL_ISUPPORTS
  3153. +    NS_DECL_IMGINOTIFICATIONOBSERVER
  3154. +
  3155. +    nsMenuObjectIconLoader(nsMenuObject *aOwner) : mOwner(aOwner),
  3156. +                                                   mIconLoaded(false) { };
  3157. +
  3158. +    void LoadIcon(nsStyleContext *aStyleContext);
  3159. +    void Destroy();
  3160. +
  3161. +private:
  3162. +    ~nsMenuObjectIconLoader() { };
  3163. +
  3164. +    nsMenuObject *mOwner;
  3165. +    nsRefPtr<imgRequestProxy> mImageRequest;
  3166. +    nsCOMPtr<nsIURI> mURI;
  3167. +    nsIntRect mImageRect;
  3168. +    bool mIconLoaded;
  3169. +};
  3170. +
  3171. +NS_IMPL_ISUPPORTS(nsMenuObjectIconLoader, imgINotificationObserver)
  3172. +
  3173. +NS_IMETHODIMP
  3174. +nsMenuObjectIconLoader::Notify(imgIRequest *aProxy,
  3175. +                               int32_t aType, const nsIntRect *aRect)
  3176. +{
  3177. +    if (!mOwner) {
  3178. +        return NS_OK;
  3179. +    }
  3180. +
  3181. +    if (aProxy != mImageRequest) {
  3182. +        return NS_ERROR_FAILURE;
  3183. +    }
  3184. +
  3185. +    if (aType == imgINotificationObserver::DECODE_COMPLETE) {
  3186. +        mImageRequest->Cancel(NS_BINDING_ABORTED);
  3187. +        mImageRequest = nullptr;
  3188. +        return NS_OK;
  3189. +    }
  3190. +
  3191. +    if (aType != imgINotificationObserver::FRAME_COMPLETE) {
  3192. +        return NS_OK;
  3193. +    }
  3194. +
  3195. +    if (mIconLoaded) {
  3196. +        return NS_OK;
  3197. +    }
  3198. +
  3199. +    mIconLoaded = true;
  3200. +
  3201. +    nsCOMPtr<imgIContainer> img;
  3202. +    mImageRequest->GetImage(getter_AddRefs(img));
  3203. +    if (!img) {
  3204. +        return NS_ERROR_FAILURE;
  3205. +    }
  3206. +
  3207. +    if (!mImageRect.IsEmpty()) {
  3208. +        img = ImageOps::Clip(img, mImageRect);
  3209. +    }
  3210. +
  3211. +    int32_t width, height;
  3212. +    img->GetWidth(&width);
  3213. +    img->GetHeight(&height);
  3214. +
  3215. +    if (width <= 0 || height <= 0) {
  3216. +        mOwner->ClearIcon();
  3217. +        return NS_OK;
  3218. +    }
  3219. +
  3220. +    if (width > 100 || height > 100) {
  3221. +        // The icon data needs to go across DBus. Make sure the icon
  3222. +        // data isn't too large, else our connection gets terminated and
  3223. +        // GDbus helpfully aborts the application. Thank you :)
  3224. +        NS_WARNING("Icon data too large");
  3225. +        mOwner->ClearIcon();
  3226. +        return NS_OK;
  3227. +    }
  3228. +
  3229. +    GdkPixbuf *pixbuf = nsImageToPixbuf::ImageToPixbuf(img);
  3230. +    if (pixbuf) {
  3231. +        dbusmenu_menuitem_property_set_image(mOwner->GetNativeData(),
  3232. +                                             DBUSMENU_MENUITEM_PROP_ICON_DATA,
  3233. +                                             pixbuf);
  3234. +        g_object_unref(pixbuf);
  3235. +    }
  3236. +
  3237. +    return NS_OK;
  3238. +}
  3239. +
  3240. +void
  3241. +nsMenuObjectIconLoader::LoadIcon(nsStyleContext *aStyleContext)
  3242. +{
  3243. +    nsIDocument *doc = mOwner->ContentNode()->OwnerDoc();
  3244. +
  3245. +    nsCOMPtr<nsIURI> uri;
  3246. +    nsIntRect imageRect;
  3247. +    imgRequestProxy *imageRequest = nullptr;
  3248. +
  3249. +    nsAutoString uriString;
  3250. +    if (mOwner->ContentNode()->GetAttr(kNameSpaceID_None, nsGkAtoms::image,
  3251. +                                       uriString)) {
  3252. +        NS_NewURI(getter_AddRefs(uri), uriString);
  3253. +    } else {
  3254. +        nsIPresShell *shell = doc->GetShell();
  3255. +        if (!shell) {
  3256. +            return;
  3257. +        }
  3258. +
  3259. +        nsPresContext *pc = shell->GetPresContext();
  3260. +        if (!pc || !aStyleContext) {
  3261. +            return;
  3262. +        }
  3263. +
  3264. +        const nsStyleList *list = aStyleContext->StyleList();
  3265. +        imageRequest = list->GetListStyleImage();
  3266. +        if (imageRequest) {
  3267. +            imageRequest->GetURI(getter_AddRefs(uri));
  3268. +            imageRect = list->mImageRegion.ToNearestPixels(
  3269. +                            pc->AppUnitsPerDevPixel());
  3270. +        }
  3271. +    }
  3272. +
  3273. +    if (!uri) {
  3274. +        mOwner->ClearIcon();
  3275. +        mURI = nullptr;
  3276. +
  3277. +        if (mImageRequest) {
  3278. +            mImageRequest->Cancel(NS_BINDING_ABORTED);
  3279. +            mImageRequest = nullptr;
  3280. +        }
  3281. +
  3282. +        return;
  3283. +    }
  3284. +
  3285. +    bool same;
  3286. +    if (mURI && NS_SUCCEEDED(mURI->Equals(uri, &same)) && same &&
  3287. +        (!imageRequest || imageRect == mImageRect)) {
  3288. +        return;
  3289. +    }
  3290. +
  3291. +    if (mImageRequest) {
  3292. +        mImageRequest->Cancel(NS_BINDING_ABORTED);
  3293. +        mImageRequest = nullptr;
  3294. +    }
  3295. +
  3296. +    mIconLoaded = false;
  3297. +
  3298. +    mURI = uri;
  3299. +
  3300. +    if (imageRequest) {
  3301. +        mImageRect = imageRect;
  3302. +        imageRequest->Clone(this, getter_AddRefs(mImageRequest));
  3303. +    } else {
  3304. +        mImageRect.SetEmpty();
  3305. +        nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
  3306. +        nsRefPtr<imgLoader> loader =
  3307. +            nsContentUtils::GetImgLoaderForDocument(doc);
  3308. +        if (!loader || !loadGroup) {
  3309. +            NS_WARNING("Failed to get loader or load group for image load");
  3310. +            return;
  3311. +        }
  3312. +
  3313. +        loader->LoadImage(uri, nullptr, nullptr, mozilla::net::RP_Default,
  3314. +                          nullptr, loadGroup, this, nullptr,
  3315. +                          nsIRequest::LOAD_NORMAL, nullptr,
  3316. +                          nsIContentPolicy::TYPE_IMAGE, EmptyString(),
  3317. +                          getter_AddRefs(mImageRequest));
  3318. +    }
  3319. +
  3320. +    if (!mIconLoaded) {
  3321. +        if (!mImageRequest) {
  3322. +            NS_WARNING("Failed to load icon");
  3323. +            return;
  3324. +        }
  3325. +
  3326. +        mImageRequest->RequestDecode();
  3327. +    }
  3328. +}
  3329. +
  3330. +void
  3331. +nsMenuObjectIconLoader::Destroy()
  3332. +{
  3333. +    if (mImageRequest) {
  3334. +        mImageRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
  3335. +        mImageRequest = nullptr;
  3336. +    }
  3337. +
  3338. +    mOwner = nullptr;
  3339. +}
  3340. +
  3341. +static int
  3342. +CalculateTextWidth(const nsAString& aText)
  3343. +{
  3344. +    if (!gPangoLayout) {
  3345. +        PangoFontMap *fontmap = pango_cairo_font_map_get_default();
  3346. +        PangoContext *ctx = pango_font_map_create_context(fontmap);
  3347. +        gPangoLayout = pango_layout_new(ctx);
  3348. +        g_object_unref(ctx);
  3349. +    }
  3350. +
  3351. +    pango_layout_set_text(gPangoLayout, NS_ConvertUTF16toUTF8(aText).get(), -1);
  3352. +
  3353. +    int width, dummy;
  3354. +    pango_layout_get_size(gPangoLayout, &width, &dummy);
  3355. +
  3356. +    return width;
  3357. +}
  3358. +
  3359. +static const nsDependentString
  3360. +GetEllipsis()
  3361. +{
  3362. +    static char16_t sBuf[4] = { 0, 0, 0, 0 };
  3363. +    if (!sBuf[0]) {
  3364. +        nsAdoptingString ellipsis = Preferences::GetLocalizedString("intl.ellipsis");
  3365. +        if (!ellipsis.IsEmpty()) {
  3366. +            uint32_t l = ellipsis.Length();
  3367. +            const nsAdoptingString::char_type *c = ellipsis.BeginReading();
  3368. +            uint32_t i = 0;
  3369. +            while (i < 3 && i < l) {
  3370. +                sBuf[i++] = *(c++);
  3371. +            }
  3372. +        } else {
  3373. +            sBuf[0] = '.';
  3374. +            sBuf[1] = '.';
  3375. +            sBuf[2] = '.';
  3376. +        }
  3377. +    }
  3378. +
  3379. +    return nsDependentString(sBuf);
  3380. +}
  3381. +
  3382. +static int
  3383. +GetEllipsisWidth()
  3384. +{
  3385. +    static int sEllipsisWidth = -1;
  3386. +
  3387. +    if (sEllipsisWidth == -1) {
  3388. +        sEllipsisWidth = CalculateTextWidth(GetEllipsis());
  3389. +    }
  3390. +
  3391. +    return sEllipsisWidth;
  3392. +}
  3393. +
  3394. +void
  3395. +nsMenuObject::InitializeNativeData()
  3396. +{
  3397. +}
  3398. +
  3399. +nsMenuObject::PropertyFlags
  3400. +nsMenuObject::SupportedProperties() const
  3401. +{
  3402. +    return static_cast<nsMenuObject::PropertyFlags>(0);
  3403. +}
  3404. +
  3405. +bool
  3406. +nsMenuObject::IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const
  3407. +{
  3408. +    return true;
  3409. +}
  3410. +
  3411. +void
  3412. +nsMenuObject::UpdateContentAttributes()
  3413. +{
  3414. +}
  3415. +
  3416. +void
  3417. +nsMenuObject::Update(nsStyleContext *aStyleContext)
  3418. +{
  3419. +}
  3420. +
  3421. +bool
  3422. +nsMenuObject::ShouldShowIcon() const
  3423. +{
  3424. +    static bool known = false;
  3425. +    static bool showImagesInMenus = false;
  3426. +
  3427. +    // Ideally we want to know the visibility of the anonymous XUL image in
  3428. +    // our menuitem, but this isn't created because we don't have a frame.
  3429. +    // The following works by default (because xul.css hides images in menuitems
  3430. +    // that don't have the "menuitem-with-favicon" class, when eIntID_ImagesInMenus
  3431. +    // is false). It's possible a third party theme could override this, but,
  3432. +    // oh well...
  3433. +    if (!known) {
  3434. +        showImagesInMenus =
  3435. +            LookAndFeel::GetInt(LookAndFeel::eIntID_ImagesInMenus);
  3436. +        known = true;
  3437. +    }
  3438. +
  3439. +    if (showImagesInMenus) {
  3440. +        return true;
  3441. +    }
  3442. +
  3443. +    const nsAttrValue *classes = mContent->GetClasses();
  3444. +    if (!classes) {
  3445. +        return false;
  3446. +    }
  3447. +
  3448. +    for (uint32_t i = 0; i < classes->GetAtomCount(); ++i) {
  3449. +        if (classes->AtomAt(i) == nsNativeMenuAtoms::menuitem_with_favicon) {
  3450. +            return true;
  3451. +        }
  3452. +    }
  3453. +
  3454. +    return false;
  3455. +}
  3456. +
  3457. +void
  3458. +nsMenuObject::ClearIcon()
  3459. +{
  3460. +    dbusmenu_menuitem_property_remove(mNativeData,
  3461. +                                      DBUSMENU_MENUITEM_PROP_ICON_DATA);
  3462. +}
  3463. +
  3464. +void
  3465. +nsMenuObject::UpdateLabel()
  3466. +{
  3467. +    // Gecko stores the label and access key in separate attributes
  3468. +    // so we need to convert label="Foo_Bar"/accesskey="F" in to
  3469. +    // label="_Foo__Bar" for dbusmenu
  3470. +
  3471. +    nsAutoString label;
  3472. +    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, label);
  3473. +
  3474. +    nsAutoString accesskey;
  3475. +    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accesskey);
  3476. +
  3477. +    const nsAutoString::char_type *akey = accesskey.BeginReading();
  3478. +    char16_t keyLower = ToLowerCase(*akey);
  3479. +    char16_t keyUpper = ToUpperCase(*akey);
  3480. +
  3481. +    const nsAutoString::char_type *iter = label.BeginReading();
  3482. +    const nsAutoString::char_type *end = label.EndReading();
  3483. +    uint32_t length = label.Length();
  3484. +    uint32_t pos = 0;
  3485. +    bool foundAccessKey = false;
  3486. +
  3487. +    while (iter != end) {
  3488. +        if (*iter != char16_t('_')) {
  3489. +            if ((*iter != keyLower && *iter != keyUpper) || foundAccessKey) {
  3490. +                ++iter;
  3491. +                ++pos;
  3492. +                continue;
  3493. +            }
  3494. +            foundAccessKey = true;
  3495. +        }
  3496. +
  3497. +        label.SetLength(++length);
  3498. +
  3499. +        iter = label.BeginReading() + pos;
  3500. +        end = label.EndReading();
  3501. +        nsAutoString::char_type *cur = label.BeginWriting() + pos;
  3502. +
  3503. +        memmove(cur + 1, cur, (length - 1 - pos) * sizeof(nsAutoString::char_type));
  3504. +        *cur = nsAutoString::char_type('_');
  3505. +
  3506. +        iter += 2;
  3507. +        pos += 2;
  3508. +    }
  3509. +
  3510. +    if (CalculateTextWidth(label) <= MAX_WIDTH) {
  3511. +        dbusmenu_menuitem_property_set(mNativeData,
  3512. +                                       DBUSMENU_MENUITEM_PROP_LABEL,
  3513. +                                       NS_ConvertUTF16toUTF8(label).get());
  3514. +        return;
  3515. +    }
  3516. +
  3517. +    // This *COMPLETELY SUCKS*
  3518. +    // This should be done at the point where the menu is drawn (hello Unity),
  3519. +    // but unfortunately it doesn't do that and will happily fill your entire
  3520. +    // screen width with a menu if you have a bookmark with a really long title.
  3521. +    // This leaves us with no other option but to ellipsize here, with no proper
  3522. +    // knowledge of Unity's render path, font size etc. This is better than nothing
  3523. +    // BAH! @*&!$
  3524. +    nsAutoString truncated;
  3525. +    int target = MAX_WIDTH - GetEllipsisWidth();
  3526. +    length = label.Length();
  3527. +
  3528. +    static nsIContent::AttrValuesArray strings[] = {
  3529. +        &nsGkAtoms::left, &nsGkAtoms::start,
  3530. +        &nsGkAtoms::center, &nsGkAtoms::right,
  3531. +        &nsGkAtoms::end, nullptr
  3532. +    };
  3533. +
  3534. +    int32_t type = mContent->FindAttrValueIn(kNameSpaceID_None,
  3535. +                                             nsGkAtoms::crop,
  3536. +                                             strings, eCaseMatters);
  3537. +
  3538. +    switch (type) {
  3539. +        case 0:
  3540. +        case 1:
  3541. +            // FIXME: Implement left cropping (do we really care?)
  3542. +        case 2:
  3543. +            // FIXME: Implement center cropping (do we really care?)
  3544. +        case 3:
  3545. +        case 4:
  3546. +        default:
  3547. +            for (uint32_t i = 0; i < length; i++) {
  3548. +                truncated.Append(label.CharAt(i));
  3549. +                if (CalculateTextWidth(truncated) > target) {
  3550. +                    break;
  3551. +                }
  3552. +            }
  3553. +
  3554. +            truncated.Append(GetEllipsis());
  3555. +    }
  3556. +
  3557. +    dbusmenu_menuitem_property_set(mNativeData,
  3558. +                                   DBUSMENU_MENUITEM_PROP_LABEL,
  3559. +                                   NS_ConvertUTF16toUTF8(truncated).get());
  3560. +}
  3561. +
  3562. +void
  3563. +nsMenuObject::UpdateVisibility(nsStyleContext *aStyleContext)
  3564. +{
  3565. +    bool vis = true;
  3566. +
  3567. +    if (aStyleContext &&
  3568. +        (aStyleContext->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_NONE ||
  3569. +         aStyleContext->StyleVisibility()->mVisible ==
  3570. +            NS_STYLE_VISIBILITY_COLLAPSE)) {
  3571. +        vis = false;
  3572. +    }
  3573. +
  3574. +    dbusmenu_menuitem_property_set_bool(mNativeData,
  3575. +                                        DBUSMENU_MENUITEM_PROP_VISIBLE,
  3576. +                                        vis);
  3577. +}
  3578. +
  3579. +void
  3580. +nsMenuObject::UpdateSensitivity()
  3581. +{
  3582. +    bool disabled = mContent->AttrValueIs(kNameSpaceID_None,
  3583. +                                          nsGkAtoms::disabled,
  3584. +                                          nsGkAtoms::_true, eCaseMatters);
  3585. +
  3586. +    dbusmenu_menuitem_property_set_bool(mNativeData,
  3587. +                                        DBUSMENU_MENUITEM_PROP_ENABLED,
  3588. +                                        !disabled);
  3589. +
  3590. +}
  3591. +
  3592. +void
  3593. +nsMenuObject::UpdateIcon(nsStyleContext *aStyleContext)
  3594. +{
  3595. +    if (ShouldShowIcon()) {
  3596. +        if (!mIconLoader) {
  3597. +            mIconLoader = new nsMenuObjectIconLoader(this);
  3598. +        }
  3599. +
  3600. +        mIconLoader->LoadIcon(aStyleContext);
  3601. +    } else {
  3602. +        if (mIconLoader) {
  3603. +            mIconLoader->Destroy();
  3604. +            mIconLoader = nullptr;
  3605. +        }
  3606. +
  3607. +        ClearIcon();
  3608. +    }
  3609. +}
  3610. +
  3611. +already_AddRefed<nsStyleContext>
  3612. +nsMenuObject::GetStyleContext()
  3613. +{
  3614. +    nsIPresShell *shell = mContent->OwnerDoc()->GetShell();
  3615. +    if (!shell) {
  3616. +        return nullptr;
  3617. +    }
  3618. +
  3619. +    nsRefPtr<nsStyleContext> sc =
  3620. +        nsComputedDOMStyle::GetStyleContextForElementNoFlush(
  3621. +            mContent->AsElement(), nullptr, shell);
  3622. +
  3623. +    return sc.forget();
  3624. +}
  3625. +
  3626. +nsresult
  3627. +nsMenuObject::Init(nsMenuContainer *aParent, nsIContent *aContent)
  3628. +{
  3629. +    NS_ENSURE_ARG(aParent);
  3630. +    NS_ENSURE_ARG(aContent);
  3631. +
  3632. +    mParent = aParent;
  3633. +    mContent = aContent;
  3634. +    mListener = aParent->DocListener();
  3635. +    NS_ENSURE_ARG(mListener);
  3636. +
  3637. +    return NS_OK;    
  3638. +}
  3639. +
  3640. +nsresult
  3641. +nsMenuObject::Init(nsNativeMenuDocListener *aListener, nsIContent *aContent)
  3642. +{
  3643. +    NS_ENSURE_ARG(aListener);
  3644. +    NS_ENSURE_ARG(aContent);
  3645. +
  3646. +    mParent = nullptr;
  3647. +    mContent = aContent;
  3648. +    mListener = aListener;
  3649. +
  3650. +    return NS_OK;
  3651. +}
  3652. +
  3653. +nsMenuObject::nsMenuObject() :
  3654. +    mParent(nullptr), mNativeData(nullptr), mFlags(0)
  3655. +{
  3656. +}
  3657. +
  3658. +nsMenuObject::~nsMenuObject()
  3659. +{
  3660. +    nsWeakMenuObjectBase::NotifyDestroyed(this);
  3661. +
  3662. +    if (mIconLoader) {
  3663. +        mIconLoader->Destroy();
  3664. +    }
  3665. +
  3666. +    if (mListener) {
  3667. +        mListener->UnregisterForContentChanges(mContent);
  3668. +    }
  3669. +
  3670. +    if (mNativeData) {
  3671. +        g_object_unref(mNativeData);
  3672. +        mNativeData = nullptr;
  3673. +    }
  3674. +}
  3675. +
  3676. +void
  3677. +nsMenuObject::CreateNativeData()
  3678. +{
  3679. +    NS_ASSERTION(mNativeData == nullptr, "This node already has a DbusmenuMenuitem. The old one will be leaked");
  3680. +
  3681. +    mNativeData = dbusmenu_menuitem_new();
  3682. +    InitializeNativeData();
  3683. +    if (mParent && mParent->IsBeingDisplayed()) {
  3684. +        ContainerIsOpening();
  3685. +    }
  3686. +
  3687. +    mListener->RegisterForContentChanges(mContent, this);
  3688. +}
  3689. +
  3690. +nsresult
  3691. +nsMenuObject::AdoptNativeData(DbusmenuMenuitem *aNativeData)
  3692. +{
  3693. +    NS_ASSERTION(mNativeData == nullptr, "This node already has a DbusmenuMenuitem. The old one will be leaked");
  3694. +
  3695. +    if (!IsCompatibleWithNativeData(aNativeData)) {
  3696. +        return NS_ERROR_FAILURE;
  3697. +    }
  3698. +
  3699. +    mNativeData = aNativeData;
  3700. +    g_object_ref(mNativeData);
  3701. +
  3702. +    PropertyFlags supported = SupportedProperties();
  3703. +    PropertyFlags mask = static_cast<PropertyFlags>(1);
  3704. +
  3705. +    for (uint32_t i = 0; gPropertyStrings[i]; ++i) {
  3706. +        if (!(mask & supported)) {
  3707. +            dbusmenu_menuitem_property_remove(mNativeData, gPropertyStrings[i]);
  3708. +        }
  3709. +        mask = static_cast<PropertyFlags>(mask << 1);
  3710. +    }
  3711. +
  3712. +    InitializeNativeData();
  3713. +    if (mParent && mParent->IsBeingDisplayed()) {
  3714. +        ContainerIsOpening();
  3715. +    }
  3716. +
  3717. +    mListener->RegisterForContentChanges(mContent, this);
  3718. +
  3719. +    return NS_OK;
  3720. +}
  3721. +
  3722. +void
  3723. +nsMenuObject::ContainerIsOpening()
  3724. +{
  3725. +    if (!nsContentUtils::IsSafeToRunScript()) {
  3726. +        nsContentUtils::AddScriptRunner(
  3727. +            new nsMenuObjectContainerOpeningRunnable(this));
  3728. +        return;
  3729. +    }
  3730. +
  3731. +    UpdateContentAttributes();
  3732. +
  3733. +    nsRefPtr<nsStyleContext> sc = GetStyleContext();
  3734. +    Update(sc);
  3735. +}
  3736. +
  3737. +/* static */ void
  3738. +nsWeakMenuObjectBase::AddWeakReference(nsWeakMenuObjectBase *aWeak)
  3739. +{
  3740. +    aWeak->SetPrevious(sHead);
  3741. +    sHead = aWeak;
  3742. +}
  3743. +
  3744. +/* static */ void
  3745. +nsWeakMenuObjectBase::RemoveWeakReference(nsWeakMenuObjectBase *aWeak)
  3746. +{
  3747. +    if (aWeak == sHead) {
  3748. +        sHead = aWeak->GetPrevious();
  3749. +        return;
  3750. +    }
  3751. +
  3752. +    nsWeakMenuObjectBase *weak = sHead;
  3753. +    while (weak && weak->GetPrevious() != aWeak) {
  3754. +        weak = weak->GetPrevious();
  3755. +    }
  3756. +
  3757. +    if (weak) {
  3758. +        weak->SetPrevious(aWeak->GetPrevious());
  3759. +    }
  3760. +}
  3761. +
  3762. +/* static */ void
  3763. +nsWeakMenuObjectBase::NotifyDestroyed(nsMenuObject *aMenuObject)
  3764. +{
  3765. +    nsWeakMenuObjectBase *weak = sHead;
  3766. +    while (weak) {
  3767. +        if (weak->getBase() == aMenuObject) {
  3768. +            weak->Clear();
  3769. +        }
  3770. +
  3771. +        weak = weak->GetPrevious();
  3772. +    }
  3773. +}
  3774. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuObject.h
  3775. ===================================================================
  3776. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  3777. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuObject.h   2014-12-08 19:19:07.478645113 +0000
  3778. @@ -0,0 +1,242 @@
  3779. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  3780. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  3781. + */
  3782. +/* This Source Code Form is subject to the terms of the Mozilla Public
  3783. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  3784. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  3785. +
  3786. +#ifndef __nsMenuObject_h__
  3787. +#define __nsMenuObject_h__
  3788. +
  3789. +#include "mozilla/Attributes.h"
  3790. +#include "nsCOMPtr.h"
  3791. +
  3792. +#include "nsDbusmenu.h"
  3793. +#include "nsNativeMenuDocListener.h"
  3794. +
  3795. +class nsIAtom;
  3796. +class nsIContent;
  3797. +class nsStyleContext;
  3798. +class nsMenuContainer;
  3799. +class nsMenuObjectIconLoader;
  3800. +
  3801. +#define DBUSMENU_PROPERTIES \
  3802. +    DBUSMENU_PROPERTY(Label, DBUSMENU_MENUITEM_PROP_LABEL, 0) \
  3803. +    DBUSMENU_PROPERTY(Enabled, DBUSMENU_MENUITEM_PROP_ENABLED, 1) \
  3804. +    DBUSMENU_PROPERTY(Visible, DBUSMENU_MENUITEM_PROP_VISIBLE, 2) \
  3805. +    DBUSMENU_PROPERTY(IconData, DBUSMENU_MENUITEM_PROP_ICON_DATA, 3) \
  3806. +    DBUSMENU_PROPERTY(Type, DBUSMENU_MENUITEM_PROP_TYPE, 4) \
  3807. +    DBUSMENU_PROPERTY(Shortcut, DBUSMENU_MENUITEM_PROP_SHORTCUT, 5) \
  3808. +    DBUSMENU_PROPERTY(ToggleType, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, 6) \
  3809. +    DBUSMENU_PROPERTY(ToggleState, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, 7) \
  3810. +    DBUSMENU_PROPERTY(ChildDisplay, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY, 8)
  3811. +
  3812. +/*
  3813. + * This is the base class for all menu nodes. Each instance represents
  3814. + * a single node in the menu hierarchy. It wraps the corresponding DOM node and
  3815. + * native menu node, keeps them in sync and transfers events between the two.
  3816. + * It is not reference counted - each node is owned by its parent (the top
  3817. + * level menubar is owned by the window) and keeps a weak pointer to its
  3818. + * parent (which is guaranteed to always be valid because a node will never
  3819. + * outlive its parent). It is not safe to keep a reference to nsMenuObject
  3820. + * externally.
  3821. + */
  3822. +class nsMenuObject : public nsNativeMenuChangeObserver
  3823. +{
  3824. +public:
  3825. +    enum EType {
  3826. +        eType_MenuBar,
  3827. +        eType_Menu,
  3828. +        eType_MenuItem,
  3829. +        eType_MenuSeparator
  3830. +    };
  3831. +
  3832. +    enum PropertyFlags {
  3833. +#define DBUSMENU_PROPERTY(e, s, b) eProp##e = (1 << b),
  3834. +        DBUSMENU_PROPERTIES
  3835. +#undef DBUSMENU_PROPERTY
  3836. +    };
  3837. +
  3838. +    virtual ~nsMenuObject();
  3839. +
  3840. +    // Get the native menu item node
  3841. +    DbusmenuMenuitem* GetNativeData() const { return mNativeData; }
  3842. +
  3843. +    // Get the parent menu object
  3844. +    nsMenuContainer* Parent() const { return mParent; }
  3845. +
  3846. +    // Get the content node
  3847. +    nsIContent* ContentNode() const { return mContent; }
  3848. +
  3849. +    // Get the type of this node. Must be provided by subclasses
  3850. +    virtual EType Type() const = 0;
  3851. +
  3852. +    // Get the document listener
  3853. +    nsNativeMenuDocListener* DocListener() const { return mListener; }
  3854. +
  3855. +    // Create the native menu item node (called by containers)
  3856. +    void CreateNativeData();
  3857. +
  3858. +    // Adopt the specified native menu item node (called by containers)
  3859. +    nsresult AdoptNativeData(DbusmenuMenuitem *aNativeData);
  3860. +
  3861. +    // Called by the container to tell us that it's opening
  3862. +    void ContainerIsOpening();
  3863. +
  3864. +protected:
  3865. +    nsMenuObject();
  3866. +    nsresult Init(nsMenuContainer *aParent, nsIContent *aContent);
  3867. +    nsresult Init(nsNativeMenuDocListener *aListener, nsIContent *aContent);
  3868. +
  3869. +    void UpdateLabel();
  3870. +    void UpdateVisibility(nsStyleContext *aStyleContext);
  3871. +    void UpdateSensitivity();
  3872. +    void UpdateIcon(nsStyleContext *aStyleContext);
  3873. +
  3874. +    already_AddRefed<nsStyleContext> GetStyleContext();
  3875. +  
  3876. +    uint8_t GetFlags() const { return mFlags; }
  3877. +    bool HasFlags(uint8_t aFlags) const
  3878. +    {
  3879. +        return (mFlags & aFlags) == aFlags;
  3880. +    }
  3881. +    void SetFlags(uint8_t aFlags)
  3882. +    {
  3883. +        mFlags |= aFlags;
  3884. +    }
  3885. +    void ClearFlags(uint8_t aFlags)
  3886. +    {
  3887. +        mFlags &= ~aFlags;
  3888. +    }
  3889. +
  3890. +private:
  3891. +    friend class nsMenuObjectIconLoader;
  3892. +
  3893. +    // Set up initial properties on the native data, connect to signals etc.
  3894. +    // This should be implemented by subclasses
  3895. +    virtual void InitializeNativeData();
  3896. +
  3897. +    // Return the properties that this menu object type supports
  3898. +    // This should be implemented by subclasses
  3899. +    virtual PropertyFlags SupportedProperties() const;
  3900. +
  3901. +    // Determine whether this menu object could use the specified
  3902. +    // native item. Returns true by default but can be overridden by subclasses
  3903. +    virtual bool
  3904. +    IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const;
  3905. +
  3906. +    // Update attributes on this objects content node when the container opens.
  3907. +    // This is called before style resolution, and should be implemented by
  3908. +    // subclasses who want to modify attributes that might affect style.
  3909. +    // This will not be called when there are script blockers
  3910. +    virtual void UpdateContentAttributes();
  3911. +
  3912. +    // Update properties that should be refreshed when the container opens.
  3913. +    // This should be implemented by subclasses that have properties which
  3914. +    // need refreshing
  3915. +    virtual void Update(nsStyleContext *aStyleContext);
  3916. +
  3917. +    bool ShouldShowIcon() const;
  3918. +    void ClearIcon();
  3919. +
  3920. +    nsCOMPtr<nsIContent> mContent;
  3921. +    // mListener is a strong ref for simplicity - someone in the tree needs to
  3922. +    // own it, and this only really needs to be the top-level object (as no
  3923. +    // children outlives their parent). However, we need to keep it alive until
  3924. +    // after running the nsMenuObject destructor for the top-level menu object,
  3925. +    // hence the strong ref
  3926. +    nsRefPtr<nsNativeMenuDocListener> mListener;
  3927. +    nsMenuContainer *mParent; // [weak]
  3928. +    DbusmenuMenuitem *mNativeData; // [strong]
  3929. +    nsRefPtr<nsMenuObjectIconLoader> mIconLoader;
  3930. +    uint8_t mFlags;
  3931. +};
  3932. +
  3933. +class nsWeakMenuObjectBase
  3934. +{
  3935. +public:
  3936. +    ~nsWeakMenuObjectBase()
  3937. +    {
  3938. +        RemoveWeakReference(this);
  3939. +    }
  3940. +
  3941. +    nsMenuObject* getBase() const { return mMenuObject; }
  3942. +
  3943. +    static void NotifyDestroyed(nsMenuObject *aMenuObject);
  3944. +
  3945. +protected:
  3946. +    nsWeakMenuObjectBase() : mMenuObject(nullptr) { };
  3947. +
  3948. +    void SetMenuObject(nsMenuObject *aMenuObject)
  3949. +    {
  3950. +        mMenuObject = aMenuObject;
  3951. +
  3952. +        mMenuObject ? AddWeakReference(this) : RemoveWeakReference(this);
  3953. +    }
  3954. +
  3955. +private:
  3956. +    nsWeakMenuObjectBase* GetPrevious() const { return mPrev; }
  3957. +    void SetPrevious(nsWeakMenuObjectBase *aPrev)
  3958. +    {
  3959. +        mPrev = aPrev;
  3960. +    }
  3961. +    void Clear() { mMenuObject = nullptr; }
  3962. +
  3963. +    static void AddWeakReference(nsWeakMenuObjectBase *aWeak);
  3964. +    static void RemoveWeakReference(nsWeakMenuObjectBase *aWeak);
  3965. +
  3966. +    nsWeakMenuObjectBase *mPrev;
  3967. +    static nsWeakMenuObjectBase *sHead;
  3968. +
  3969. +    nsMenuObject *mMenuObject;
  3970. +};
  3971. +
  3972. +// Keep a weak pointer to a menu object. Note, if you need to work
  3973. +// with a pointer to this class, use nsAutoWeakMenuObject instead
  3974. +template<class T>
  3975. +class nsWeakMenuObject : public nsWeakMenuObjectBase
  3976. +{
  3977. +public:
  3978. +    nsWeakMenuObject() :
  3979. +        nsWeakMenuObjectBase() { };
  3980. +
  3981. +    nsWeakMenuObject(T *aMenuObject) :
  3982. +        nsWeakMenuObjectBase()
  3983. +    {
  3984. +        SetMenuObject(aMenuObject);
  3985. +    }
  3986. +
  3987. +    T* get() const { return static_cast<T *>(getBase()); }
  3988. +
  3989. +    T* operator->() const { return get(); }
  3990. +
  3991. +    operator T*() const { return get(); }
  3992. +};
  3993. +
  3994. +template<class T>
  3995. +class nsAutoWeakMenuObject
  3996. +{
  3997. +public:
  3998. +    nsAutoWeakMenuObject() { };
  3999. +
  4000. +    nsAutoWeakMenuObject(T *aMenuObject) :
  4001. +        mPtr(new nsWeakMenuObject<T>(aMenuObject)) { };
  4002. +
  4003. +    nsAutoWeakMenuObject(nsWeakMenuObject<T> *aWeak) :
  4004. +        mPtr(aWeak) { };
  4005. +
  4006. +    T* get() const { return static_cast<T *>(*mPtr); }
  4007. +
  4008. +    T* operator->() const { return get(); }
  4009. +
  4010. +    operator T*() const { return get(); }
  4011. +
  4012. +    nsWeakMenuObject<T>* getWeakPtr() const { return mPtr; }
  4013. +
  4014. +    nsWeakMenuObject<T>* forget() { return mPtr.forget(); }
  4015. +
  4016. +private:
  4017. +    nsAutoPtr<nsWeakMenuObject<T> > mPtr;
  4018. +};
  4019. +
  4020. +#endif /* __nsMenuObject_h__ */
  4021. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuSeparator.cpp
  4022. ===================================================================
  4023. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  4024. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuSeparator.cpp  2014-12-08 19:19:07.478645113 +0000
  4025. @@ -0,0 +1,90 @@
  4026. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  4027. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  4028. + */
  4029. +/* This Source Code Form is subject to the terms of the Mozilla Public
  4030. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  4031. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4032. +
  4033. +#include "nsAutoPtr.h"
  4034. +#include "nsCRT.h"
  4035. +#include "nsGkAtoms.h"
  4036. +#include "nsStyleContext.h"
  4037. +
  4038. +#include "nsDbusmenu.h"
  4039. +
  4040. +#include "nsMenuContainer.h"
  4041. +#include "nsMenuSeparator.h"
  4042. +
  4043. +void
  4044. +nsMenuSeparator::InitializeNativeData()
  4045. +{
  4046. +    dbusmenu_menuitem_property_set(GetNativeData(),
  4047. +                                   DBUSMENU_MENUITEM_PROP_TYPE,
  4048. +                                   "separator");
  4049. +}
  4050. +
  4051. +void
  4052. +nsMenuSeparator::Update(nsStyleContext *aContext)
  4053. +{
  4054. +    UpdateVisibility(aContext);
  4055. +}
  4056. +
  4057. +bool
  4058. +nsMenuSeparator::IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const
  4059. +{
  4060. +    return nsCRT::strcmp(dbusmenu_menuitem_property_get(aNativeData,
  4061. +                                                        DBUSMENU_MENUITEM_PROP_TYPE),
  4062. +                         "separator") == 0;
  4063. +}
  4064. +
  4065. +nsMenuObject::PropertyFlags
  4066. +nsMenuSeparator::SupportedProperties() const
  4067. +{
  4068. +    return static_cast<nsMenuObject::PropertyFlags>(
  4069. +        nsMenuObject::ePropVisible |
  4070. +        nsMenuObject::ePropType
  4071. +    );
  4072. +}
  4073. +
  4074. +nsMenuSeparator::nsMenuSeparator()
  4075. +{
  4076. +    MOZ_COUNT_CTOR(nsMenuSeparator);
  4077. +}
  4078. +
  4079. +nsMenuSeparator::~nsMenuSeparator()
  4080. +{
  4081. +    MOZ_COUNT_DTOR(nsMenuSeparator);
  4082. +}
  4083. +
  4084. +nsMenuObject::EType
  4085. +nsMenuSeparator::Type() const
  4086. +{
  4087. +    return nsMenuObject::eType_MenuSeparator;
  4088. +}
  4089. +
  4090. +/* static */ nsMenuObject*
  4091. +nsMenuSeparator::Create(nsMenuContainer *aParent, nsIContent *aContent)
  4092. +{
  4093. +    nsAutoPtr<nsMenuSeparator> sep(new nsMenuSeparator());
  4094. +    if (NS_FAILED(sep->Init(aParent, aContent))) {
  4095. +        return nullptr;
  4096. +    }
  4097. +
  4098. +    return sep.forget();
  4099. +}
  4100. +
  4101. +void
  4102. +nsMenuSeparator::OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute)
  4103. +{
  4104. +    NS_ASSERTION(aContent == ContentNode(), "Received an event that wasn't meant for us!");
  4105. +
  4106. +    if (!Parent()->IsBeingDisplayed()) {
  4107. +        return;
  4108. +    }
  4109. +
  4110. +    if (aAttribute == nsGkAtoms::hidden ||
  4111. +        aAttribute == nsGkAtoms::collapsed) {
  4112. +        nsRefPtr<nsStyleContext> sc = GetStyleContext();
  4113. +        UpdateVisibility(sc);
  4114. +    }
  4115. +}
  4116. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuSeparator.h
  4117. ===================================================================
  4118. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  4119. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsMenuSeparator.h    2014-12-08 19:19:07.478645113 +0000
  4120. @@ -0,0 +1,41 @@
  4121. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  4122. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  4123. + */
  4124. +/* This Source Code Form is subject to the terms of the Mozilla Public
  4125. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  4126. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4127. +
  4128. +#ifndef __nsMenuSeparator_h__
  4129. +#define __nsMenuSeparator_h__
  4130. +
  4131. +#include "mozilla/Attributes.h"
  4132. +
  4133. +#include "nsMenuObject.h"
  4134. +
  4135. +class nsIContent;
  4136. +class nsIAtom;
  4137. +class nsMenuContainer;
  4138. +
  4139. +// Menu separator class
  4140. +class nsMenuSeparator MOZ_FINAL : public nsMenuObject
  4141. +{
  4142. +public:
  4143. +    ~nsMenuSeparator();
  4144. +
  4145. +    nsMenuObject::EType Type() const;
  4146. +
  4147. +    static nsMenuObject* Create(nsMenuContainer *aParent,
  4148. +                                nsIContent *aContent);
  4149. +
  4150. +    void OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute);
  4151. +
  4152. +private:
  4153. +    nsMenuSeparator();
  4154. +
  4155. +    void InitializeNativeData();
  4156. +    void Update(nsStyleContext *aStyleContext);
  4157. +    bool IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const;
  4158. +    nsMenuObject::PropertyFlags SupportedProperties() const;
  4159. +};
  4160. +
  4161. +#endif /* __nsMenuSeparator_h__ */
  4162. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuAtomList.h
  4163. ===================================================================
  4164. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  4165. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuAtomList.h   2014-12-08 19:19:07.478645113 +0000
  4166. @@ -0,0 +1,11 @@
  4167. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  4168. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  4169. + */
  4170. +/* This Source Code Form is subject to the terms of the Mozilla Public
  4171. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  4172. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4173. +
  4174. +WIDGET_ATOM2(menuitem_with_favicon, "menuitem-with-favicon")
  4175. +WIDGET_ATOM2(_moz_menupopupstate, "_moz-menupopupstate")
  4176. +WIDGET_ATOM(openedwithkey)
  4177. +WIDGET_ATOM(shellshowingmenubar)
  4178. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuAtoms.cpp
  4179. ===================================================================
  4180. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  4181. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuAtoms.cpp    2014-12-08 19:19:07.478645113 +0000
  4182. @@ -0,0 +1,39 @@
  4183. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  4184. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  4185. + */
  4186. +/* This Source Code Form is subject to the terms of the Mozilla Public
  4187. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  4188. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4189. +
  4190. +#include "nsIAtom.h"
  4191. +#include "nsStaticAtom.h"
  4192. +
  4193. +#include "nsNativeMenuAtoms.h"
  4194. +
  4195. +using namespace mozilla;
  4196. +
  4197. +#define WIDGET_ATOM(_name) nsIAtom* nsNativeMenuAtoms::_name;
  4198. +#define WIDGET_ATOM2(_name, _value) nsIAtom* nsNativeMenuAtoms::_name;
  4199. +#include "nsNativeMenuAtomList.h"
  4200. +#undef WIDGET_ATOM
  4201. +#undef WIDGET_ATOM2
  4202. +
  4203. +#define WIDGET_ATOM(name_) NS_STATIC_ATOM_BUFFER(name_##_buffer, #name_)
  4204. +#define WIDGET_ATOM2(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_)
  4205. +#include "nsNativeMenuAtomList.h"
  4206. +#undef WIDGET_ATOM
  4207. +#undef WIDGET_ATOM2
  4208. +
  4209. +static const nsStaticAtom gAtoms[] = {
  4210. +#define WIDGET_ATOM(name_) NS_STATIC_ATOM(name_##_buffer, &nsNativeMenuAtoms::name_),
  4211. +#define WIDGET_ATOM2(name_, value_) NS_STATIC_ATOM(name_##_buffer, &nsNativeMenuAtoms::name_),
  4212. +#include "nsNativeMenuAtomList.h"
  4213. +#undef WIDGET_ATOM
  4214. +#undef WIDGET_ATOM2
  4215. +};
  4216. +
  4217. +/* static */ void
  4218. +nsNativeMenuAtoms::Init()
  4219. +{
  4220. +    NS_RegisterStaticAtoms(gAtoms);
  4221. +}
  4222. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuAtoms.h
  4223. ===================================================================
  4224. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  4225. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuAtoms.h  2014-12-08 19:19:07.478645113 +0000
  4226. @@ -0,0 +1,25 @@
  4227. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  4228. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  4229. + */
  4230. +/* This Source Code Form is subject to the terms of the Mozilla Public
  4231. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  4232. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4233. +
  4234. +#ifndef __nsNativeMenuAtoms_h__
  4235. +#define __nsNativeMenuAtoms_h__
  4236. +
  4237. +class nsIAtom;
  4238. +
  4239. +class nsNativeMenuAtoms
  4240. +{
  4241. +public:
  4242. +    static void Init();
  4243. +
  4244. +#define WIDGET_ATOM(_name) static nsIAtom* _name;
  4245. +#define WIDGET_ATOM2(_name, _value) static nsIAtom* _name;
  4246. +#include "nsNativeMenuAtomList.h"
  4247. +#undef WIDGET_ATOM
  4248. +#undef WIDGET_ATOM2
  4249. +};
  4250. +
  4251. +#endif /* __nsNativeMenuAtoms_h__ */
  4252. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuDocListener.cpp
  4253. ===================================================================
  4254. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  4255. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuDocListener.cpp  2014-12-08 19:19:07.478645113 +0000
  4256. @@ -0,0 +1,369 @@
  4257. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  4258. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  4259. + */
  4260. +/* This Source Code Form is subject to the terms of the Mozilla Public
  4261. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  4262. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4263. +
  4264. +#include "mozilla/Assertions.h"
  4265. +#include "mozilla/DebugOnly.h"
  4266. +#include "mozilla/dom/Element.h"
  4267. +#include "nsContentUtils.h"
  4268. +#include "nsIAtom.h"
  4269. +#include "nsIContent.h"
  4270. +#include "nsIDocument.h"
  4271. +
  4272. +#include "nsMenuContainer.h"
  4273. +
  4274. +#include "nsNativeMenuDocListener.h"
  4275. +
  4276. +using namespace mozilla;
  4277. +
  4278. +uint32_t nsNativeMenuDocListener::sUpdateDepth = 0;
  4279. +
  4280. +nsNativeMenuDocListenerTArray *gPendingListeners;
  4281. +
  4282. +/*
  4283. + * Small helper which caches a single listener, so that consecutive
  4284. + * events which go to the same node avoid multiple hash table lookups
  4285. + */
  4286. +class MOZ_STACK_CLASS DispatchHelper
  4287. +{
  4288. +public:
  4289. +    DispatchHelper(nsNativeMenuDocListener *aListener,
  4290. +                   nsIContent *aContent
  4291. +                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
  4292. +                   mObserver(nullptr)
  4293. +    {
  4294. +        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
  4295. +        if (aContent == aListener->mLastSource) {
  4296. +            mObserver = aListener->mLastTarget;
  4297. +        } else {
  4298. +            mObserver = aListener->mContentToObserverTable.Get(aContent);
  4299. +            if (mObserver) {
  4300. +                aListener->mLastSource = aContent;
  4301. +                aListener->mLastTarget = mObserver;
  4302. +            }
  4303. +        }
  4304. +    }
  4305. +
  4306. +    ~DispatchHelper() { };
  4307. +
  4308. +    nsNativeMenuChangeObserver* Observer() const { return mObserver; }
  4309. +
  4310. +    bool HasObserver() const { return !!mObserver; }
  4311. +
  4312. +private:
  4313. +    nsNativeMenuChangeObserver *mObserver;
  4314. +    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
  4315. +};
  4316. +
  4317. +NS_IMPL_ISUPPORTS(nsNativeMenuDocListener, nsIMutationObserver)
  4318. +
  4319. +void
  4320. +nsNativeMenuDocListener::DoAttributeChanged(nsIContent *aContent,
  4321. +                                            nsIAtom *aAttribute)
  4322. +{
  4323. +    DispatchHelper h(this, aContent);
  4324. +    if (h.HasObserver()) {
  4325. +        h.Observer()->OnAttributeChanged(aContent, aAttribute);
  4326. +    }
  4327. +}
  4328. +
  4329. +void
  4330. +nsNativeMenuDocListener::DoContentInserted(nsIContent *aContainer,
  4331. +                                           nsIContent *aChild,
  4332. +                                           nsIContent *aPrevSibling)
  4333. +{
  4334. +    DispatchHelper h(this, aContainer);
  4335. +    if (h.HasObserver()) {
  4336. +        h.Observer()->OnContentInserted(aContainer, aChild, aPrevSibling);
  4337. +    }
  4338. +}
  4339. +
  4340. +void
  4341. +nsNativeMenuDocListener::DoContentRemoved(nsIContent *aContainer,
  4342. +                                          nsIContent *aChild)
  4343. +{
  4344. +    DispatchHelper h(this, aContainer);
  4345. +    if (h.HasObserver()) {
  4346. +        h.Observer()->OnContentRemoved(aContainer, aChild);
  4347. +    }
  4348. +}
  4349. +
  4350. +void
  4351. +nsNativeMenuDocListener::DoBeginUpdateBatch(nsIContent *aTarget)
  4352. +{
  4353. +    DispatchHelper h(this, aTarget);
  4354. +    if (h.HasObserver()) {
  4355. +        h.Observer()->BeginUpdateBatch(aTarget);
  4356. +    }
  4357. +}
  4358. +
  4359. +void
  4360. +nsNativeMenuDocListener::DoEndUpdateBatch(nsIContent *aTarget)
  4361. +{
  4362. +    DispatchHelper h(this, aTarget);
  4363. +    if (h.HasObserver()) {
  4364. +        h.Observer()->EndUpdateBatch();
  4365. +    }
  4366. +}
  4367. +
  4368. +void
  4369. +nsNativeMenuDocListener::FlushPendingMutations()
  4370. +{
  4371. +    nsIContent *batchTarget = nullptr;
  4372. +    bool inUpdateBatch = false;
  4373. +
  4374. +    while (mPendingMutations.Length() > 0) {
  4375. +        MutationRecord *m = mPendingMutations[0];
  4376. +
  4377. +        if (m->mTarget != batchTarget) {
  4378. +            if (inUpdateBatch) {
  4379. +                DoEndUpdateBatch(batchTarget);
  4380. +                inUpdateBatch = false;
  4381. +            }
  4382. +
  4383. +            batchTarget = m->mTarget;
  4384. +
  4385. +            if (mPendingMutations.Length() > 1 &&
  4386. +                mPendingMutations[1]->mTarget == batchTarget) {
  4387. +                DoBeginUpdateBatch(batchTarget);
  4388. +                inUpdateBatch = true;
  4389. +            }
  4390. +        }
  4391. +
  4392. +        switch (m->mType) {
  4393. +            case MutationRecord::eAttributeChanged:
  4394. +                DoAttributeChanged(m->mTarget, m->mAttribute);
  4395. +                break;
  4396. +            case MutationRecord::eContentInserted:
  4397. +                DoContentInserted(m->mTarget, m->mChild, m->mPrevSibling);
  4398. +                break;
  4399. +            case MutationRecord::eContentRemoved:
  4400. +                DoContentRemoved(m->mTarget, m->mChild);
  4401. +                break;
  4402. +            default:
  4403. +                NS_NOTREACHED("Invalid type");
  4404. +        }
  4405. +
  4406. +        mPendingMutations.RemoveElementAt(0);
  4407. +    }
  4408. +
  4409. +    if (inUpdateBatch) {
  4410. +        DoEndUpdateBatch(batchTarget);
  4411. +    }
  4412. +}
  4413. +
  4414. +/* static */ void
  4415. +nsNativeMenuDocListener::ScheduleFlush(nsNativeMenuDocListener *aListener)
  4416. +{
  4417. +    NS_ASSERTION(sUpdateDepth > 0, "Shouldn't be doing this now");
  4418. +
  4419. +    if (!gPendingListeners) {
  4420. +        gPendingListeners = new nsNativeMenuDocListenerTArray;
  4421. +    }
  4422. +
  4423. +    if (gPendingListeners->IndexOf(aListener) ==
  4424. +        nsNativeMenuDocListenerTArray::NoIndex) {
  4425. +        gPendingListeners->AppendElement(aListener);
  4426. +    }
  4427. +}
  4428. +
  4429. +/* static */ void
  4430. +nsNativeMenuDocListener::CancelFlush(nsNativeMenuDocListener *aListener)
  4431. +{
  4432. +    if (!gPendingListeners) {
  4433. +        return;
  4434. +    }
  4435. +
  4436. +    gPendingListeners->RemoveElement(aListener);
  4437. +}
  4438. +
  4439. +/* static */ void
  4440. +nsNativeMenuDocListener::EndUpdates()
  4441. +{
  4442. +    if (sUpdateDepth == 1 && gPendingListeners) {
  4443. +        while (gPendingListeners->Length() > 0) {
  4444. +            (*gPendingListeners)[0]->FlushPendingMutations();
  4445. +            gPendingListeners->RemoveElementAt(0);
  4446. +        }
  4447. +    }
  4448. +
  4449. +    NS_ASSERTION(sUpdateDepth > 0, "Negative update depth!");
  4450. +    sUpdateDepth--;
  4451. +}
  4452. +
  4453. +nsNativeMenuDocListener::nsNativeMenuDocListener() :
  4454. +    mDocument(nullptr),
  4455. +    mLastSource(nullptr),
  4456. +    mLastTarget(nullptr)
  4457. +{
  4458. +    MOZ_COUNT_CTOR(nsNativeMenuDocListener);
  4459. +}
  4460. +
  4461. +nsNativeMenuDocListener::~nsNativeMenuDocListener()
  4462. +{
  4463. +    MOZ_ASSERT(mContentToObserverTable.Count() == 0,
  4464. +               "Some nodes forgot to unregister listeners. This is bad! (and we're lucky we made it this far)");
  4465. +    MOZ_COUNT_DTOR(nsNativeMenuDocListener);
  4466. +}
  4467. +
  4468. +nsresult
  4469. +nsNativeMenuDocListener::Init(nsIContent *aRootNode)
  4470. +{
  4471. +    NS_ENSURE_ARG(aRootNode);
  4472. +
  4473. +    mRootNode = aRootNode;
  4474. +
  4475. +    return NS_OK;
  4476. +}
  4477. +
  4478. +void
  4479. +nsNativeMenuDocListener::AttributeChanged(nsIDocument *aDocument,
  4480. +                                          mozilla::dom::Element *aElement,
  4481. +                                          int32_t aNameSpaceID,
  4482. +                                          nsIAtom *aAttribute,
  4483. +                                          int32_t aModType)
  4484. +{
  4485. +    if (sUpdateDepth == 0) {
  4486. +        DoAttributeChanged(aElement, aAttribute);
  4487. +        return;
  4488. +    }
  4489. +
  4490. +    MutationRecord *m = *mPendingMutations.AppendElement(new MutationRecord);
  4491. +    m->mType = MutationRecord::eAttributeChanged;
  4492. +    m->mTarget = aElement;
  4493. +    m->mAttribute = aAttribute;
  4494. +
  4495. +    ScheduleFlush(this);
  4496. +}
  4497. +
  4498. +void
  4499. +nsNativeMenuDocListener::ContentAppended(nsIDocument *aDocument,
  4500. +                                         nsIContent *aContainer,
  4501. +                                         nsIContent *aFirstNewContent,
  4502. +                                         int32_t aNewIndexInContainer)
  4503. +{
  4504. +    for (nsIContent *c = aFirstNewContent; c; c = c->GetNextSibling()) {
  4505. +        ContentInserted(aDocument, aContainer, c, 0);
  4506. +    }
  4507. +}
  4508. +
  4509. +void
  4510. +nsNativeMenuDocListener::ContentInserted(nsIDocument *aDocument,
  4511. +                                         nsIContent *aContainer,
  4512. +                                         nsIContent *aChild,
  4513. +                                         int32_t aIndexInContainer)
  4514. +{
  4515. +    nsIContent *prevSibling = nsMenuContainer::GetPreviousSupportedSibling(aChild);
  4516. +
  4517. +    if (sUpdateDepth == 0) {
  4518. +        DoContentInserted(aContainer, aChild, prevSibling);
  4519. +        return;
  4520. +    }
  4521. +
  4522. +    MutationRecord *m = *mPendingMutations.AppendElement(new MutationRecord);
  4523. +    m->mType = MutationRecord::eContentInserted;
  4524. +    m->mTarget = aContainer;
  4525. +    m->mChild = aChild;
  4526. +    m->mPrevSibling = prevSibling;
  4527. +
  4528. +    ScheduleFlush(this);
  4529. +}
  4530. +
  4531. +void
  4532. +nsNativeMenuDocListener::ContentRemoved(nsIDocument *aDocument,
  4533. +                                        nsIContent *aContainer,
  4534. +                                        nsIContent *aChild,
  4535. +                                        int32_t aIndexInContainer,
  4536. +                                        nsIContent *aPreviousSibling)
  4537. +{
  4538. +    if (sUpdateDepth == 0) {
  4539. +        DoContentRemoved(aContainer, aChild);
  4540. +        return;
  4541. +    }
  4542. +
  4543. +    MutationRecord *m = *mPendingMutations.AppendElement(new MutationRecord);
  4544. +    m->mType = MutationRecord::eContentRemoved;
  4545. +    m->mTarget = aContainer;
  4546. +    m->mChild = aChild;
  4547. +
  4548. +    ScheduleFlush(this);
  4549. +}                                                          
  4550. +
  4551. +void
  4552. +nsNativeMenuDocListener::NodeWillBeDestroyed(const nsINode *aNode)
  4553. +{
  4554. +    mDocument = nullptr;
  4555. +}
  4556. +
  4557. +/* static */ already_AddRefed<nsNativeMenuDocListener>
  4558. +nsNativeMenuDocListener::Create(nsIContent *aRootNode)
  4559. +{
  4560. +    nsRefPtr<nsNativeMenuDocListener> listener = new nsNativeMenuDocListener();
  4561. +    if (NS_FAILED(listener->Init(aRootNode))) {
  4562. +        return nullptr;
  4563. +    }
  4564. +
  4565. +    return listener.forget();
  4566. +}
  4567. +
  4568. +void
  4569. +nsNativeMenuDocListener::RegisterForContentChanges(nsIContent *aContent,
  4570. +                                                   nsNativeMenuChangeObserver *aObserver)
  4571. +{
  4572. +    NS_ASSERTION(aContent, "Need content parameter");
  4573. +    NS_ASSERTION(aObserver, "Need observer parameter");
  4574. +    if (!aContent || !aObserver) {
  4575. +        return;
  4576. +    }
  4577. +
  4578. +    DebugOnly<nsNativeMenuChangeObserver *> old;
  4579. +    NS_ASSERTION(!mContentToObserverTable.Get(aContent, &old) || old == aObserver,
  4580. +                 "Multiple observers for the same content node are not supported");
  4581. +
  4582. +    mContentToObserverTable.Put(aContent, aObserver);
  4583. +}
  4584. +
  4585. +void
  4586. +nsNativeMenuDocListener::UnregisterForContentChanges(nsIContent *aContent)
  4587. +{
  4588. +    NS_ASSERTION(aContent, "Need content parameter");
  4589. +    if (!aContent) {
  4590. +        return;
  4591. +    }
  4592. +
  4593. +    mContentToObserverTable.Remove(aContent);
  4594. +    if (aContent == mLastSource) {
  4595. +        mLastSource = nullptr;
  4596. +        mLastTarget = nullptr;
  4597. +    }
  4598. +}
  4599. +
  4600. +void
  4601. +nsNativeMenuDocListener::Start()
  4602. +{
  4603. +    if (mDocument) {
  4604. +        return;
  4605. +    }
  4606. +
  4607. +    mDocument = mRootNode->OwnerDoc();
  4608. +    if (!mDocument) {
  4609. +        return;
  4610. +    }
  4611. +
  4612. +    mDocument->AddMutationObserver(this);
  4613. +}
  4614. +
  4615. +void
  4616. +nsNativeMenuDocListener::Stop()
  4617. +{
  4618. +    if (mDocument) {
  4619. +        mDocument->RemoveMutationObserver(this);
  4620. +        mDocument = nullptr;
  4621. +    }
  4622. +
  4623. +    CancelFlush(this);
  4624. +    mPendingMutations.Clear();
  4625. +}
  4626. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuDocListener.h
  4627. ===================================================================
  4628. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  4629. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuDocListener.h    2014-12-08 19:19:07.478645113 +0000
  4630. @@ -0,0 +1,152 @@
  4631. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  4632. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  4633. + */
  4634. +/* This Source Code Form is subject to the terms of the Mozilla Public
  4635. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  4636. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4637. +
  4638. +#ifndef __nsNativeMenuDocListener_h__
  4639. +#define __nsNativeMenuDocListener_h__
  4640. +
  4641. +#include "mozilla/Attributes.h"
  4642. +#include "mozilla/GuardObjects.h"
  4643. +#include "nsAutoPtr.h"
  4644. +#include "nsDataHashtable.h"
  4645. +#include "nsStubMutationObserver.h"
  4646. +#include "nsTArray.h"
  4647. +
  4648. +class nsIAtom;
  4649. +class nsIContent;
  4650. +class nsIDocument;
  4651. +class nsNativeMenuChangeObserver;
  4652. +
  4653. +/*
  4654. + * This class keeps a mapping of content nodes to observers and forwards DOM
  4655. + * mutations to these. There is exactly one of these for every menubar.
  4656. + */
  4657. +class nsNativeMenuDocListener MOZ_FINAL : nsStubMutationObserver
  4658. +{
  4659. +public:
  4660. +    NS_DECL_ISUPPORTS
  4661. +    NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
  4662. +    NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
  4663. +    NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
  4664. +    NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
  4665. +    NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
  4666. +
  4667. +    static already_AddRefed<nsNativeMenuDocListener> Create(nsIContent *aRootNode);
  4668. +
  4669. +    // Register an observer to receive mutation events for the specified
  4670. +    // content node. The caller must keep the observer alive until
  4671. +    // UnregisterForContentChanges is called.
  4672. +    void RegisterForContentChanges(nsIContent *aContent,
  4673. +                                   nsNativeMenuChangeObserver *aObserver);
  4674. +
  4675. +    // Unregister the registered observer for the specified content node
  4676. +    void UnregisterForContentChanges(nsIContent *aContent);
  4677. +
  4678. +    // Start listening to the document and forwarding DOM mutations to
  4679. +    // registered observers.
  4680. +    void Start();
  4681. +
  4682. +    // Stop listening to the document. No DOM mutations will be forwarded
  4683. +    // to registered observers.
  4684. +    void Stop();
  4685. +
  4686. +private:
  4687. +    friend class nsNativeMenuAutoUpdateBatch;
  4688. +    friend class DispatchHelper;
  4689. +
  4690. +    struct MutationRecord {
  4691. +        enum RecordType {
  4692. +            eAttributeChanged,
  4693. +            eContentInserted,
  4694. +            eContentRemoved
  4695. +        } mType;
  4696. +
  4697. +        nsCOMPtr<nsIContent> mTarget;
  4698. +        nsCOMPtr<nsIContent> mChild;
  4699. +        nsCOMPtr<nsIContent> mPrevSibling;
  4700. +        nsCOMPtr<nsIAtom> mAttribute;
  4701. +    };
  4702. +
  4703. +    nsNativeMenuDocListener();
  4704. +    ~nsNativeMenuDocListener();
  4705. +    nsresult Init(nsIContent *aRootNode);
  4706. +
  4707. +    void DoAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute);
  4708. +    void DoContentInserted(nsIContent *aContainer,
  4709. +                           nsIContent *aChild,
  4710. +                           nsIContent *aPrevSibling);
  4711. +    void DoContentRemoved(nsIContent *aContainer, nsIContent *aChild);
  4712. +    void DoBeginUpdateBatch(nsIContent *aTarget);
  4713. +    void DoEndUpdateBatch(nsIContent *aTarget);
  4714. +    void FlushPendingMutations();
  4715. +    static void ScheduleFlush(nsNativeMenuDocListener *aListener);
  4716. +    static void CancelFlush(nsNativeMenuDocListener *aListener);
  4717. +    static void BeginUpdates() { ++sUpdateDepth; }
  4718. +    static void EndUpdates();
  4719. +
  4720. +    nsCOMPtr<nsIContent> mRootNode;
  4721. +    nsIDocument *mDocument;
  4722. +    nsIContent *mLastSource;
  4723. +    nsNativeMenuChangeObserver *mLastTarget;
  4724. +    nsTArray<nsAutoPtr<MutationRecord> > mPendingMutations;
  4725. +    nsDataHashtable<nsPtrHashKey<nsIContent>, nsNativeMenuChangeObserver*> mContentToObserverTable;
  4726. +
  4727. +    static uint32_t sUpdateDepth;
  4728. +};
  4729. +
  4730. +typedef nsTArray<nsRefPtr<nsNativeMenuDocListener> > nsNativeMenuDocListenerTArray;
  4731. +
  4732. +class nsNativeMenuChangeObserver
  4733. +{
  4734. +public:
  4735. +    virtual void OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute)
  4736. +    {
  4737. +        NS_ERROR("Unhandled AttributeChanged() notification");
  4738. +    }
  4739. +
  4740. +    virtual void OnContentInserted(nsIContent *aContainer,
  4741. +                                   nsIContent *aChild,
  4742. +                                   nsIContent *aPrevSibling)
  4743. +    {
  4744. +        NS_ERROR("Unhandled ContentInserted() notification");
  4745. +    }
  4746. +
  4747. +    virtual void OnContentRemoved(nsIContent *aContainer, nsIContent *aChild)
  4748. +    {
  4749. +        NS_ERROR("Unhandled ContentRemoved() notification");
  4750. +    }
  4751. +
  4752. +    virtual void BeginUpdateBatch(nsIContent *aContent) { };
  4753. +
  4754. +    virtual void EndUpdateBatch() { };
  4755. +};
  4756. +
  4757. +/*
  4758. + * This class is intended to be used inside GObject signal handlers.
  4759. + * It allows us to queue updates until we have finished delivering
  4760. + * events to Gecko, and then we can batch updates to our view of the
  4761. + * menu. This allows us to do menu updates without altering the structure
  4762. + * seen by the OS.
  4763. + */
  4764. +class MOZ_STACK_CLASS nsNativeMenuAutoUpdateBatch
  4765. +{
  4766. +public:
  4767. +    nsNativeMenuAutoUpdateBatch(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
  4768. +    {
  4769. +        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
  4770. +        nsNativeMenuDocListener::BeginUpdates();
  4771. +    }
  4772. +
  4773. +    ~nsNativeMenuAutoUpdateBatch()
  4774. +    {
  4775. +        nsNativeMenuDocListener::EndUpdates();
  4776. +    }
  4777. +
  4778. +private:
  4779. +    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
  4780. +};
  4781. +
  4782. +#endif /* __nsNativeMenuDocListener_h__ */
  4783. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuService.cpp
  4784. ===================================================================
  4785. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  4786. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuService.cpp  2014-12-08 19:19:07.478645113 +0000
  4787. @@ -0,0 +1,506 @@
  4788. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  4789. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  4790. + */
  4791. +/* This Source Code Form is subject to the terms of the Mozilla Public
  4792. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  4793. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4794. +
  4795. +#include "mozilla/Preferences.h"
  4796. +#include "mozilla/Services.h"
  4797. +#include "nsAutoPtr.h"
  4798. +#include "nsCOMPtr.h"
  4799. +#include "nsCRT.h"
  4800. +#include "nsGtkUtils.h"
  4801. +#include "nsIContent.h"
  4802. +#include "nsIObserverService.h"
  4803. +#include "nsIWidget.h"
  4804. +#include "nsServiceManagerUtils.h"
  4805. +#include "nsWindow.h"
  4806. +#include "nsXPCOM.h"
  4807. +#include "prlink.h"
  4808. +
  4809. +#include "nsDbusmenu.h"
  4810. +#include "nsMenuBar.h"
  4811. +#include "nsNativeMenuAtoms.h"
  4812. +#include "nsNativeMenuDocListener.h"
  4813. +
  4814. +#include <glib-object.h>
  4815. +#include <pango/pango.h>
  4816. +#include <stdlib.h>
  4817. +
  4818. +#include "nsNativeMenuService.h"
  4819. +
  4820. +using namespace mozilla;
  4821. +
  4822. +nsNativeMenuService* nsNativeMenuService::sService = nullptr;
  4823. +bool nsNativeMenuService::sShutdown = false;
  4824. +
  4825. +extern PangoLayout* gPangoLayout;
  4826. +extern nsNativeMenuDocListenerTArray* gPendingListeners;
  4827. +
  4828. +static const nsTArray<nsMenuBar *>::index_type NoIndex = nsTArray<nsMenuBar *>::NoIndex;
  4829. +
  4830. +#if not GLIB_CHECK_VERSION(2,26,0)
  4831. +enum GBusType {
  4832. +    G_BUS_TYPE_STARTER = -1,
  4833. +    G_BUS_TYPE_NONE = 0,
  4834. +    G_BUS_TYPE_SYSTEM = 1,
  4835. +    G_BUS_TYPE_SESSION = 2
  4836. +};
  4837. +
  4838. +enum GDBusProxyFlags {
  4839. +    G_DBUS_PROXY_FLAGS_NONE = 0,
  4840. +    G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = 1 << 0,
  4841. +    G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = 1 << 1,
  4842. +    G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START = 1 << 2,
  4843. +    G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES = 1 << 3
  4844. +};
  4845. +
  4846. +enum GDBusCallFlags {
  4847. +    G_DBUS_CALL_FLAGS_NONE = 0,
  4848. +    G_DBUS_CALL_FLAGS_NO_AUTO_START = 1 << 0
  4849. +};
  4850. +
  4851. +typedef _GDBusInterfaceInfo GDBusInterfaceInfo;
  4852. +typedef _GDBusProxy GDBusProxy;
  4853. +typedef _GVariant GVariant;
  4854. +#endif
  4855. +
  4856. +#undef g_dbus_proxy_new_for_bus
  4857. +#undef g_dbus_proxy_new_for_bus_finish
  4858. +#undef g_dbus_proxy_call
  4859. +#undef g_dbus_proxy_call_finish
  4860. +#undef g_dbus_proxy_get_name_owner
  4861. +
  4862. +typedef void (*_g_dbus_proxy_new_for_bus_fn)(GBusType, GDBusProxyFlags,
  4863. +                                             GDBusInterfaceInfo*,
  4864. +                                             const gchar*, const gchar*,
  4865. +                                             const gchar*, GCancellable*,
  4866. +                                             GAsyncReadyCallback, gpointer);
  4867. +
  4868. +typedef GDBusProxy* (*_g_dbus_proxy_new_for_bus_finish_fn)(GAsyncResult*,
  4869. +                                                           GError**);
  4870. +typedef void (*_g_dbus_proxy_call_fn)(GDBusProxy*, const gchar*, GVariant*,
  4871. +                                      GDBusCallFlags, gint, GCancellable*,
  4872. +                                      GAsyncReadyCallback, gpointer);
  4873. +typedef GVariant* (*_g_dbus_proxy_call_finish_fn)(GDBusProxy*, GAsyncResult*,
  4874. +                                                  GError**);
  4875. +typedef gchar* (*_g_dbus_proxy_get_name_owner_fn)(GDBusProxy*);
  4876. +
  4877. +static _g_dbus_proxy_new_for_bus_fn _g_dbus_proxy_new_for_bus;
  4878. +static _g_dbus_proxy_new_for_bus_finish_fn _g_dbus_proxy_new_for_bus_finish;
  4879. +static _g_dbus_proxy_call_fn _g_dbus_proxy_call;
  4880. +static _g_dbus_proxy_call_finish_fn _g_dbus_proxy_call_finish;
  4881. +static _g_dbus_proxy_get_name_owner_fn _g_dbus_proxy_get_name_owner;
  4882. +
  4883. +#define g_dbus_proxy_new_for_bus _g_dbus_proxy_new_for_bus
  4884. +#define g_dbus_proxy_new_for_bus_finish _g_dbus_proxy_new_for_bus_finish
  4885. +#define g_dbus_proxy_call _g_dbus_proxy_call
  4886. +#define g_dbus_proxy_call_finish _g_dbus_proxy_call_finish
  4887. +#define g_dbus_proxy_get_name_owner _g_dbus_proxy_get_name_owner
  4888. +
  4889. +static PRLibrary *gGIOLib = nullptr;
  4890. +
  4891. +static nsresult
  4892. +GDBusInit()
  4893. +{
  4894. +    gGIOLib = PR_LoadLibrary("libgio-2.0.so.0");
  4895. +    if (!gGIOLib) {
  4896. +        return NS_ERROR_FAILURE;
  4897. +    }
  4898. +
  4899. +    g_dbus_proxy_new_for_bus = (_g_dbus_proxy_new_for_bus_fn)PR_FindFunctionSymbol(gGIOLib, "g_dbus_proxy_new_for_bus");
  4900. +    g_dbus_proxy_new_for_bus_finish = (_g_dbus_proxy_new_for_bus_finish_fn)PR_FindFunctionSymbol(gGIOLib, "g_dbus_proxy_new_for_bus_finish");
  4901. +    g_dbus_proxy_call = (_g_dbus_proxy_call_fn)PR_FindFunctionSymbol(gGIOLib, "g_dbus_proxy_call");
  4902. +    g_dbus_proxy_call_finish = (_g_dbus_proxy_call_finish_fn)PR_FindFunctionSymbol(gGIOLib, "g_dbus_proxy_call_finish");
  4903. +    g_dbus_proxy_get_name_owner = (_g_dbus_proxy_get_name_owner_fn)PR_FindFunctionSymbol(gGIOLib, "g_dbus_proxy_get_name_owner");
  4904. +
  4905. +    if (!g_dbus_proxy_new_for_bus ||
  4906. +        !g_dbus_proxy_new_for_bus_finish ||
  4907. +        !g_dbus_proxy_call ||
  4908. +        !g_dbus_proxy_call_finish ||
  4909. +        !g_dbus_proxy_get_name_owner) {
  4910. +        return NS_ERROR_FAILURE;
  4911. +    }
  4912. +
  4913. +    return NS_OK;
  4914. +}
  4915. +
  4916. +NS_IMPL_ISUPPORTS(nsNativeMenuService, nsINativeMenuService, nsIObserver)
  4917. +
  4918. +/* static */ void
  4919. +nsNativeMenuService::EnsureInitialized()
  4920. +{
  4921. +    if (!sService) {
  4922. +        nsCOMPtr<nsINativeMenuService> service =
  4923. +            do_GetService("@mozilla.org/widget/nativemenuservice;1");
  4924. +    }
  4925. +    NS_ASSERTION(sService != nullptr || sShutdown == true,
  4926. +                 "Failed to create native menu service");
  4927. +}
  4928. +
  4929. +void
  4930. +nsNativeMenuService::SetOnline(bool aOnline)
  4931. +{
  4932. +    if (!Preferences::GetBool("ui.use_unity_menubar", true)) {
  4933. +        aOnline = false;
  4934. +    }
  4935. +
  4936. +    mOnline = aOnline;
  4937. +    if (aOnline) {
  4938. +        for (uint32_t i = 0; i < mMenuBars.Length(); ++i) {
  4939. +            RegisterNativeMenuBar(mMenuBars[i]);
  4940. +        }
  4941. +    } else {
  4942. +        for (uint32_t i = 0; i < mMenuBars.Length(); ++i) {
  4943. +            mMenuBars[i]->Deactivate();
  4944. +        }
  4945. +    }
  4946. +}
  4947. +
  4948. +void
  4949. +nsNativeMenuService::RegisterNativeMenuBar(nsMenuBar *aMenuBar)
  4950. +{
  4951. +    if (!mOnline) {
  4952. +        return;
  4953. +    }
  4954. +
  4955. +    // This will effectively create the native menubar for
  4956. +    // exporting over the session bus, and hide the XUL menubar
  4957. +    aMenuBar->Activate();
  4958. +
  4959. +    if (!mDbusProxy ||
  4960. +        !gtk_widget_get_mapped(aMenuBar->TopLevelWindow()) ||
  4961. +        aMenuBar->RegisterRequestInProgress()) {
  4962. +        // Don't go further if we don't have a proxy for the shell menu
  4963. +        // service, the window isn't mapped or there is a request in progress.
  4964. +        return;
  4965. +    }
  4966. +
  4967. +    uint32_t xid = aMenuBar->WindowId();
  4968. +    nsAdoptingCString path = aMenuBar->ObjectPath();
  4969. +    if (xid == 0 || path.IsEmpty()) {
  4970. +        NS_WARNING("Menubar has invalid XID or object path");
  4971. +        return;
  4972. +    }
  4973. +
  4974. +    // We keep a weak ref because we can't assume that GDBus cancellation
  4975. +    // is reliable (see https://launchpad.net/bugs/953562)
  4976. +    nsAutoWeakMenuObject<nsMenuBar> weakMenuBar(aMenuBar);
  4977. +
  4978. +    g_dbus_proxy_call(mDbusProxy, "RegisterWindow",
  4979. +                      g_variant_new("(uo)", xid, path.get()),
  4980. +                      G_DBUS_CALL_FLAGS_NONE, -1,
  4981. +                      aMenuBar->BeginRegisterRequest(),
  4982. +                      register_native_menubar_cb, weakMenuBar.forget());
  4983. +}
  4984. +
  4985. +/* static */ void
  4986. +nsNativeMenuService::name_owner_changed_cb(GObject *gobject,
  4987. +                                           GParamSpec *pspec,
  4988. +                                           gpointer user_data)
  4989. +{
  4990. +    nsNativeMenuService::GetSingleton()->OnNameOwnerChanged();
  4991. +}
  4992. +
  4993. +/* static */ void
  4994. +nsNativeMenuService::proxy_created_cb(GObject *source_object,
  4995. +                                      GAsyncResult *res,
  4996. +                                      gpointer user_data)
  4997. +{
  4998. +    GError *error = nullptr;
  4999. +    GDBusProxy *proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
  5000. +    if (error && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
  5001. +        g_error_free(error);
  5002. +        return;
  5003. +    }
  5004. +
  5005. +    if (error) {
  5006. +        g_error_free(error);
  5007. +    }
  5008. +
  5009. +    // We need this check because we can't assume that GDBus cancellation
  5010. +    // is reliable (see https://launchpad.net/bugs/953562)
  5011. +    if (sShutdown) {
  5012. +        if (proxy) {
  5013. +            g_object_unref(proxy);
  5014. +        }
  5015. +        return;
  5016. +    }
  5017. +
  5018. +    nsNativeMenuService::GetSingleton()->OnProxyCreated(proxy);
  5019. +}
  5020. +
  5021. +/* static */ void
  5022. +nsNativeMenuService::register_native_menubar_cb(GObject *source_object,
  5023. +                                                GAsyncResult *res,
  5024. +                                                gpointer user_data)
  5025. +{
  5026. +    nsAutoWeakMenuObject<nsMenuBar> weakMenuBar(
  5027. +        static_cast<nsWeakMenuObject<nsMenuBar> *>(user_data));
  5028. +
  5029. +    GError *error = nullptr;
  5030. +    GVariant *results = g_dbus_proxy_call_finish(G_DBUS_PROXY(source_object),
  5031. +                                                 res, &error);
  5032. +    if (results) {
  5033. +        // There's nothing useful in the response
  5034. +        g_variant_unref(results);
  5035. +    }
  5036. +
  5037. +    bool success = error ? false : true;
  5038. +    if (error && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
  5039. +        g_error_free(error);
  5040. +        return;
  5041. +    }
  5042. +
  5043. +    if (error) {
  5044. +        g_error_free(error);
  5045. +    }
  5046. +
  5047. +    if (sShutdown || !weakMenuBar) {
  5048. +        return;
  5049. +    }
  5050. +
  5051. +    nsNativeMenuService::GetSingleton()->OnNativeMenuBarRegistered(weakMenuBar,
  5052. +                                                                   success);
  5053. +}
  5054. +
  5055. +/* static */ gboolean
  5056. +nsNativeMenuService::map_event_cb(GtkWidget *widget,
  5057. +                                  GdkEvent *event,
  5058. +                                  gpointer user_data)
  5059. +{
  5060. +    nsMenuBar *menubar = static_cast<nsMenuBar *>(user_data);
  5061. +    nsNativeMenuService::GetSingleton()->RegisterNativeMenuBar(menubar);
  5062. +
  5063. +    return FALSE;
  5064. +}
  5065. +
  5066. +void
  5067. +nsNativeMenuService::OnNameOwnerChanged()
  5068. +{
  5069. +    char *owner = g_dbus_proxy_get_name_owner(mDbusProxy);
  5070. +    SetOnline(owner ? true : false);
  5071. +    g_free(owner);
  5072. +}
  5073. +
  5074. +void
  5075. +nsNativeMenuService::OnProxyCreated(GDBusProxy *aProxy)
  5076. +{
  5077. +    mDbusProxy = aProxy;
  5078. +    mCreateProxyRequest.Finish();
  5079. +
  5080. +    if (!mDbusProxy) {
  5081. +        SetOnline(false);
  5082. +        return;
  5083. +    }
  5084. +
  5085. +    g_signal_connect(mDbusProxy, "notify::g-name-owner",
  5086. +                     G_CALLBACK(name_owner_changed_cb), nullptr);
  5087. +
  5088. +    OnNameOwnerChanged();
  5089. +}
  5090. +
  5091. +void
  5092. +nsNativeMenuService::OnNativeMenuBarRegistered(nsMenuBar *aMenuBar,
  5093. +                                               bool aSuccess)
  5094. +{
  5095. +    aMenuBar->EndRegisterRequest();
  5096. +    if (!aSuccess) {
  5097. +        aMenuBar->Deactivate();
  5098. +    }
  5099. +}
  5100. +
  5101. +/* static */ void
  5102. +nsNativeMenuService::PrefChangedCallback(const char *aPref,
  5103. +                                         void *aClosure)
  5104. +{
  5105. +    nsNativeMenuService::GetSingleton()->PrefChanged();
  5106. +}
  5107. +
  5108. +void
  5109. +nsNativeMenuService::PrefChanged()
  5110. +{
  5111. +    if (!mDbusProxy) {
  5112. +        SetOnline(false);
  5113. +        return;
  5114. +    }
  5115. +
  5116. +    OnNameOwnerChanged();
  5117. +}
  5118. +
  5119. +nsNativeMenuService::nsNativeMenuService() :
  5120. +    mDbusProxy(nullptr), mOnline(false)
  5121. +{
  5122. +}
  5123. +
  5124. +nsNativeMenuService::~nsNativeMenuService()
  5125. +{
  5126. +    SetOnline(false);
  5127. +
  5128. +    // Make sure we disconnect map-event handlers
  5129. +    while (mMenuBars.Length() > 0) {
  5130. +        NotifyNativeMenuBarDestroyed(mMenuBars[0]);
  5131. +    }
  5132. +
  5133. +    Preferences::UnregisterCallback(PrefChangedCallback,
  5134. +                                    "ui.use_unity_menubar");
  5135. +
  5136. +    if (mDbusProxy) {
  5137. +        g_signal_handlers_disconnect_by_func(mDbusProxy,
  5138. +                                             FuncToGpointer(name_owner_changed_cb),
  5139. +                                             NULL);
  5140. +        g_object_unref(mDbusProxy);
  5141. +    }
  5142. +
  5143. +    if (gPendingListeners) {
  5144. +        delete gPendingListeners;
  5145. +        gPendingListeners = nullptr;
  5146. +    }
  5147. +    if (gPangoLayout) {
  5148. +        g_object_unref(gPangoLayout);
  5149. +        gPangoLayout = nullptr;
  5150. +    }
  5151. +}
  5152. +
  5153. +nsresult
  5154. +nsNativeMenuService::Init()
  5155. +{
  5156. +    nsresult rv = nsDbusmenuFunctions::Init();
  5157. +    if (NS_FAILED(rv)) {
  5158. +        return rv;
  5159. +    }
  5160. +
  5161. +    rv = GDBusInit();
  5162. +    if (NS_FAILED(rv)) {
  5163. +        return rv;
  5164. +    }
  5165. +
  5166. +    Preferences::RegisterCallback(PrefChangedCallback,
  5167. +                                  "ui.use_unity_menubar");
  5168. +
  5169. +    mCreateProxyRequest.Start();
  5170. +
  5171. +    g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION,
  5172. +                             static_cast<GDBusProxyFlags>(
  5173. +                             G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
  5174. +                             G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
  5175. +                             G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START),
  5176. +                             nullptr,
  5177. +                             "com.canonical.AppMenu.Registrar",
  5178. +                             "/com/canonical/AppMenu/Registrar",
  5179. +                             "com.canonical.AppMenu.Registrar",
  5180. +                             mCreateProxyRequest, proxy_created_cb,
  5181. +                             nullptr);
  5182. +
  5183. +    /* We don't technically know that the shell will draw the menubar until
  5184. +     * we know whether anybody owns the name of the menubar service on the
  5185. +     * session bus. However, discovering this happens asynchronously so
  5186. +     * we optimize for the common case here by assuming that the shell will
  5187. +     * draw window menubars if we are running inside Unity. This should
  5188. +     * mean that we avoid temporarily displaying the window menubar ourselves
  5189. +     */
  5190. +    const char *desktop = getenv("XDG_CURRENT_DESKTOP");
  5191. +    if (nsCRT::strcmp(desktop, "Unity") == 0) {
  5192. +        SetOnline(true);
  5193. +    }
  5194. +
  5195. +    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
  5196. +    if (os) {
  5197. +        os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
  5198. +    }
  5199. +
  5200. +    return NS_OK;
  5201. +}
  5202. +
  5203. +/* static */ already_AddRefed<nsNativeMenuService>
  5204. +nsNativeMenuService::GetInstance()
  5205. +{
  5206. +    nsRefPtr<nsNativeMenuService> service(sService);
  5207. +
  5208. +    if (service) {
  5209. +        return service.forget();
  5210. +    }
  5211. +
  5212. +    NS_ASSERTION(sShutdown == false,
  5213. +                 "Attempted to access menubar service too late");
  5214. +
  5215. +    if (sShutdown) {
  5216. +        return nullptr;
  5217. +    }
  5218. +
  5219. +    sService = new nsNativeMenuService();
  5220. +    NS_ADDREF(sService);
  5221. +
  5222. +    if (NS_FAILED(sService->Init())) {
  5223. +        NS_RELEASE(sService);
  5224. +        sService = nullptr;
  5225. +        return nullptr;
  5226. +    }
  5227. +
  5228. +    service = sService;
  5229. +    return service.forget();
  5230. +}
  5231. +
  5232. +/* static */ nsNativeMenuService*
  5233. +nsNativeMenuService::GetSingleton()
  5234. +{
  5235. +    EnsureInitialized();
  5236. +    return sService;
  5237. +}
  5238. +
  5239. +NS_IMETHODIMP
  5240. +nsNativeMenuService::Observe(nsISupports *aSubject,
  5241. +                             const char *aTopic,
  5242. +                             const char16_t *aData)
  5243. +{
  5244. +    if (!nsCRT::strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
  5245. +        sShutdown = true;
  5246. +        nsCOMPtr<nsIObserverService> os = services::GetObserverService();
  5247. +        if (os) {
  5248. +            os->RemoveObserver(sService, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
  5249. +        }
  5250. +        NS_IF_RELEASE(sService);
  5251. +    }
  5252. +
  5253. +    return NS_OK;
  5254. +}
  5255. +
  5256. +void
  5257. +nsNativeMenuService::NotifyNativeMenuBarDestroyed(nsMenuBar *aMenuBar)
  5258. +{
  5259. +    g_signal_handlers_disconnect_by_func(aMenuBar->TopLevelWindow(),
  5260. +                                         FuncToGpointer(map_event_cb),
  5261. +                                         aMenuBar);
  5262. +
  5263. +    mMenuBars.RemoveElement(aMenuBar);
  5264. +}
  5265. +
  5266. +NS_IMETHODIMP
  5267. +nsNativeMenuService::CreateNativeMenuBar(nsIWidget *aParent,
  5268. +                                         nsIContent *aMenuBarNode)
  5269. +{
  5270. +    NS_ENSURE_ARG(aParent);
  5271. +    NS_ENSURE_ARG(aMenuBarNode);
  5272. +
  5273. +    nsAutoPtr<nsMenuBar> menubar(nsMenuBar::Create(aParent, aMenuBarNode));
  5274. +    if (!menubar) {
  5275. +        NS_WARNING("Failed to create menubar");
  5276. +        return NS_ERROR_FAILURE;
  5277. +    }
  5278. +
  5279. +    // Unity forgets our window if it is unmapped by the application, which
  5280. +    // happens with some extensions that add "minimize to tray" type
  5281. +    // functionality. We hook on to the MapNotify event to re-register our menu
  5282. +    // with Unity
  5283. +    g_signal_connect(G_OBJECT(menubar->TopLevelWindow()),
  5284. +                     "map-event", G_CALLBACK(map_event_cb),
  5285. +                     menubar);
  5286. +
  5287. +    mMenuBars.AppendElement(menubar);
  5288. +    RegisterNativeMenuBar(menubar);
  5289. +
  5290. +    static_cast<nsWindow *>(aParent)->SetMenuBar(menubar.forget());
  5291. +
  5292. +    return NS_OK;
  5293. +}
  5294. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuService.h
  5295. ===================================================================
  5296. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  5297. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuService.h    2014-12-08 19:19:07.478645113 +0000
  5298. @@ -0,0 +1,88 @@
  5299. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  5300. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  5301. + */
  5302. +/* This Source Code Form is subject to the terms of the Mozilla Public
  5303. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  5304. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5305. +
  5306. +#ifndef __nsNativeMenuService_h__
  5307. +#define __nsNativeMenuService_h__
  5308. +
  5309. +#include "mozilla/Attributes.h"
  5310. +#include "nsCOMPtr.h"
  5311. +#include "nsINativeMenuService.h"
  5312. +#include "nsIObserver.h"
  5313. +#include "nsTArray.h"
  5314. +
  5315. +#include "nsNativeMenuUtils.h"
  5316. +
  5317. +#include <gdk/gdk.h>
  5318. +#include <gio/gio.h>
  5319. +#include <gtk/gtk.h>
  5320. +
  5321. +class nsMenuBar;
  5322. +
  5323. +/*
  5324. + * The main native menu service singleton. nsWebShellWindow calls in to this when
  5325. + * a new top level window is created.
  5326. + *
  5327. + * Menubars are owned by their nsWindow. This service holds a weak reference to
  5328. + * each menubar for the purpose of re-registering them with the shell if it
  5329. + * needs to. The menubar is responsible for notifying the service when the last
  5330. + * reference to it is dropped.
  5331. + */
  5332. +class nsNativeMenuService MOZ_FINAL : public nsINativeMenuService,
  5333. +                                      public nsIObserver
  5334. +{
  5335. +public:
  5336. +    NS_DECL_ISUPPORTS
  5337. +    NS_DECL_NSIOBSERVER
  5338. +
  5339. +    NS_IMETHOD CreateNativeMenuBar(nsIWidget* aParent, nsIContent* aMenuBarNode);
  5340. +
  5341. +    nsresult Init();
  5342. +
  5343. +    // Returns the singleton addref'd for the service manager
  5344. +    static already_AddRefed<nsNativeMenuService> GetInstance();
  5345. +
  5346. +    // Returns the singleton without increasing the reference count
  5347. +    static nsNativeMenuService* GetSingleton();
  5348. +
  5349. +    // Called by a menubar when the last reference to it is dropped
  5350. +    void NotifyNativeMenuBarDestroyed(nsMenuBar *aMenuBar);
  5351. +
  5352. +private:
  5353. +    nsNativeMenuService();
  5354. +    ~nsNativeMenuService();
  5355. +
  5356. +    static void EnsureInitialized();
  5357. +    void SetOnline(bool aOnline);
  5358. +    void RegisterNativeMenuBar(nsMenuBar *aMenuBar);
  5359. +    static void name_owner_changed_cb(GObject *gobject,
  5360. +                                      GParamSpec *pspec,
  5361. +                                      gpointer user_data);
  5362. +    static void proxy_created_cb(GObject *source_object,
  5363. +                                 GAsyncResult *res,
  5364. +                                 gpointer user_data);
  5365. +    static void register_native_menubar_cb(GObject *source_object,
  5366. +                                           GAsyncResult *res,
  5367. +                                           gpointer user_data);
  5368. +    static gboolean map_event_cb(GtkWidget *widget, GdkEvent *event,
  5369. +                                 gpointer user_data);
  5370. +    void OnNameOwnerChanged();
  5371. +    void OnProxyCreated(GDBusProxy *aProxy);
  5372. +    void OnNativeMenuBarRegistered(nsMenuBar *aMenuBar,
  5373. +                                   bool aSuccess);
  5374. +    static void PrefChangedCallback(const char *aPref, void *aClosure);
  5375. +    void PrefChanged();
  5376. +
  5377. +    nsNativeMenuGIORequest mCreateProxyRequest;
  5378. +    GDBusProxy *mDbusProxy;
  5379. +    bool mOnline;
  5380. +    nsTArray<nsMenuBar *> mMenuBars;
  5381. +
  5382. +    static bool sShutdown;
  5383. +    static nsNativeMenuService *sService;
  5384. +};
  5385. +
  5386. +#endif /* __nsNativeMenuService_h__ */
  5387. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuUtils.h
  5388. ===================================================================
  5389. --- /dev/null   1970-01-01 00:00:00.000000000 +0000
  5390. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsNativeMenuUtils.h  2014-12-08 19:19:07.478645113 +0000
  5391. @@ -0,0 +1,59 @@
  5392. +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  5393. +/* vim:expandtab:shiftwidth=4:tabstop=4:
  5394. + */
  5395. +/* This Source Code Form is subject to the terms of the Mozilla Public
  5396. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  5397. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5398. +
  5399. +#ifndef __nsNativeMenuUtils_h__
  5400. +#define __nsNativeMenuUtils_h__
  5401. +
  5402. +#include <glib-object.h>
  5403. +#include <gio/gio.h>
  5404. +
  5405. +class nsNativeMenuGIORequest
  5406. +{
  5407. +public:
  5408. +    nsNativeMenuGIORequest() : mCancellable(nullptr) { };
  5409. +
  5410. +    ~nsNativeMenuGIORequest() {
  5411. +        Cancel();
  5412. +    }
  5413. +
  5414. +    void Start() {
  5415. +        Cancel();
  5416. +        mCancellable = g_cancellable_new();
  5417. +    }
  5418. +
  5419. +    void Finish() {
  5420. +        if (mCancellable) {
  5421. +            g_object_unref(mCancellable);
  5422. +            mCancellable = nullptr;
  5423. +        }
  5424. +    }
  5425. +
  5426. +    void Cancel() {
  5427. +        if (mCancellable) {
  5428. +            g_cancellable_cancel(mCancellable);
  5429. +            g_object_unref(mCancellable);
  5430. +            mCancellable = nullptr;
  5431. +        }
  5432. +    }
  5433. +
  5434. +    bool InProgress() const {
  5435. +        if (!mCancellable) {
  5436. +            return false;
  5437. +        }
  5438. +
  5439. +        return !g_cancellable_is_cancelled(mCancellable);
  5440. +    }
  5441. +
  5442. +    operator GCancellable*() const {
  5443. +        return mCancellable;
  5444. +    }
  5445. +
  5446. +private:
  5447. +    GCancellable *mCancellable;
  5448. +};
  5449. +
  5450. +#endif /* __nsNativeMenuUtils_h__ */
  5451. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsWidgetFactory.cpp
  5452. ===================================================================
  5453. --- firefox-trunk-37.0~a1~hg20141208r218676.orig/widget/gtk/nsWidgetFactory.cpp 2014-12-08 19:19:07.486645126 +0000
  5454. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsWidgetFactory.cpp  2014-12-08 19:19:07.482645120 +0000
  5455. @@ -44,6 +44,9 @@
  5456.  #include "GfxInfoX11.h"
  5457.  #endif
  5458.  
  5459. +#include "nsNativeMenuService.h"
  5460. +#include "nsNativeMenuAtoms.h"
  5461. +
  5462.  #include "nsNativeThemeGTK.h"
  5463.  
  5464.  #include "nsIComponentRegistrar.h"
  5465. @@ -117,6 +120,9 @@
  5466.  }
  5467.  #endif
  5468.  
  5469. +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsNativeMenuService,
  5470. +                                         nsNativeMenuService::GetInstance)
  5471. +
  5472.  #ifdef NS_PRINTING
  5473.  NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecGTK)
  5474.  NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintOptionsGTK, Init)
  5475. @@ -197,6 +203,7 @@
  5476.  NS_DEFINE_NAMED_CID(NS_IDLE_SERVICE_CID);
  5477.  NS_DEFINE_NAMED_CID(NS_GFXINFO_CID);
  5478.  #endif
  5479. +NS_DEFINE_NAMED_CID(NS_NATIVEMENUSERVICE_CID);
  5480.  
  5481.  
  5482.  static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
  5483. @@ -229,6 +236,7 @@
  5484.      { &kNS_IDLE_SERVICE_CID, false, nullptr, nsIdleServiceGTKConstructor },
  5485.      { &kNS_GFXINFO_CID, false, nullptr, mozilla::widget::GfxInfoConstructor },
  5486.  #endif
  5487. +    { &kNS_NATIVEMENUSERVICE_CID, true, NULL, nsNativeMenuServiceConstructor },
  5488.      { nullptr }
  5489.  };
  5490.  
  5491. @@ -262,6 +270,7 @@
  5492.      { "@mozilla.org/widget/idleservice;1", &kNS_IDLE_SERVICE_CID },
  5493.      { "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID },
  5494.  #endif
  5495. +    { "@mozilla.org/widget/nativemenuservice;1", &kNS_NATIVEMENUSERVICE_CID },
  5496.      { nullptr }
  5497.  };
  5498.  
  5499. @@ -280,13 +289,22 @@
  5500.  #endif
  5501.  }
  5502.  
  5503. +static nsresult
  5504. +nsWidgetGtk2ModuleCtor()
  5505. +{
  5506. +  nsAppShellInit();
  5507. +  nsNativeMenuAtoms::Init();
  5508. +
  5509. +  return NS_OK;
  5510. +}
  5511. +
  5512.  static const mozilla::Module kWidgetModule = {
  5513.      mozilla::Module::kVersion,
  5514.      kWidgetCIDs,
  5515.      kWidgetContracts,
  5516.      nullptr,
  5517.      nullptr,
  5518. -    nsAppShellInit,
  5519. +    nsWidgetGtk2ModuleCtor,
  5520.      nsWidgetGtk2ModuleDtor
  5521.  };
  5522.  
  5523. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsWindow.cpp
  5524. ===================================================================
  5525. --- firefox-trunk-37.0~a1~hg20141208r218676.orig/widget/gtk/nsWindow.cpp    2014-12-08 19:19:07.486645126 +0000
  5526. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsWindow.cpp 2014-12-08 19:19:07.482645120 +0000
  5527. @@ -4778,6 +4778,11 @@
  5528.      return NS_OK;
  5529.  }
  5530.  
  5531. +void
  5532. +nsWindow::SetMenuBar(nsMenuBar *aMenuBar) {
  5533. +    mMenuBar = aMenuBar;
  5534. +}
  5535. +
  5536.  bool
  5537.  nsWindow::CheckForRollup(gdouble aMouseX, gdouble aMouseY,
  5538.                           bool aIsWheel, bool aAlwaysRollup)
  5539. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsWindow.h
  5540. ===================================================================
  5541. --- firefox-trunk-37.0~a1~hg20141208r218676.orig/widget/gtk/nsWindow.h  2014-12-08 19:19:07.486645126 +0000
  5542. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/nsWindow.h   2014-12-08 19:19:07.482645120 +0000
  5543. @@ -34,6 +34,8 @@
  5544.  
  5545.  #include "nsGtkIMModule.h"
  5546.  
  5547. +#include "nsMenuBar.h"
  5548. +
  5549.  #undef LOG
  5550.  #ifdef MOZ_LOGGING
  5551.  
  5552. @@ -149,6 +151,8 @@
  5553.      NS_IMETHOD         MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen = nullptr);
  5554.      NS_IMETHOD         HideWindowChrome(bool aShouldHide);
  5555.  
  5556. +    void               SetMenuBar(nsMenuBar *aMenuBar);
  5557. +
  5558.      /**
  5559.       * GetLastUserInputTime returns a timestamp for the most recent user input
  5560.       * event.  This is intended for pointer grab requests (including drags).
  5561. @@ -473,6 +477,8 @@
  5562.       * however, IME doesn't work at that time.
  5563.       */
  5564.      nsRefPtr<nsGtkIMModule> mIMModule;
  5565. +
  5566. +    nsAutoPtr<nsMenuBar> mMenuBar;
  5567.  };
  5568.  
  5569.  class nsChildWindow : public nsWindow {
  5570. Index: firefox-trunk-37.0~a1~hg20141208r218676/xpfe/appshell/nsWebShellWindow.cpp
  5571. ===================================================================
  5572. --- firefox-trunk-37.0~a1~hg20141208r218676.orig/xpfe/appshell/nsWebShellWindow.cpp 2014-12-08 19:19:07.486645126 +0000
  5573. +++ firefox-trunk-37.0~a1~hg20141208r218676/xpfe/appshell/nsWebShellWindow.cpp  2014-12-08 19:19:07.482645120 +0000
  5574. @@ -58,6 +58,7 @@
  5575.  #include "nsIScreen.h"
  5576.  
  5577.  #include "nsIContent.h" // for menus
  5578. +#include "nsIAtom.h"
  5579.  #include "nsIScriptSecurityManager.h"
  5580.  
  5581.  // For calculating size
  5582. @@ -71,7 +72,7 @@
  5583.  #include "mozilla/DebugOnly.h"
  5584.  #include "mozilla/MouseEvents.h"
  5585.  
  5586. -#ifdef XP_MACOSX
  5587. +#if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
  5588.  #include "nsINativeMenuService.h"
  5589.  #define USE_NATIVE_MENUS
  5590.  #endif
  5591. @@ -432,8 +433,13 @@
  5592.    if (!menubarNode)
  5593.      return;
  5594.  
  5595. -  nsCOMPtr<nsINativeMenuService> nms = do_GetService("@mozilla.org/widget/nativemenuservice;1");
  5596.    nsCOMPtr<nsIContent> menubarContent(do_QueryInterface(menubarNode));
  5597. +#ifdef MOZ_WIDGET_GTK
  5598. +  nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_CSTRING("_moz-menubarkeeplocal"));
  5599. +  if (menubarContent->AttrValueIs(kNameSpaceID_None, atom, nsGkAtoms::_true, eCaseMatters))
  5600. +    return;
  5601. +#endif
  5602. +  nsCOMPtr<nsINativeMenuService> nms = do_GetService("@mozilla.org/widget/nativemenuservice;1");
  5603.    if (nms && menubarContent)
  5604.      nms->CreateNativeMenuBar(aParentWindow, menubarContent);
  5605.  }
  5606. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/moz.build
  5607. ===================================================================
  5608. --- firefox-trunk-37.0~a1~hg20141208r218676.orig/widget/gtk/moz.build   2014-12-08 19:19:07.486645126 +0000
  5609. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/gtk/moz.build    2014-12-08 19:19:07.482645120 +0000
  5610. @@ -19,11 +19,20 @@
  5611.      'nsAppShell.cpp',
  5612.      'nsBidiKeyboard.cpp',
  5613.      'nsColorPicker.cpp',
  5614. +    'nsDbusmenu.cpp',
  5615.      'nsFilePicker.cpp',
  5616.      'nsGtkIMModule.cpp',
  5617.      'nsGtkKeyUtils.cpp',
  5618.      'nsImageToPixbuf.cpp',
  5619.      'nsLookAndFeel.cpp',
  5620. +    'nsMenu.cpp',
  5621. +    'nsMenuBar.cpp',
  5622. +    'nsMenuContainer.cpp',
  5623. +    'nsMenuItem.cpp',
  5624. +    'nsMenuObject.cpp',
  5625. +    'nsMenuSeparator.cpp',
  5626. +    'nsNativeMenuAtoms.cpp',
  5627. +    'nsNativeMenuDocListener.cpp',
  5628.      'nsNativeThemeGTK.cpp',
  5629.      'nsScreenGtk.cpp',
  5630.      'nsScreenManagerGtk.cpp',
  5631. @@ -35,6 +44,7 @@
  5632.  ]
  5633.  
  5634.  SOURCES += [
  5635. +    'nsNativeMenuService.cpp',
  5636.      'nsWindow.cpp', # conflicts with X11 headers
  5637.  ]
  5638.  
  5639. @@ -82,6 +92,7 @@
  5640.  
  5641.  LOCAL_INCLUDES += [
  5642.      '/layout/generic',
  5643. +    '/layout/style',
  5644.      '/layout/xul',
  5645.      '/other-licenses/atk-1.0',
  5646.      '/widget',
  5647. Index: firefox-trunk-37.0~a1~hg20141208r218676/browser/base/content/browser.js
  5648. ===================================================================
  5649. --- firefox-trunk-37.0~a1~hg20141208r218676.orig/browser/base/content/browser.js    2014-12-08 19:19:07.486645126 +0000
  5650. +++ firefox-trunk-37.0~a1~hg20141208r218676/browser/base/content/browser.js 2014-12-08 19:19:07.486645126 +0000
  5651. @@ -4787,6 +4787,8 @@
  5652.    let toolbarNodes = Array.slice(gNavToolbox.childNodes);
  5653.    toolbarNodes = toolbarNodes.concat(gNavToolbox.externalToolbars);
  5654.    toolbarNodes = toolbarNodes.filter(node => node.getAttribute("toolbarname"));
  5655. +  if (document.documentElement.getAttribute("shellshowingmenubar") == "true")
  5656. +    toolbarNodes = toolbarNodes.filter(node => node.id != "toolbar-menubar");
  5657.    return toolbarNodes;
  5658.  }
  5659.  
  5660. Index: firefox-trunk-37.0~a1~hg20141208r218676/widget/moz.build
  5661. ===================================================================
  5662. --- firefox-trunk-37.0~a1~hg20141208r218676.orig/widget/moz.build   2014-12-08 19:19:07.486645126 +0000
  5663. +++ firefox-trunk-37.0~a1~hg20141208r218676/widget/moz.build    2014-12-08 19:19:07.486645126 +0000
  5664. @@ -37,10 +37,12 @@
  5665.          'nsITaskbarProgress.idl',
  5666.      ]
  5667.      EXPORTS += [
  5668. -        'nsINativeMenuService.h',
  5669.          'nsIPrintDialogService.h',
  5670.      ]
  5671.  
  5672. +if toolkit in ('cocoa', 'gtk2', 'gtk3'):
  5673. +    EXPORTS += ['nsINativeMenuService.h']
  5674. +
  5675.  TEST_DIRS += ['tests']
  5676.  
  5677.  # Don't build the DSO under the 'build' directory as windows does.
  5678. Index: firefox-trunk-37.0~a1~hg20141208r218676/modules/libpref/init/all.js
  5679. ===================================================================
  5680. --- firefox-trunk-37.0~a1~hg20141208r218676.orig/modules/libpref/init/all.js    2014-12-08 19:17:38.482511700 +0000
  5681. +++ firefox-trunk-37.0~a1~hg20141208r218676/modules/libpref/init/all.js 2014-12-08 19:20:22.742757624 +0000
  5682. @@ -175,6 +175,9 @@
  5683.  pref("browser.sessionhistory.max_total_viewers", -1);
  5684.  
  5685.  pref("ui.use_native_colors", true);
  5686. +#ifdef MOZ_WIDGET_GTK
  5687. +pref("ui.use_unity_menubar", true);
  5688. +#endif
  5689.  pref("ui.click_hold_context_menus", false);
  5690.  pref("browser.display.use_document_fonts",  1);  // 0 = never, 1 = quick, 2 = always
  5691.  // 0 = default: always, except in high contrast mode
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement