summarylogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.SRCINFO22
-rw-r--r--0001-Bug-1338655-Don-t-try-to-build-mp4parse-bindings.-r-.patch48
-rw-r--r--PKGBUILD79
-rw-r--r--fix-wifi-scanner.diff16
-rw-r--r--no-crmf.diff39
-rw-r--r--rust-i686.patch16
-rw-r--r--thunderbird-60.2.1-buildfix.patch42
-rw-r--r--thunderbird-install-dir.patch12
-rw-r--r--unity-menubar.patch3725
9 files changed, 1773 insertions, 2226 deletions
diff --git a/.SRCINFO b/.SRCINFO
index a7ef19b70f25..2117d413ce82 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,6 +1,6 @@
pkgbase = thunderbird-appmenu
pkgdesc = Thunderbird from extra with appmenu patch
- pkgver = 52.9.1
+ pkgver = 60.2.1
pkgrel = 1
url = https://aur.archlinux.org/packages/thunderbird-appmenu/
arch = x86_64
@@ -37,26 +37,16 @@ pkgbase = thunderbird-appmenu
depends = libvpx
depends = icu
optdepends = libcanberra: sound support
- provides = thunderbird=52.9.1
- conflicts = thunderbird
options = !emptydirs
options = !makeflags
- source = https://ftp.mozilla.org/pub/mozilla.org/thunderbird/releases/52.9.1/source/thunderbird-52.9.1.source.tar.xz
+ source = https://ftp.mozilla.org/pub/mozilla.org/thunderbird/releases/60.2.1/source/thunderbird-60.2.1.source.tar.xz
source = thunderbird.desktop
- source = 0001-Bug-1338655-Don-t-try-to-build-mp4parse-bindings.-r-.patch
- source = rust-i686.patch
- source = fix-wifi-scanner.diff
- source = thunderbird-install-dir.patch
- source = no-crmf.diff
+ source = thunderbird-60.2.1-buildfix.patch
source = unity-menubar.patch
- sha256sums = 286fa71504e7184f3a41bcbdebf591bebe8e04dccbad1c93a47c6e72a7125c4d
+ sha256sums = d313f25cd7ddc016bf8e4d4115f14b34a66621c0feabbc0dd72f9304cb93d7bf
sha256sums = 3534ea85d8e0e35dba5f40a7a07844df19f3a480e1358fc50c2502f122dab789
- sha256sums = 413cd6d366d78f325d80ebebccfd0afa0d266b40b2e54b66ba2fa03c15f3ea67
- sha256sums = f61ea706ce6905f568b9bdafd1b044b58f20737426f0aa5019ddb9b64031a269
- sha256sums = 9765bca5d63fb5525bbd0520b7ab1d27cabaed697e2fc7791400abc3fa4f13b8
- sha256sums = 24599eab8862476744fe1619a9a53a5b8cdcab30b3fc5767512f31d3529bd05d
- sha256sums = a7317caba56e89932bd9e3b9352d94701dd9a419685057f238b1ded8dc0adcd7
- sha256sums = 87f194b87e2291c232a00cfa45c246e57fbdb1d02ba0a19f1f818f0fc9bf5967
+ sha256sums = 884c5a6742677c83173812f7abb1e409a2b13371ba6079c4cb69b3e95010de05
+ sha256sums = c6082f9ab534cdc67c53fa1685df628f03b0674390522f32fe640ce92f1b15ab
pkgname = thunderbird-appmenu
diff --git a/0001-Bug-1338655-Don-t-try-to-build-mp4parse-bindings.-r-.patch b/0001-Bug-1338655-Don-t-try-to-build-mp4parse-bindings.-r-.patch
deleted file mode 100644
index cf2e0eb9b80b..000000000000
--- a/0001-Bug-1338655-Don-t-try-to-build-mp4parse-bindings.-r-.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From bbd48a5613c872883616884cfaf41665b0e4ec9b Mon Sep 17 00:00:00 2001
-From: Ralph Giles <giles@mozilla.com>
-Date: Fri, 10 Feb 2017 12:58:18 -0800
-Subject: [PATCH] Bug 1338655 - Don't try to build mp4parse bindings. r=froydnj
-
-We use the cheddar crate to generate a C header file
-for our mp4parse_capi wrapper crate. Currently we
-do this at code check-in time via update-rust.sh.
-
-Cargo 0.18 and later will try to execute a build.rs
-file in the crate source tree regardless of whether
-it's specified in Cargo.toml so patching out that
-line just results in 'crate cheddar not found'.
-
-This change restores the old behaviour by substituting
-a 'build = false' line instead.
-
-We do have syntex vendored, but we don't currently build
-it by default, so I prefer this solution to just vendoring
-cheddar and generating the header at build time. The syntex
-crate is quite large and adds significantly to our compile
-time.
-
-MozReview-Commit-ID: InJRRODWAdP
-
---HG--
-extra : rebase_source : 29378fcbc86015ce6cc22dc66d38a43ddbac204e
----
- media/libstagefright/binding/mp4parse-cargo.patch | 5 +++--
- media/libstagefright/binding/mp4parse_capi/Cargo.toml | 2 ++
- 2 files changed, 5 insertions(+), 2 deletions(-)
-
-diff --git a/media/libstagefright/binding/mp4parse_capi/Cargo.toml b/media/libstagefright/binding/mp4parse_capi/Cargo.toml
-index aee7ee947151a27c..d7e3f55119d3f4b6 100644
---- a/media/libstagefright/binding/mp4parse_capi/Cargo.toml
-+++ b/media/libstagefright/binding/mp4parse_capi/Cargo.toml
-@@ -18,6 +18,8 @@ exclude = [
- "*.mp4",
- ]
-
-+build = false
-+
- [dependencies]
- byteorder = "1.0.0"
- "mp4parse" = {version = "0.6.0", path = "../mp4parse"}
---
-2.12.2
-
diff --git a/PKGBUILD b/PKGBUILD
index 5136ad941365..10ca84121c37 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -1,4 +1,3 @@
-# $Id$
# Contributor: Jan Alexander Steffens (heftig) <jan.steffens@gmail.com>
# Contributor: Ionut Biru <ibiru@archlinux.org>
# Contributor: Alexander Baldeck <alexander@archlinux.org>
@@ -6,9 +5,10 @@
# Contributor: Anders Bostrom <anders.bostrom@home.se>
# Additional patching: Nikita Tarasov <nikatar@disroot.org>
+
_pkgname=thunderbird
pkgname=thunderbird-appmenu
-pkgver=52.9.1
+pkgver=60.2.1
pkgrel=1
pkgdesc="Thunderbird from extra with appmenu patch"
arch=(x86_64)
@@ -19,23 +19,15 @@ depends=(gtk3 gtk2 mozilla-common libxt startup-notification mime-types dbus-gli
makedepends=(unzip zip diffutils python2 yasm mesa imake gconf libpulse inetutils xorg-server-xvfb
autoconf2.13 rust clang llvm)
optdepends=('libcanberra: sound support')
-provides=("thunderbird=$pkgver")
-conflicts=("thunderbird")
options=(!emptydirs !makeflags)
source=(https://ftp.mozilla.org/pub/mozilla.org/thunderbird/releases/$pkgver/source/thunderbird-$pkgver.source.tar.xz
$_pkgname.desktop
- 0001-Bug-1338655-Don-t-try-to-build-mp4parse-bindings.-r-.patch
- rust-i686.patch fix-wifi-scanner.diff
- thunderbird-install-dir.patch no-crmf.diff
+ thunderbird-60.2.1-buildfix.patch
unity-menubar.patch)
-sha256sums=('286fa71504e7184f3a41bcbdebf591bebe8e04dccbad1c93a47c6e72a7125c4d'
+sha256sums=('d313f25cd7ddc016bf8e4d4115f14b34a66621c0feabbc0dd72f9304cb93d7bf'
'3534ea85d8e0e35dba5f40a7a07844df19f3a480e1358fc50c2502f122dab789'
- '413cd6d366d78f325d80ebebccfd0afa0d266b40b2e54b66ba2fa03c15f3ea67'
- 'f61ea706ce6905f568b9bdafd1b044b58f20737426f0aa5019ddb9b64031a269'
- '9765bca5d63fb5525bbd0520b7ab1d27cabaed697e2fc7791400abc3fa4f13b8'
- '24599eab8862476744fe1619a9a53a5b8cdcab30b3fc5767512f31d3529bd05d'
- 'a7317caba56e89932bd9e3b9352d94701dd9a419685057f238b1ded8dc0adcd7'
- '87f194b87e2291c232a00cfa45c246e57fbdb1d02ba0a19f1f818f0fc9bf5967')
+ '884c5a6742677c83173812f7abb1e409a2b13371ba6079c4cb69b3e95010de05'
+ 'c6082f9ab534cdc67c53fa1685df628f03b0674390522f32fe640ce92f1b15ab')
# Google API keys (see http://www.chromium.org/developers/how-tos/api-keys)
# Note: These are for Arch Linux use ONLY. For your own distribution, please
@@ -50,40 +42,21 @@ _google_api_key=AIzaSyDwr302FpOSkGRpLlUpPThNTDPbXcIn_FM
_mozilla_api_key=16674381-f021-49de-8622-3021c5942aff
prepare() {
- mkdir path
- ln -s /usr/bin/python2 path/python
-
cd $_pkgname-$pkgver
- patch -Np1 -i ../thunderbird-install-dir.patch
-
- # https://bugzilla.mozilla.org/show_bug.cgi?id=1314968
- patch -d mozilla -Np1 < ../fix-wifi-scanner.diff
-
- # https://bugzilla.mozilla.org/show_bug.cgi?id=1371991
- patch -Np1 -i ../no-crmf.diff
-
- # Build with the rust targets we actually ship
- patch -d mozilla -Np1 < ../rust-i686.patch
-
- # https://bugs.archlinux.org/task/53890
- patch -d mozilla -Np1 < ../0001-Bug-1338655-Don-t-try-to-build-mp4parse-bindings.-r-.patch
-
- # actual appmenu patch from ubuntu repos
- patch -Np1 -i ../unity-menubar.patch
echo -n "$_google_api_key" >google-api-key
echo -n "$_mozilla_api_key" >mozilla-api-key
cat >.mozconfig <<END
-ac_add_options --enable-application=mail
+ac_add_options --enable-application=comm/mail
ac_add_options --enable-calendar
ac_add_options --prefix=/usr
ac_add_options --enable-release
-ac_add_options --enable-gold
-ac_add_options --enable-pie
-ac_add_options --enable-optimize="-O2"
-ac_add_options --enable-rust
+ac_add_options --enable-linker=gold
+ac_add_options --enable-hardening
+ac_add_options --enable-optimize
+ac_add_options --enable-rust-simd
# Branding
ac_add_options --enable-official-branding
@@ -113,30 +86,30 @@ ac_add_options --enable-startup-notification
ac_add_options --disable-crashreporter
ac_add_options --disable-updater
END
+ patch -Np1 < ../thunderbird-60.2.1-buildfix.patch
+
+ # actual appmenu patch from ubuntu repos
+ patch -Np1 -i ../unity-menubar.patch
}
build() {
cd $_pkgname-$pkgver
-
- # _FORTIFY_SOURCE causes configure failures
- CPPFLAGS+=" -O2"
-
- export PATH="$srcdir/path:$PATH"
-
- # Do PGO
- #xvfb-run -a -n 95 -s "-extension GLX -screen 0 1280x1024x24" \
- # make -f client.mk build MOZ_PGO=1
- make -f client.mk build
+ ./mach configure
+ ./mach build
+ ./mach buildsymbols
}
package() {
cd $_pkgname-$pkgver
- make -f client.mk DESTDIR="$pkgdir" INSTALL_SDK= install
+ DESTDIR="$pkgdir" ./mach install
_vendorjs="$pkgdir/usr/lib/$_pkgname/defaults/preferences/vendor.js"
install -Dm644 /dev/stdin "$_vendorjs" <<END
// Use LANG environment variable to choose locale
-pref("intl.locale.matchOS", true);
+pref("intl.locale.requested", "");
+
+// Use system-provided dictionaries
+pref("spellchecker.dictionary_path", "/usr/share/hunspell");
// Disable default mailer checking.
pref("mail.shell.checkDefaultMail", false);
@@ -159,10 +132,12 @@ app.distributor.channel=$_pkgname
app.partner.archlinux=archlinux
END
- for i in 16 22 24 32 48 256; do
- install -Dm644 other-licenses/branding/thunderbird/mailicon$i.png \
+ for i in 16 22 24 32 48 64 128 256; do
+ install -Dm644 comm/mail/branding/thunderbird/default${i}.png \
"$pkgdir/usr/share/icons/hicolor/${i}x${i}/apps/$_pkgname.png"
done
+ install -Dm644 comm/mail/branding/thunderbird/TB-symbolic.svg \
+ "$pkgdir/usr/share/icons/hicolor/symbolic/apps/thunderbird-symbolic.svg"
install -Dm644 ../$_pkgname.desktop \
"$pkgdir/usr/share/applications/$_pkgname.desktop"
diff --git a/fix-wifi-scanner.diff b/fix-wifi-scanner.diff
deleted file mode 100644
index f8fdd6c42142..000000000000
--- a/fix-wifi-scanner.diff
+++ /dev/null
@@ -1,16 +0,0 @@
- netwerk/wifi/nsWifiScannerDBus.cpp | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git c/netwerk/wifi/nsWifiScannerDBus.cpp i/netwerk/wifi/nsWifiScannerDBus.cpp
-index 182553e18fa6e104..6fa0a0b023d3e45f 100644
---- c/netwerk/wifi/nsWifiScannerDBus.cpp
-+++ i/netwerk/wifi/nsWifiScannerDBus.cpp
-@@ -62,7 +62,7 @@ nsWifiScannerDBus::SendMessage(const char* aInterface,
- return NS_ERROR_FAILURE;
- }
- } else if (!strcmp(aFuncCall, "GetAll")) {
-- const char* param = "";
-+ const char* param = "org.freedesktop.NetworkManager.AccessPoint";
- if (!dbus_message_iter_append_basic(&argsIter, DBUS_TYPE_STRING, &param)) {
- return NS_ERROR_FAILURE;
- }
diff --git a/no-crmf.diff b/no-crmf.diff
deleted file mode 100644
index 73e545de0eed..000000000000
--- a/no-crmf.diff
+++ /dev/null
@@ -1,39 +0,0 @@
-diff -u -rN thunderbird-52.1.1/mozilla/config/external/nss/crmf/moz.build thunderbird-52.1.1-nocrmf/mozilla/config/external/nss/crmf/moz.build
---- thunderbird-52.1.1/mozilla/config/external/nss/crmf/moz.build 2017-05-09 23:35:13.000000000 +0200
-+++ thunderbird-52.1.1-nocrmf/mozilla/config/external/nss/crmf/moz.build 2017-06-14 01:51:36.686773132 +0200
-@@ -8,7 +8,6 @@
-
- if CONFIG['MOZ_SYSTEM_NSS']:
- OS_LIBS += [l for l in CONFIG['NSS_LIBS'] if l.startswith('-L')]
-- OS_LIBS += ['-lcrmf']
- else:
- USE_LIBS += [
- # The dependency on nss is not real, but is required to force the
-diff -u -rN thunderbird-52.1.1/mozilla/old-configure thunderbird-52.1.1-nocrmf/mozilla/old-configure
---- thunderbird-52.1.1/mozilla/old-configure 2017-05-09 23:35:35.000000000 +0200
-+++ thunderbird-52.1.1-nocrmf/mozilla/old-configure 2017-06-14 01:50:28.726873231 +0200
-@@ -10692,9 +10692,7 @@
-
- fi
-
--if test -n "$MOZ_SYSTEM_NSS"; then
-- NSS_LIBS="$NSS_LIBS -lcrmf"
--else
-+if test -z "$MOZ_SYSTEM_NSS"; then
- NSS_CFLAGS="-I${DIST}/include/nss"
- fi
-
-diff -u -rN thunderbird-52.1.1/mozilla/old-configure.in thunderbird-52.1.1-nocrmf/mozilla/old-configure.in
---- thunderbird-52.1.1/mozilla/old-configure.in 2017-05-09 23:35:22.000000000 +0200
-+++ thunderbird-52.1.1-nocrmf/mozilla/old-configure.in 2017-06-14 01:50:50.953507079 +0200
-@@ -2126,9 +2126,7 @@
- AM_PATH_NSS(3.28.4, [MOZ_SYSTEM_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
- fi
-
--if test -n "$MOZ_SYSTEM_NSS"; then
-- NSS_LIBS="$NSS_LIBS -lcrmf"
--else
-+if test -z "$MOZ_SYSTEM_NSS"; then
- NSS_CFLAGS="-I${DIST}/include/nss"
- fi
-
diff --git a/rust-i686.patch b/rust-i686.patch
deleted file mode 100644
index 85512e1436b8..000000000000
--- a/rust-i686.patch
+++ /dev/null
@@ -1,16 +0,0 @@
- build/moz.configure/rust.configure | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git c/build/moz.configure/rust.configure i/build/moz.configure/rust.configure
-index cd86b24153debb1b..44911715e25d95e3 100644
---- c/build/moz.configure/rust.configure
-+++ i/build/moz.configure/rust.configure
-@@ -81,7 +81,7 @@ def rust_target(rust_compiler, rustc, target, cross_compiling):
- # OpenBSD
- ('x86_64', 'OpenBSD'): 'x86_64-unknown-openbsd',
- # Linux
-- ('x86', 'Linux'): 'i586-unknown-linux-gnu',
-+ ('x86', 'Linux'): 'i686-unknown-linux-gnu',
- # Linux
- ('x86_64', 'Linux'): 'x86_64-unknown-linux-gnu',
- # OS X and iOS
diff --git a/thunderbird-60.2.1-buildfix.patch b/thunderbird-60.2.1-buildfix.patch
new file mode 100644
index 000000000000..f7742ea91281
--- /dev/null
+++ b/thunderbird-60.2.1-buildfix.patch
@@ -0,0 +1,42 @@
+
+# HG changeset patch
+# User Chris Manchester <cmanchester@mozilla.com>
+# Date 1533063488 25200
+# Node ID bc651d3d910cbc0730d870c5436b29ddc01fef10
+# Parent e9dd9434ad9ac15284429d904a45e4daf567c03b
+Bug 1479540 - Accept "triplet" strings with only two parts in moz.configure. r=froydnj, a=jcristau
+
+MozReview-Commit-ID: 7pFhoJgBMhQ
+
+diff --git a/build/moz.configure/init.configure b/build/moz.configure/init.configure
+--- a/build/moz.configure/init.configure
++++ b/build/moz.configure/init.configure
+@@ -586,17 +586,26 @@ option('--target', nargs=1,
+ @imports(_from='__builtin__', _import='KeyError')
+ @imports(_from='__builtin__', _import='ValueError')
+ def split_triplet(triplet, allow_unknown=False):
+ # The standard triplet is defined as
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+ # There is also a quartet form:
+ # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+ # But we can consider the "KERNEL-OPERATING_SYSTEM" as one.
+- cpu, manufacturer, os = triplet.split('-', 2)
++ # Additionally, some may omit "unknown" when the manufacturer
++ # is not specified and emit
++ # CPU_TYPE-OPERATING_SYSTEM
++ parts = triplet.split('-', 2)
++ if len(parts) == 3:
++ cpu, _, os = parts
++ elif len(parts) == 2:
++ cpu, os = parts
++ else:
++ die("Unexpected triplet string: %s" % triplet)
+
+ # Autoconf uses config.sub to validate and canonicalize those triplets,
+ # but the granularity of its results has never been satisfying to our
+ # use, so we've had our own, different, canonicalization. We've also
+ # historically not been very consistent with how we use the canonicalized
+ # values. Hopefully, this will help us make things better.
+ # The tests are inherited from our decades-old autoconf-based configure,
+ # which can probably be improved/cleaned up because they are based on a
+
diff --git a/thunderbird-install-dir.patch b/thunderbird-install-dir.patch
deleted file mode 100644
index 0c7ffaa4a52a..000000000000
--- a/thunderbird-install-dir.patch
+++ /dev/null
@@ -1,12 +0,0 @@
-diff -upr comm-esr31.orig/mozilla/config/baseconfig.mk comm-esr31/mozilla/config/baseconfig.mk
---- comm-esr31.orig/mozilla/config/baseconfig.mk 2014-07-22 09:44:22.000000000 +0300
-+++ comm-esr31/mozilla/config/baseconfig.mk 2014-07-22 09:46:45.000000000 +0300
-@@ -4,7 +4,7 @@
- # whether a normal build is happening or whether the check is running.
- includedir := $(includedir)/$(MOZ_APP_NAME)-$(MOZ_APP_VERSION)
- idldir = $(datadir)/idl/$(MOZ_APP_NAME)-$(MOZ_APP_VERSION)
--installdir = $(libdir)/$(MOZ_APP_NAME)-$(MOZ_APP_VERSION)
-+installdir = $(libdir)/$(MOZ_APP_NAME)
- sdkdir = $(libdir)/$(MOZ_APP_NAME)-devel-$(MOZ_APP_VERSION)
- ifndef TOP_DIST
- TOP_DIST = dist
diff --git a/unity-menubar.patch b/unity-menubar.patch
index 088026c5910d..d91486e09912 100644
--- a/unity-menubar.patch
+++ b/unity-menubar.patch
@@ -1,7 +1,5 @@
-Index: firefox-52.0~b9+build2/mozilla/browser/base/content/browser-menubar.inc
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/browser/base/content/browser-menubar.inc
-+++ firefox-52.0~b9+build2/mozilla/browser/base/content/browser-menubar.inc
+--- a/browser/base/content/browser-menubar.inc
++++ b/browser/base/content/browser-menubar.inc
@@ -5,7 +5,11 @@
<menubar id="main-menubar"
@@ -14,11 +12,29 @@ Index: firefox-52.0~b9+build2/mozilla/browser/base/content/browser-menubar.inc
this.setAttribute('openedwithkey',
event.target.parentNode.openedWithKey);"
style="border:0px;padding:0px;margin:0px;-moz-appearance:none">
-Index: firefox-52.0~b9+build2/mozilla/browser/components/places/content/places.xul
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/browser/components/places/content/places.xul
-+++ firefox-52.0~b9+build2/mozilla/browser/components/places/content/places.xul
-@@ -157,7 +157,7 @@
+--- a/browser/base/content/browser.js
++++ b/browser/base/content/browser.js
+@@ -5547,11 +5547,17 @@
+
+ let toolbarNodes = gNavToolbox.childNodes;
+
++ let shellShowingMenubar = document.documentElement.getAttribute("shellshowingmenubar") == "true";
++
+ for (let toolbar of toolbarNodes) {
+ if (!toolbar.hasAttribute("toolbarname")) {
+ continue;
+ }
+
++ if (shellShowingMenubar && toolbar.id == "toolbar-menubar") {
++ continue;
++ }
++
+ let menuItem = document.createElement("menuitem");
+ let hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
+ "autohide" : "collapsed";
+--- a/browser/components/places/content/places.xul
++++ b/browser/components/places/content/places.xul
+@@ -161,7 +161,7 @@
<toolbarbutton type="menu" class="tabbable"
onpopupshowing="document.getElementById('placeContent').focus()"
#else
@@ -27,11 +43,57 @@ Index: firefox-52.0~b9+build2/mozilla/browser/components/places/content/places.x
<menu accesskey="&organize.accesskey;" class="menu-iconic"
#endif
id="organizeButton" label="&organize.label;"
-Index: firefox-52.0~b9+build2/mozilla/toolkit/content/widgets/popup.xml
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/toolkit/content/widgets/popup.xml
-+++ firefox-52.0~b9+build2/mozilla/toolkit/content/widgets/popup.xml
-@@ -25,8 +25,14 @@
+--- a/layout/build/moz.build
++++ b/layout/build/moz.build
+@@ -71,6 +71,10 @@
+ '/dom/system',
+ '/dom/system/android',
+ ]
++elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
++ LOCAL_INCLUDES += [
++ '/widget/gtk',
++ ]
+
+ if CONFIG['MOZ_WEBSPEECH']:
+ LOCAL_INCLUDES += [
+--- a/layout/build/nsLayoutStatics.cpp
++++ b/layout/build/nsLayoutStatics.cpp
+@@ -126,6 +126,10 @@
+ #include "nsHostObjectProtocolHandler.h"
+ #include "nsThreadManager.h"
+
++#ifdef MOZ_WIDGET_GTK
++#include "nsNativeMenuAtoms.h"
++#endif
++
+ using namespace mozilla;
+ using namespace mozilla::net;
+ using namespace mozilla::dom;
+@@ -157,6 +161,9 @@
+ nsGkAtoms::AddRefAtoms();
+ nsHTMLTags::RegisterAtoms();
+ nsRDFAtoms::RegisterAtoms();
++#ifdef MOZ_WIDGET_GTK
++ nsNativeMenuAtoms::RegisterAtoms();
++#endif
+
+ NS_SetStaticAtomsDone();
+
+--- a/modules/libpref/init/all.js
++++ b/modules/libpref/init/all.js
+@@ -265,6 +265,9 @@
+ pref("browser.sessionhistory.max_total_viewers", -1);
+
+ pref("ui.use_native_colors", true);
++#ifdef MOZ_WIDGET_GTK
++pref("ui.use_unity_menubar", true);
++#endif
+ pref("ui.click_hold_context_menus", false);
+
+ // Pop up context menu on mouseup instead of mousedown, if that's the OS default.
+--- a/toolkit/content/widgets/popup.xml
++++ b/toolkit/content/widgets/popup.xml
+@@ -27,8 +27,14 @@
</getter>
</property>
@@ -39,8 +101,8 @@ Index: firefox-52.0~b9+build2/mozilla/toolkit/content/widgets/popup.xml
- onget="return this.popupBoxObject.popupState"/>
+ <property name="state" readonly="true">
+ <getter><![CDATA[
-+ if (this.hasAttribute('_moz-menupopupstate'))
-+ return this.getAttribute('_moz-menupopupstate');
++ if (this.hasAttribute('_moz-nativemenupopupstate'))
++ return this.getAttribute('_moz-nativemenupopupstate');
+ else
+ return this.popupBoxObject.popupState;
+ ]]></getter>
@@ -48,11 +110,9 @@ Index: firefox-52.0~b9+build2/mozilla/toolkit/content/widgets/popup.xml
<property name="triggerNode" readonly="true"
onget="return this.popupBoxObject.triggerNode"/>
-Index: firefox-52.0~b9+build2/mozilla/toolkit/content/xul.css
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/toolkit/content/xul.css
-+++ firefox-52.0~b9+build2/mozilla/toolkit/content/xul.css
-@@ -307,6 +307,18 @@ toolbar[type="menubar"][autohide="true"]
+--- a/toolkit/content/xul.css
++++ b/toolkit/content/xul.css
+@@ -312,6 +312,18 @@
}
%endif
@@ -68,13 +128,49 @@ Index: firefox-52.0~b9+build2/mozilla/toolkit/content/xul.css
+}
+%endif
+
- toolbarseparator {
- -moz-binding: url("chrome://global/content/bindings/toolbar.xml#toolbardecoration");
+ toolbarspring {
+ -moz-box-flex: 1000;
}
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsDbusmenu.cpp
-===================================================================
+--- a/widget/gtk/moz.build
++++ b/widget/gtk/moz.build
+@@ -39,10 +39,18 @@
+ 'nsAppShell.cpp',
+ 'nsBidiKeyboard.cpp',
+ 'nsColorPicker.cpp',
++ 'nsDbusmenu.cpp',
+ 'nsFilePicker.cpp',
+ 'nsGtkKeyUtils.cpp',
+ 'nsImageToPixbuf.cpp',
+ 'nsLookAndFeel.cpp',
++ 'nsMenuBar.cpp',
++ 'nsMenuContainer.cpp',
++ 'nsMenuItem.cpp',
++ 'nsMenuObject.cpp',
++ 'nsMenuSeparator.cpp',
++ 'nsNativeMenuAtoms.cpp',
++ 'nsNativeMenuDocListener.cpp',
+ 'nsNativeThemeGTK.cpp',
+ 'nsSound.cpp',
+ 'nsToolkit.cpp',
+@@ -54,6 +62,8 @@
+ ]
+
+ SOURCES += [
++ 'nsMenu.cpp', # conflicts with X11 headers
++ 'nsNativeMenuService.cpp',
+ 'nsWindow.cpp', # conflicts with X11 headers
+ ]
+
+@@ -124,6 +134,7 @@
+
+ LOCAL_INCLUDES += [
+ '/layout/generic',
++ '/layout/style',
+ '/layout/xul',
+ '/other-licenses/atk-1.0',
+ '/widget',
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsDbusmenu.cpp
++++ b/widget/gtk/nsDbusmenu.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
@@ -139,11 +235,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsDbusmenu.cpp
+
+ return NS_OK;
+}
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsDbusmenu.h
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsDbusmenu.h
-@@ -0,0 +1,99 @@
++++ b/widget/gtk/nsDbusmenu.h
+@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -211,6 +305,8 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsDbusmenu.h
+class nsDbusmenuFunctions
+{
+public:
++ nsDbusmenuFunctions() = delete;
++
+ static nsresult Init();
+
+#define FUNC(name, type, params) \
@@ -243,11 +339,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsDbusmenu.h
+#define dbusmenu_menuitem_property_set_shortcut nsDbusmenuFunctions::s_dbusmenu_menuitem_property_set_shortcut
+
+#endif /* __nsDbusmenu_h__ */
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
-@@ -0,0 +1,868 @@
++++ b/widget/gtk/nsMenu.cpp
+@@ -0,0 +1,854 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -257,8 +351,16 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+
+#define _IMPL_NS_LAYOUT
+
++#include "mozilla/dom/Element.h"
++#include "mozilla/Assertions.h"
++#include "mozilla/EventDispatcher.h"
++#include "mozilla/GeckoStyleContext.h"
+#include "mozilla/GuardObjects.h"
+#include "mozilla/MouseEvents.h"
++#include "mozilla/Move.h"
++#include "mozilla/ServoStyleContext.h"
++#include "mozilla/ServoTypes.h"
++#include "mozilla/StyleSetHandle.h"
+#include "mozilla/StyleSetHandleInlines.h"
+#include "nsAutoPtr.h"
+#include "nsBindingManager.h"
@@ -267,7 +369,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+#include "nsCSSValue.h"
+#include "nsGkAtoms.h"
+#include "nsGtkUtils.h"
-+#include "nsIAtom.h"
++#include "nsAtom.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+#include "nsIPresShell.h"
@@ -275,7 +377,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+#include "nsITimer.h"
+#include "nsString.h"
+#include "nsStyleContext.h"
-+#include "nsStyleSet.h"
++#include "nsStyleContextInlines.h"
+#include "nsStyleStruct.h"
+#include "nsThreadUtils.h"
+#include "nsXBLBinding.h"
@@ -290,59 +392,78 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+
+using namespace mozilla;
+
-+class MOZ_STACK_CLASS nsMenuUpdateBatch
++class nsMenuContentInsertedEvent : public Runnable
+{
+public:
-+ nsMenuUpdateBatch(nsMenu *aMenu MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
-+ mMenu(aMenu)
-+ {
-+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-+ mMenu->BeginUpdateBatchInternal();
-+ }
++ nsMenuContentInsertedEvent(nsMenu *aMenu,
++ nsIContent *aContainer,
++ nsIContent *aChild,
++ nsIContent *aPrevSibling) :
++ Runnable("nsMenuContentInsertedEvent"),
++ mWeakMenu(aMenu),
++ mContainer(aContainer),
++ mChild(aChild),
++ mPrevSibling(aPrevSibling) { }
+
-+ ~nsMenuUpdateBatch()
++ NS_IMETHODIMP Run()
+ {
-+ mMenu->EndUpdateBatch();
++ if (!mWeakMenu) {
++ return NS_OK;
++ }
++
++ static_cast<nsMenu *>(mWeakMenu.get())->HandleContentInserted(mContainer,
++ mChild,
++ mPrevSibling);
++ return NS_OK;
+ }
+
+private:
-+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-+ nsMenu *mMenu;
++ nsWeakMenuObject mWeakMenu;
++
++ nsCOMPtr<nsIContent> mContainer;
++ nsCOMPtr<nsIContent> mChild;
++ nsCOMPtr<nsIContent> mPrevSibling;
+};
+
-+class nsSetAttrRunnableNoNotify : public Runnable
++class nsMenuContentRemovedEvent : public Runnable
+{
+public:
-+ nsSetAttrRunnableNoNotify(nsIContent *aContent, nsIAtom *aAttribute,
-+ nsAString& aValue) :
-+ mContent(aContent), mAttribute(aAttribute), mValue(aValue) { };
++ nsMenuContentRemovedEvent(nsMenu *aMenu,
++ nsIContent *aContainer,
++ nsIContent *aChild) :
++ Runnable("nsMenuContentRemovedEvent"),
++ mWeakMenu(aMenu),
++ mContainer(aContainer),
++ mChild(aChild) { }
+
+ NS_IMETHODIMP Run()
+ {
-+ return mContent->SetAttr(kNameSpaceID_None, mAttribute, mValue, false);
++ if (!mWeakMenu) {
++ return NS_OK;
++ }
++
++ static_cast<nsMenu *>(mWeakMenu.get())->HandleContentRemoved(mContainer,
++ mChild);
++ return NS_OK;
+ }
+
+private:
-+ nsCOMPtr<nsIContent> mContent;
-+ nsCOMPtr<nsIAtom> mAttribute;
-+ nsAutoString mValue;
++ nsWeakMenuObject mWeakMenu;
++
++ nsCOMPtr<nsIContent> mContainer;
++ nsCOMPtr<nsIContent> mChild;
+};
+
-+class nsUnsetAttrRunnableNoNotify : public Runnable
++static void
++DispatchMouseEvent(nsIContent *aTarget, mozilla::EventMessage aMsg)
+{
-+public:
-+ nsUnsetAttrRunnableNoNotify(nsIContent *aContent, nsIAtom *aAttribute) :
-+ mContent(aContent), mAttribute(aAttribute) { };
-+
-+ NS_IMETHODIMP Run()
-+ {
-+ return mContent->UnsetAttr(kNameSpaceID_None, mAttribute, false);
++ if (!aTarget) {
++ return;
+ }
+
-+private:
-+ nsCOMPtr<nsIContent> mContent;
-+ nsCOMPtr<nsIAtom> mAttribute;
-+};
++ WidgetMouseEvent event(true, aMsg, nullptr, WidgetMouseEvent::eReal);
++ EventDispatcher::Dispatch(aTarget, nullptr, &event);
++}
+
+static void
+AttachXBLBindings(nsIContent *aContent)
@@ -355,7 +476,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+
+ RefPtr<nsStyleContext> sc =
+ shell->StyleSet()->ResolveStyleFor(aContent->AsElement(),
-+ nullptr);
++ nullptr,
++ LazyComputeBehavior::Allow);
++
+ if (!sc) {
+ return;
+ }
@@ -372,8 +495,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+
+ RefPtr<nsXBLBinding> binding;
+ bool dummy;
-+ nsresult rv = xbl->LoadBindings(aContent, display->mBinding->GetURI(),
-+ display->mBinding->mOriginPrincipal,
++ nsresult rv = xbl->LoadBindings(aContent->AsElement(),
++ display->mBinding->GetURI(),
++ display->mBinding->mExtraData->GetPrincipal(),
+ getter_AddRefs(binding), &dummy);
+ if ((NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED) || !binding) {
+ return;
@@ -385,8 +509,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+void
+nsMenu::SetPopupState(EPopupState aState)
+{
-+ ClearFlags(((1U << NSMENU_NUMBER_OF_POPUPSTATE_BITS) - 1U) << NSMENU_NUMBER_OF_FLAGS);
-+ SetFlags(aState << NSMENU_NUMBER_OF_FLAGS);
++ mPopupState = aState;
+
+ if (!mPopupContent) {
+ return;
@@ -407,31 +530,28 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+ break;
+ }
+
-+ if (nsContentUtils::IsSafeToRunScript()) {
-+ if (state.IsEmpty()) {
-+ mPopupContent->UnsetAttr(kNameSpaceID_None,
-+ nsNativeMenuAtoms::_moz_menupopupstate,
-+ false);
-+ } else {
-+ mPopupContent->SetAttr(kNameSpaceID_None,
-+ nsNativeMenuAtoms::_moz_menupopupstate,
-+ state, false);
-+ }
++ if (state.IsEmpty()) {
++ mPopupContent->AsElement()->UnsetAttr(
++ kNameSpaceID_None, nsNativeMenuAtoms::_moz_nativemenupopupstate,
++ false);
+ } else {
-+ nsCOMPtr<nsIRunnable> r;
-+ if (state.IsEmpty()) {
-+ r = new nsUnsetAttrRunnableNoNotify(
-+ mPopupContent, nsNativeMenuAtoms::_moz_menupopupstate);
-+ } else {
-+ r = new nsSetAttrRunnableNoNotify(
-+ mPopupContent, nsNativeMenuAtoms::_moz_menupopupstate,
-+ state);
-+ }
-+ nsContentUtils::AddScriptRunner(r);
++ mPopupContent->AsElement()->SetAttr(
++ kNameSpaceID_None, nsNativeMenuAtoms::_moz_nativemenupopupstate,
++ state, false);
+ }
+}
+
+/* static */ void
++nsMenu::DoOpenCallback(nsITimer *aTimer, void *aClosure)
++{
++ nsMenu* self = static_cast<nsMenu *>(aClosure);
++
++ dbusmenu_menuitem_show_to_user(self->GetNativeData(), 0);
++
++ self->mOpenDelayTimer = nullptr;
++}
++
++/* static */ void
+nsMenu::menu_event_cb(DbusmenuMenuitem *menu,
+ const gchar *name,
+ GVariant *value,
@@ -456,89 +576,62 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+void
+nsMenu::MaybeAddPlaceholderItem()
+{
-+ NS_ASSERTION(!IsInUpdateBatch(),
-+ "Shouldn't be modifying the native menu structure now");
++ MOZ_ASSERT(!IsInBatchedUpdate(),
++ "Shouldn't be modifying the native menu structure now");
+
+ GList *children = dbusmenu_menuitem_get_children(GetNativeData());
+ if (!children) {
-+ NS_ASSERTION(!HasPlaceholderItem(), "Huh?");
-+
-+ DbusmenuMenuitem *ph = dbusmenu_menuitem_new();
-+ if (!ph) {
-+ return;
-+ }
-+
-+ dbusmenu_menuitem_property_set_bool(
-+ ph, DBUSMENU_MENUITEM_PROP_VISIBLE, false);
++ MOZ_ASSERT(!mPlaceholderItem);
+
-+ if (!dbusmenu_menuitem_child_append(GetNativeData(), ph)) {
-+ NS_WARNING("Failed to create placeholder item");
-+ g_object_unref(ph);
++ mPlaceholderItem = dbusmenu_menuitem_new();
++ if (!mPlaceholderItem) {
+ return;
+ }
+
-+ g_object_unref(ph);
++ dbusmenu_menuitem_property_set_bool(mPlaceholderItem,
++ DBUSMENU_MENUITEM_PROP_VISIBLE,
++ false);
+
-+ SetHasPlaceholderItem(true);
++ MOZ_ALWAYS_TRUE(
++ dbusmenu_menuitem_child_append(GetNativeData(), mPlaceholderItem));
+ }
+}
+
-+bool
++void
+nsMenu::EnsureNoPlaceholderItem()
+{
-+ NS_ASSERTION(!IsInUpdateBatch(),
-+ "Shouldn't be modifying the native menu structure now");
-+
-+ if (HasPlaceholderItem()) {
-+ GList *children = dbusmenu_menuitem_get_children(GetNativeData());
-+
-+ NS_ASSERTION(g_list_length(children) == 1,
-+ "Unexpected number of children in native menu (should be 1!)");
++ MOZ_ASSERT(!IsInBatchedUpdate(),
++ "Shouldn't be modifying the native menu structure now");
+
-+ SetHasPlaceholderItem(false);
-+
-+ if (!children) {
-+ return true;
-+ }
-+
-+ if (!dbusmenu_menuitem_child_delete(
-+ GetNativeData(), static_cast<DbusmenuMenuitem *>(children->data))) {
-+ NS_ERROR("Failed to remove placeholder item");
-+ return false;
-+ }
-+ }
-+
-+ return true;
-+}
-+
-+static void
-+DispatchMouseEvent(nsIContent *aTarget, mozilla::EventMessage aMsg)
-+{
-+ if (!aTarget) {
++ if (!mPlaceholderItem) {
+ return;
+ }
+
-+ WidgetMouseEvent event(true, aMsg, nullptr, WidgetMouseEvent::eReal);
-+ aTarget->DispatchDOMEvent(&event, nullptr, nullptr, nullptr);
++ MOZ_ALWAYS_TRUE(
++ dbusmenu_menuitem_child_delete(GetNativeData(), mPlaceholderItem));
++ MOZ_ASSERT(!dbusmenu_menuitem_get_children(GetNativeData()));
++
++ g_object_unref(mPlaceholderItem);
++ mPlaceholderItem = nullptr;
+}
+
+void
+nsMenu::OnOpen()
+{
-+ if (NeedsRebuild()) {
++ if (mNeedsRebuild) {
+ Build();
+ }
+
-+ nsWeakMenuObject<nsMenu> self(this);
++ nsWeakMenuObject self(this);
+ nsCOMPtr<nsIContent> origPopupContent(mPopupContent);
+ {
-+ nsNativeMenuAutoUpdateBatch batch;
++ nsNativeMenuDocListener::BlockUpdatesScope updatesBlocker;
+
+ SetPopupState(ePopupState_Showing);
+ DispatchMouseEvent(mPopupContent, eXULPopupShowing);
+
-+ ContentNode()->SetAttr(kNameSpaceID_None, nsGkAtoms::open,
-+ NS_LITERAL_STRING("true"), true);
++ ContentNode()->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::open,
++ NS_LITERAL_STRING("true"), true);
+ }
+
+ if (!self) {
@@ -551,7 +644,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+ return;
+ }
+
-+ nsNativeMenuAutoUpdateBatch batch;
++ nsNativeMenuDocListener::BlockUpdatesScope updatesBlocker;
+
+ size_t count = ChildCount();
+ for (size_t i = 0; i < count; ++i) {
@@ -565,9 +658,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+void
+nsMenu::Build()
+{
-+ nsMenuUpdateBatch batch(this);
-+
-+ SetNeedsRebuild(false);
++ mNeedsRebuild = false;
+
+ while (ChildCount() > 0) {
+ RemoveChildAt(0);
@@ -581,63 +672,29 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+
+ uint32_t count = mPopupContent->GetChildCount();
+ for (uint32_t i = 0; i < count; ++i) {
-+ nsIContent *childContent = mPopupContent->GetChildAt(i);
++ nsIContent *childContent = mPopupContent->GetChildAt_Deprecated(i);
+
-+ nsresult rv;
-+ nsMenuObject *child = CreateChild(childContent, &rv);
++ UniquePtr<nsMenuObject> child = CreateChild(childContent);
+
-+ if (child) {
-+ rv = AppendChild(child);
++ if (!child) {
++ continue;
+ }
+
-+ if (NS_FAILED(rv)) {
-+ NS_ERROR("Menu build failed");
-+ SetNeedsRebuild(true);
-+ return;
-+ }
++ AppendChild(Move(child));
+ }
+}
+
+void
-+nsMenu::InitializeNativeData()
-+{
-+ // Dbusmenu provides an "about-to-show" signal, and also "opened" and
-+ // "closed" events. However, Unity is the only thing that sends
-+ // both "about-to-show" and "opened" events. Unity 2D and the HUD only
-+ // send "opened" events, so we ignore "about-to-show" (I don't think
-+ // there's any real difference between them anyway).
-+ // To complicate things, there are certain conditions where we don't
-+ // get a "closed" event, so we need to be able to handle this :/
-+ g_signal_connect(G_OBJECT(GetNativeData()), "event",
-+ G_CALLBACK(menu_event_cb), this);
-+
-+ UpdateLabel();
-+ UpdateSensitivity();
-+
-+ SetNeedsRebuild(true);
-+ MaybeAddPlaceholderItem();
-+
-+ AttachXBLBindings(ContentNode());
-+}
-+
-+void
-+nsMenu::Update(nsStyleContext *aStyleContext)
-+{
-+ UpdateVisibility(aStyleContext);
-+ UpdateIcon(aStyleContext);
-+}
-+
-+void
+nsMenu::InitializePopup()
+{
+ nsCOMPtr<nsIContent> oldPopupContent;
+ oldPopupContent.swap(mPopupContent);
+
+ for (uint32_t i = 0; i < ContentNode()->GetChildCount(); ++i) {
-+ nsIContent *child = ContentNode()->GetChildAt(i);
++ nsIContent *child = ContentNode()->GetChildAt_Deprecated(i);
+
+ int32_t dummy;
-+ nsCOMPtr<nsIAtom> tag = child->OwnerDoc()->BindingManager()->ResolveTag(child, &dummy);
++ nsAtom* tag = child->OwnerDoc()->BindingManager()->ResolveTag(child, &dummy);
+ if (tag == nsGkAtoms::menupopup) {
+ mPopupContent = child;
+ break;
@@ -666,258 +723,182 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+}
+
+void
-+nsMenu::BeginUpdateBatchInternal()
-+{
-+ NS_ASSERTION(!IsInUpdateBatch(), "Already in an update batch!");
-+
-+ SetIsInUpdateBatch(true);
-+ SetDidStructureMutate(false);
-+}
-+
-+nsresult
+nsMenu::RemoveChildAt(size_t aIndex)
+{
-+ NS_ASSERTION(IsInUpdateBatch() || !HasPlaceholderItem(),
-+ "Shouldn't have a placeholder menuitem");
++ MOZ_ASSERT(IsInBatchedUpdate() || !mPlaceholderItem,
++ "Shouldn't have a placeholder menuitem");
+
-+ SetDidStructureMutate(true);
++ nsMenuContainer::RemoveChildAt(aIndex, !IsInBatchedUpdate());
++ StructureMutated();
+
-+ nsresult rv = nsMenuContainer::RemoveChildAt(aIndex, !IsInUpdateBatch());
-+
-+ if (!IsInUpdateBatch()) {
++ if (!IsInBatchedUpdate()) {
+ MaybeAddPlaceholderItem();
+ }
-+
-+ return rv;
+}
+
-+nsresult
++void
+nsMenu::RemoveChild(nsIContent *aChild)
+{
+ size_t index = IndexOf(aChild);
+ if (index == NoIndex) {
-+ return NS_ERROR_INVALID_ARG;
++ return;
+ }
+
-+ return RemoveChildAt(index);
++ RemoveChildAt(index);
+}
+
-+nsresult
-+nsMenu::InsertChildAfter(nsMenuObject *aChild, nsIContent *aPrevSibling)
++void
++nsMenu::InsertChildAfter(UniquePtr<nsMenuObject> aChild,
++ nsIContent *aPrevSibling)
+{
-+ if (!IsInUpdateBatch() && !EnsureNoPlaceholderItem()) {
-+ return NS_ERROR_FAILURE;
++ if (!IsInBatchedUpdate()) {
++ EnsureNoPlaceholderItem();
+ }
+
-+ SetDidStructureMutate(true);
-+
-+ return nsMenuContainer::InsertChildAfter(aChild, aPrevSibling,
-+ !IsInUpdateBatch());
++ nsMenuContainer::InsertChildAfter(Move(aChild), aPrevSibling,
++ !IsInBatchedUpdate());
++ StructureMutated();
+}
+
-+nsresult
-+nsMenu::AppendChild(nsMenuObject *aChild)
++void
++nsMenu::AppendChild(UniquePtr<nsMenuObject> aChild)
+{
-+ if (!IsInUpdateBatch() && !EnsureNoPlaceholderItem()) {
-+ return NS_ERROR_FAILURE;
++ if (!IsInBatchedUpdate()) {
++ EnsureNoPlaceholderItem();
+ }
+
-+ SetDidStructureMutate(true);
-+
-+ return nsMenuContainer::AppendChild(aChild, !IsInUpdateBatch());
++ nsMenuContainer::AppendChild(Move(aChild), !IsInBatchedUpdate());
++ StructureMutated();
+}
+
-+bool
-+nsMenu::CanOpen() const
++bool
++nsMenu::IsInBatchedUpdate() const
+{
-+ bool isVisible = dbusmenu_menuitem_property_get_bool(GetNativeData(),
-+ DBUSMENU_MENUITEM_PROP_VISIBLE);
-+ bool isDisabled = ContentNode()->AttrValueIs(kNameSpaceID_None,
-+ nsGkAtoms::disabled,
-+ nsGkAtoms::_true,
-+ eCaseMatters);
-+
-+ return (isVisible && !isDisabled);
-+}
-+
-+nsMenuObject::PropertyFlags
-+nsMenu::SupportedProperties() const
-+{
-+ return static_cast<nsMenuObject::PropertyFlags>(
-+ nsMenuObject::ePropLabel |
-+ nsMenuObject::ePropEnabled |
-+ nsMenuObject::ePropVisible |
-+ nsMenuObject::ePropIconData |
-+ nsMenuObject::ePropChildDisplay
-+ );
++ return mBatchedUpdateState != eBatchedUpdateState_Inactive;
+}
+
-+nsMenu::nsMenu() :
-+ nsMenuContainer()
-+{
-+ MOZ_COUNT_CTOR(nsMenu);
-+}
-+
-+nsMenu::~nsMenu()
++void
++nsMenu::StructureMutated()
+{
-+ if (IsInUpdateBatch()) {
-+ EndUpdateBatch();
-+ }
-+
-+ // Although nsTArray will take care of this in its destructor,
-+ // we have to manually ensure children are removed from our native menu
-+ // item, just in case our parent recycles us
-+ while (ChildCount() > 0) {
-+ RemoveChildAt(0);
-+ }
-+
-+ EnsureNoPlaceholderItem();
-+
-+ if (DocListener() && mPopupContent) {
-+ DocListener()->UnregisterForContentChanges(mPopupContent);
-+ }
-+
-+ if (GetNativeData()) {
-+ g_signal_handlers_disconnect_by_func(GetNativeData(),
-+ FuncToGpointer(menu_event_cb),
-+ this);
++ if (!IsInBatchedUpdate()) {
++ return;
+ }
+
-+ MOZ_COUNT_DTOR(nsMenu);
++ mBatchedUpdateState = eBatchedUpdateState_DidMutate;
+}
+
-+/* static */ nsMenuObject*
-+nsMenu::Create(nsMenuContainer *aParent, nsIContent *aContent)
++bool
++nsMenu::CanOpen() const
+{
-+ nsAutoPtr<nsMenu> menu(new nsMenu());
-+ if (NS_FAILED(menu->Init(aParent, aContent))) {
-+ return nullptr;
-+ }
++ bool isVisible = dbusmenu_menuitem_property_get_bool(GetNativeData(),
++ DBUSMENU_MENUITEM_PROP_VISIBLE);
++ bool isDisabled = ContentNode()->AsElement()->AttrValueIs(kNameSpaceID_None,
++ nsGkAtoms::disabled,
++ nsGkAtoms::_true,
++ eCaseMatters);
+
-+ return menu.forget();
++ return (isVisible && !isDisabled);
+}
+
-+static void
-+DoOpen(nsITimer *aTimer, void *aClosure)
++void
++nsMenu::HandleContentInserted(nsIContent *aContainer,
++ nsIContent *aChild,
++ nsIContent *aPrevSibling)
+{
-+ nsAutoWeakMenuObject<nsMenu> weakMenu(
-+ static_cast<nsWeakMenuObject<nsMenu> *>(aClosure));
++ if (aContainer == mPopupContent) {
++ UniquePtr<nsMenuObject> child = CreateChild(aChild);
+
-+ if (weakMenu) {
-+ dbusmenu_menuitem_show_to_user(weakMenu->GetNativeData(), 0);
++ if (child) {
++ InsertChildAfter(Move(child), aPrevSibling);
++ }
++ } else {
++ Build();
+ }
-+
-+ NS_RELEASE(aTimer);
-+}
-+
-+nsMenuObject::EType
-+nsMenu::Type() const
-+{
-+ return nsMenuObject::eType_Menu;
-+}
-+
-+bool
-+nsMenu::IsBeingDisplayed() const
-+{
-+ return PopupState() == ePopupState_Open;
+}
+
-+bool
-+nsMenu::NeedsRebuild() const
++void
++nsMenu::HandleContentRemoved(nsIContent *aContainer, nsIContent *aChild)
+{
-+ return HasFlags(eFlag_NeedsRebuild);
++ if (aContainer == mPopupContent) {
++ RemoveChild(aChild);
++ } else {
++ Build();
++ }
+}
+
+void
-+nsMenu::OpenMenu()
++nsMenu::InitializeNativeData()
+{
-+ if (!CanOpen()) {
-+ return;
-+ }
-+
-+ // Here, we synchronously fire popupshowing and popupshown events and then
-+ // open the menu after a short delay. This allows the menu to refresh before
-+ // it's shown, and avoids an issue where keyboard focus is not on the first
-+ // item of the history menu in Firefox when opening it with the keyboard,
-+ // because extra items to appear at the top of the menu
-+
-+ OnOpen();
-+
-+ nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
-+ if (!timer) {
-+ return;
-+ }
++ // Dbusmenu provides an "about-to-show" signal, and also "opened" and
++ // "closed" events. However, Unity is the only thing that sends
++ // both "about-to-show" and "opened" events. Unity 2D and the HUD only
++ // send "opened" events, so we ignore "about-to-show" (I don't think
++ // there's any real difference between them anyway).
++ // To complicate things, there are certain conditions where we don't
++ // get a "closed" event, so we need to be able to handle this :/
++ g_signal_connect(G_OBJECT(GetNativeData()), "event",
++ G_CALLBACK(menu_event_cb), this);
+
-+ nsAutoWeakMenuObject<nsMenu> weakMenu(this);
++ mNeedsRebuild = true;
++ mNeedsUpdate = true;
+
-+ if (NS_FAILED(timer->InitWithFuncCallback(DoOpen, weakMenu.getWeakPtr(),
-+ 100, nsITimer::TYPE_ONE_SHOT))) {
-+ return;
-+ }
++ MaybeAddPlaceholderItem();
+
-+ timer.forget();
-+ weakMenu.forget();
++ AttachXBLBindings(ContentNode());
+}
+
+void
-+nsMenu::OnClose()
++nsMenu::Update(nsStyleContext *aStyleContext)
+{
-+ if (PopupState() == ePopupState_Closed) {
-+ return;
-+ }
-+
-+ // We do this to avoid mutating our view of the menu until
-+ // after we have finished
-+ nsNativeMenuAutoUpdateBatch batch;
-+
-+ SetPopupState(ePopupState_Hiding);
-+ DispatchMouseEvent(mPopupContent, eXULPopupHiding);
++ if (mNeedsUpdate) {
++ mNeedsUpdate = false;
+
-+ // Sigh, make sure all of our descendants are closed, as we don't
-+ // always get closed events for submenus when scrubbing quickly through
-+ // the menu
-+ size_t count = ChildCount();
-+ for (size_t i = 0; i < count; ++i) {
-+ if (ChildAt(i)->Type() == nsMenuObject::eType_Menu) {
-+ static_cast<nsMenu *>(ChildAt(i))->OnClose();
-+ }
++ UpdateLabel();
++ UpdateSensitivity();
+ }
+
-+ SetPopupState(ePopupState_Closed);
-+ DispatchMouseEvent(mPopupContent, eXULPopupHidden);
++ UpdateVisibility(aStyleContext);
++ UpdateIcon(aStyleContext);
++}
+
-+ ContentNode()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::open, true);
++nsMenuObject::PropertyFlags
++nsMenu::SupportedProperties() const
++{
++ return static_cast<nsMenuObject::PropertyFlags>(
++ nsMenuObject::ePropLabel |
++ nsMenuObject::ePropEnabled |
++ nsMenuObject::ePropVisible |
++ nsMenuObject::ePropIconData |
++ nsMenuObject::ePropChildDisplay
++ );
+}
+
+void
-+nsMenu::OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute)
++nsMenu::OnAttributeChanged(nsIContent *aContent, nsAtom *aAttribute)
+{
-+ NS_ASSERTION(aContent == ContentNode() || aContent == mPopupContent,
-+ "Received an event that wasn't meant for us!");
++ MOZ_ASSERT(aContent == ContentNode() || aContent == mPopupContent,
++ "Received an event that wasn't meant for us!");
+
-+ if (aAttribute == nsGkAtoms::open) {
++ if (mNeedsUpdate) {
+ return;
+ }
+
-+ if (Parent()->NeedsRebuild()) {
++ if (aContent != ContentNode()) {
+ return;
+ }
+
-+ if (aContent == ContentNode()) {
-+ if (aAttribute == nsGkAtoms::disabled) {
-+ UpdateSensitivity();
-+ } else if (aAttribute == nsGkAtoms::label ||
-+ aAttribute == nsGkAtoms::accesskey ||
-+ aAttribute == nsGkAtoms::crop) {
-+ UpdateLabel();
-+ }
-+ }
-+
-+ if (!Parent()->IsBeingDisplayed() || aContent != ContentNode()) {
++ if (!Parent()->IsBeingDisplayed()) {
++ mNeedsUpdate = true;
+ return;
+ }
+
-+ if (aAttribute == nsGkAtoms::hidden ||
++ if (aAttribute == nsGkAtoms::disabled) {
++ UpdateSensitivity();
++ } else if (aAttribute == nsGkAtoms::label ||
++ aAttribute == nsGkAtoms::accesskey ||
++ aAttribute == nsGkAtoms::crop) {
++ UpdateLabel();
++ } else if (aAttribute == nsGkAtoms::hidden ||
+ aAttribute == nsGkAtoms::collapsed) {
+ RefPtr<nsStyleContext> sc = GetStyleContext();
+ UpdateVisibility(sc);
@@ -931,57 +912,40 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+nsMenu::OnContentInserted(nsIContent *aContainer, nsIContent *aChild,
+ nsIContent *aPrevSibling)
+{
-+ NS_ASSERTION(aContainer == ContentNode() || aContainer == mPopupContent,
-+ "Received an event that wasn't meant for us!");
++ MOZ_ASSERT(aContainer == ContentNode() || aContainer == mPopupContent,
++ "Received an event that wasn't meant for us!");
+
-+ if (NeedsRebuild()) {
++ if (mNeedsRebuild) {
+ return;
+ }
+
-+ if (PopupState() == ePopupState_Closed) {
-+ SetNeedsRebuild(true);
++ if (mPopupState == ePopupState_Closed) {
++ mNeedsRebuild = true;
+ return;
+ }
+
-+ if (aContainer == mPopupContent) {
-+ nsresult rv;
-+ nsMenuObject *child = CreateChild(aChild, &rv);
-+
-+ if (child) {
-+ rv = InsertChildAfter(child, aPrevSibling);
-+ }
-+ if (NS_FAILED(rv)) {
-+ NS_ERROR("OnContentInserted() failed");
-+ SetNeedsRebuild(true);
-+ }
-+ } else {
-+ Build();
-+ }
++ nsContentUtils::AddScriptRunner(
++ new nsMenuContentInsertedEvent(this, aContainer, aChild,
++ aPrevSibling));
+}
+
+void
+nsMenu::OnContentRemoved(nsIContent *aContainer, nsIContent *aChild)
+{
-+ NS_ASSERTION(aContainer == ContentNode() || aContainer == mPopupContent,
-+ "Received an event that wasn't meant for us!");
++ MOZ_ASSERT(aContainer == ContentNode() || aContainer == mPopupContent,
++ "Received an event that wasn't meant for us!");
+
-+ if (NeedsRebuild()) {
++ if (mNeedsRebuild) {
+ return;
+ }
+
-+ if (PopupState() == ePopupState_Closed) {
-+ SetNeedsRebuild(true);
++ if (mPopupState == ePopupState_Closed) {
++ mNeedsRebuild = true;
+ return;
+ }
+
-+ if (aContainer == mPopupContent) {
-+ if (NS_FAILED(RemoveChild(aChild))) {
-+ NS_ERROR("OnContentRemoved() failed");
-+ SetNeedsRebuild(true);
-+ }
-+ } else {
-+ Build();
-+ }
++ nsContentUtils::AddScriptRunner(
++ new nsMenuContentRemovedEvent(this, aContainer, aChild));
+}
+
+/*
@@ -998,32 +962,35 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+ */
+
+void
-+nsMenu::BeginUpdateBatch(nsIContent *aContent)
++nsMenu::OnBeginUpdates(nsIContent *aContent)
+{
-+ NS_ASSERTION(aContent == ContentNode() || aContent == mPopupContent,
-+ "Received an event that wasn't meant for us!");
++ MOZ_ASSERT(aContent == ContentNode() || aContent == mPopupContent,
++ "Received an event that wasn't meant for us!");
++ MOZ_ASSERT(!IsInBatchedUpdate(), "Already in an update batch!");
+
-+ if (aContent == mPopupContent) {
-+ BeginUpdateBatchInternal();
++ if (aContent != mPopupContent) {
++ return;
+ }
++
++ mBatchedUpdateState = eBatchedUpdateState_Active;
+}
+
+void
-+nsMenu::EndUpdateBatch()
++nsMenu::OnEndUpdates()
+{
-+ NS_ASSERTION(IsInUpdateBatch(), "Not in an update batch");
++ if (!IsInBatchedUpdate()) {
++ return;
++ }
+
-+ SetIsInUpdateBatch(false);
++ bool didMutate = mBatchedUpdateState == eBatchedUpdateState_DidMutate;
++ mBatchedUpdateState = eBatchedUpdateState_Inactive;
+
+ /* Optimize for the case where we only had attribute changes */
-+ if (!DidStructureMutate()) {
++ if (!didMutate) {
+ return;
+ }
+
-+ if (!EnsureNoPlaceholderItem()) {
-+ SetNeedsRebuild(true);
-+ return;
-+ }
++ EnsureNoPlaceholderItem();
+
+ GList *nextNativeChild = dbusmenu_menuitem_get_children(GetNativeData());
+ DbusmenuMenuitem *nextOwnedNativeChild = nullptr;
@@ -1054,11 +1021,8 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+ static_cast<DbusmenuMenuitem *>(nextNativeChild->data);
+ nextNativeChild = nextNativeChild->next;
+
-+ if (!dbusmenu_menuitem_child_delete(GetNativeData(), data)) {
-+ NS_ERROR("Failed to remove orphaned native item from menu");
-+ SetNeedsRebuild(true);
-+ return;
-+ }
++ MOZ_ALWAYS_TRUE(dbusmenu_menuitem_child_delete(GetNativeData(),
++ data));
+ }
+
+ if (nextNativeChild) {
@@ -1090,37 +1054,151 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.cpp
+ // At this point, we modify the native menu structure.
+ if (!child->GetNativeData()) {
+ child->CreateNativeData();
-+ if (!dbusmenu_menuitem_child_add_position(GetNativeData(),
-+ child->GetNativeData(),
-+ i)) {
-+ NS_ERROR("Failed to add new native item");
-+ SetNeedsRebuild(true);
-+ return;
-+ }
++ MOZ_ALWAYS_TRUE(
++ dbusmenu_menuitem_child_add_position(GetNativeData(),
++ child->GetNativeData(),
++ i));
+ }
+ }
+ }
+
+ while (nextNativeChild) {
-+
+ DbusmenuMenuitem *data =
+ static_cast<DbusmenuMenuitem *>(nextNativeChild->data);
+ nextNativeChild = nextNativeChild->next;
+
-+ if (!dbusmenu_menuitem_child_delete(GetNativeData(), data)) {
-+ NS_ERROR("Failed to remove orphaned native item from menu");
-+ SetNeedsRebuild(true);
-+ return;
-+ }
++ MOZ_ALWAYS_TRUE(dbusmenu_menuitem_child_delete(GetNativeData(), data));
+ }
+
+ MaybeAddPlaceholderItem();
+}
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.h
-===================================================================
++
++nsMenu::nsMenu(nsMenuContainer *aParent, nsIContent *aContent) :
++ nsMenuContainer(aParent, aContent),
++ mNeedsRebuild(false),
++ mNeedsUpdate(false),
++ mPlaceholderItem(nullptr),
++ mPopupState(ePopupState_Closed),
++ mBatchedUpdateState(eBatchedUpdateState_Inactive)
++{
++ MOZ_COUNT_CTOR(nsMenu);
++}
++
++nsMenu::~nsMenu()
++{
++ if (IsInBatchedUpdate()) {
++ OnEndUpdates();
++ }
++
++ // Although nsTArray will take care of this in its destructor,
++ // we have to manually ensure children are removed from our native menu
++ // item, just in case our parent recycles us
++ while (ChildCount() > 0) {
++ RemoveChildAt(0);
++ }
++
++ EnsureNoPlaceholderItem();
++
++ if (DocListener() && mPopupContent) {
++ DocListener()->UnregisterForContentChanges(mPopupContent);
++ }
++
++ if (GetNativeData()) {
++ g_signal_handlers_disconnect_by_func(GetNativeData(),
++ FuncToGpointer(menu_event_cb),
++ this);
++ }
++
++ MOZ_COUNT_DTOR(nsMenu);
++}
++
++nsMenuObject::EType
++nsMenu::Type() const
++{
++ return eType_Menu;
++}
++
++bool
++nsMenu::IsBeingDisplayed() const
++{
++ return mPopupState == ePopupState_Open;
++}
++
++bool
++nsMenu::NeedsRebuild() const
++{
++ return mNeedsRebuild;
++}
++
++void
++nsMenu::OpenMenu()
++{
++ if (!CanOpen()) {
++ return;
++ }
++
++ if (mOpenDelayTimer) {
++ return;
++ }
++
++ // Here, we synchronously fire popupshowing and popupshown events and then
++ // open the menu after a short delay. This allows the menu to refresh before
++ // it's shown, and avoids an issue where keyboard focus is not on the first
++ // item of the history menu in Firefox when opening it with the keyboard,
++ // because extra items to appear at the top of the menu
++
++ OnOpen();
++
++ mOpenDelayTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
++ if (!mOpenDelayTimer) {
++ return;
++ }
++
++ if (NS_FAILED(mOpenDelayTimer->InitWithNamedFuncCallback(DoOpenCallback,
++ this,
++ 100,
++ nsITimer::TYPE_ONE_SHOT,
++ "nsMenu::DoOpenCallback"))) {
++ mOpenDelayTimer = nullptr;
++ }
++}
++
++void
++nsMenu::OnClose()
++{
++ if (mPopupState == ePopupState_Closed) {
++ return;
++ }
++
++ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
++
++ // We do this to avoid mutating our view of the menu until
++ // after we have finished
++ nsNativeMenuDocListener::BlockUpdatesScope updatesBlocker;
++
++ SetPopupState(ePopupState_Hiding);
++ DispatchMouseEvent(mPopupContent, eXULPopupHiding);
++
++ // Sigh, make sure all of our descendants are closed, as we don't
++ // always get closed events for submenus when scrubbing quickly through
++ // the menu
++ size_t count = ChildCount();
++ for (size_t i = 0; i < count; ++i) {
++ if (ChildAt(i)->Type() == nsMenuObject::eType_Menu) {
++ static_cast<nsMenu *>(ChildAt(i))->OnClose();
++ }
++ }
++
++ SetPopupState(ePopupState_Closed);
++ DispatchMouseEvent(mPopupContent, eXULPopupHidden);
++
++ ContentNode()->AsElement()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::open,
++ true);
++}
++
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.h
-@@ -0,0 +1,166 @@
++++ b/widget/gtk/nsMenu.h
+@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -1132,6 +1210,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.h
+#define __nsMenu_h__
+
+#include "mozilla/Attributes.h"
++#include "mozilla/UniquePtr.h"
+#include "nsCOMPtr.h"
+
+#include "nsDbusmenu.h"
@@ -1140,8 +1219,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.h
+
+#include <glib.h>
+
-+class nsIAtom;
++class nsAtom;
+class nsIContent;
++class nsITimer;
+class nsStyleContext;
+
+#define NSMENU_NUMBER_OF_POPUPSTATE_BITS 2U
@@ -1151,15 +1231,13 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.h
+class nsMenu final : public nsMenuContainer
+{
+public:
++ nsMenu(nsMenuContainer *aParent, nsIContent *aContent);
+ ~nsMenu();
+
-+ static nsMenuObject* Create(nsMenuContainer *aParent,
-+ nsIContent *aContent);
++ nsMenuObject::EType Type() const override;
+
-+ nsMenuObject::EType Type() const;
-+
-+ bool IsBeingDisplayed() const;
-+ bool NeedsRebuild() const;
++ bool IsBeingDisplayed() const override;
++ bool NeedsRebuild() const override;
+
+ // Tell the desktop shell to display this menu
+ void OpenMenu();
@@ -1168,31 +1246,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.h
+ // menuitems can do the shells work. Sigh....
+ void OnClose();
+
-+ void OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute);
-+ void OnContentInserted(nsIContent *aContainer, nsIContent *aChild,
-+ nsIContent *aPrevSibling);
-+ void OnContentRemoved(nsIContent *aContainer, nsIContent *aChild);
-+ void BeginUpdateBatch(nsIContent *aContent);
-+ void EndUpdateBatch();
-+
+private:
-+ friend class nsMenuUpdateBatch;
-+
-+ enum {
-+ // This menu needs rebuilding the next time it is opened
-+ eFlag_NeedsRebuild = 1 << 0,
-+
-+ // This menu contains a placeholder
-+ eFlag_HasPlaceholderItem = 1 << 1,
-+
-+ // This menu is currently receiving a batch of updates, and
-+ // the native structure should not be modified
-+ eFlag_InUpdateBatch = 1 << 2,
-+
-+ // Children were added to / removed from this menu (only valid
-+ // when eFlag_InUpdateBatch is set)
-+ eFlag_StructureMutated = 1 << 3
-+ };
++ friend class nsMenuContentInsertedEvent;
++ friend class nsMenuContentRemovedEvent;
+
+ enum EPopupState {
+ ePopupState_Closed,
@@ -1201,64 +1257,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.h
+ ePopupState_Hiding
+ };
+
-+ nsMenu();
-+
-+ void SetNeedsRebuild(bool aValue)
-+ {
-+ if (aValue) {
-+ SetFlags(eFlag_NeedsRebuild);
-+ } else {
-+ ClearFlags(eFlag_NeedsRebuild);
-+ }
-+ }
-+ bool HasPlaceholderItem() const
-+ {
-+ return HasFlags(eFlag_HasPlaceholderItem);
-+ }
-+ void SetHasPlaceholderItem(bool aValue)
-+ {
-+ if (aValue) {
-+ SetFlags(eFlag_HasPlaceholderItem);
-+ } else {
-+ ClearFlags(eFlag_HasPlaceholderItem);
-+ }
-+ }
-+
-+ bool IsInUpdateBatch() const
-+ {
-+ return HasFlags(eFlag_InUpdateBatch);
-+ }
-+ void SetIsInUpdateBatch(bool aValue)
-+ {
-+ if (aValue) {
-+ SetFlags(eFlag_InUpdateBatch);
-+ } else {
-+ ClearFlags(eFlag_InUpdateBatch);
-+ }
-+ }
-+
-+ bool DidStructureMutate() const
-+ {
-+ return HasFlags(eFlag_StructureMutated);
-+ }
-+ void SetDidStructureMutate(bool aValue)
-+ {
-+ if (aValue) {
-+ SetFlags(eFlag_StructureMutated);
-+ } else {
-+ ClearFlags(eFlag_StructureMutated);
-+ }
-+ }
-+
-+ EPopupState PopupState() const
-+ {
-+ return static_cast<EPopupState>(
-+ (GetFlags() &
-+ (((1U << NSMENU_NUMBER_OF_POPUPSTATE_BITS) - 1U)
-+ << NSMENU_NUMBER_OF_FLAGS)) >> NSMENU_NUMBER_OF_FLAGS);
-+ };
+ void SetPopupState(EPopupState aState);
+
++ static void DoOpenCallback(nsITimer *aTimer, void *aClosure);
+ static void menu_event_cb(DbusmenuMenuitem *menu,
+ const gchar *name,
+ GVariant *value,
@@ -1268,30 +1269,63 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenu.h
+ // We add a placeholder item to empty menus so that Unity actually treats
+ // us as a proper menu, rather than a menuitem without a submenu
+ void MaybeAddPlaceholderItem();
-+ bool EnsureNoPlaceholderItem();
++
++ // Removes a placeholder item if it exists and asserts that this succeeds
++ void EnsureNoPlaceholderItem();
+
+ void OnOpen();
+ void Build();
-+ void InitializeNativeData();
-+ void Update(nsStyleContext *aStyleContext);
+ void InitializePopup();
-+ void BeginUpdateBatchInternal();
-+ nsresult RemoveChildAt(size_t aIndex);
-+ nsresult RemoveChild(nsIContent *aChild);
-+ nsresult InsertChildAfter(nsMenuObject *aChild, nsIContent *aPrevSibling);
-+ nsresult AppendChild(nsMenuObject *aChild);
++ void RemoveChildAt(size_t aIndex);
++ void RemoveChild(nsIContent *aChild);
++ void InsertChildAfter(mozilla::UniquePtr<nsMenuObject> aChild,
++ nsIContent *aPrevSibling);
++ void AppendChild(mozilla::UniquePtr<nsMenuObject> aChild);
++ bool IsInBatchedUpdate() const;
++ void StructureMutated();
+ bool CanOpen() const;
-+ nsMenuObject::PropertyFlags SupportedProperties() const;
++
++ void HandleContentInserted(nsIContent *aContainer,
++ nsIContent *aChild,
++ nsIContent *aPrevSibling);
++ void HandleContentRemoved(nsIContent *aContainer,
++ nsIContent *aChild);
++
++ void InitializeNativeData() override;
++ void Update(nsStyleContext *aStyleContext) override;
++ nsMenuObject::PropertyFlags SupportedProperties() const override;
++
++ void OnAttributeChanged(nsIContent *aContent, nsAtom *aAttribute) override;
++ void OnContentInserted(nsIContent *aContainer, nsIContent *aChild,
++ nsIContent *aPrevSibling) override;
++ void OnContentRemoved(nsIContent *aContainer, nsIContent *aChild) override;
++ void OnBeginUpdates(nsIContent *aContent) override;
++ void OnEndUpdates() override;
++
++ bool mNeedsRebuild;
++ bool mNeedsUpdate;
++
++ DbusmenuMenuitem *mPlaceholderItem;
++
++ EPopupState mPopupState;
++
++ enum EBatchedUpdateState {
++ eBatchedUpdateState_Inactive,
++ eBatchedUpdateState_Active,
++ eBatchedUpdateState_DidMutate
++ };
++
++ EBatchedUpdateState mBatchedUpdateState;
+
+ nsCOMPtr<nsIContent> mPopupContent;
++
++ nsCOMPtr<nsITimer> mOpenDelayTimer;
+};
+
+#endif /* __nsMenu_h__ */
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
-@@ -0,0 +1,545 @@
++++ b/widget/gtk/nsMenuBar.cpp
+@@ -0,0 +1,558 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -1299,15 +1333,21 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
++#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/dom/Element.h"
++#include "mozilla/dom/Event.h"
++#include "mozilla/dom/KeyboardEvent.h"
++#include "mozilla/dom/KeyboardEventBinding.h"
++#include "mozilla/Move.h"
+#include "mozilla/Preferences.h"
+#include "nsAutoPtr.h"
++#include "nsContentUtils.h"
+#include "nsIDocument.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMEventListener.h"
+#include "nsIDOMEventTarget.h"
-+#include "nsIDOMKeyEvent.h"
++#include "nsIRunnable.h"
+#include "nsIWidget.h"
+#include "nsTArray.h"
+#include "nsUnicharUtils.h"
@@ -1325,24 +1365,84 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+
+using namespace mozilla;
+
-+class nsMenuBarDocEventListener final : public nsIDOMEventListener
++static bool
++ShouldHandleKeyEvent(dom::KeyboardEvent *aEvent)
++{
++ return !aEvent->DefaultPrevented() && aEvent->IsTrusted();
++}
++
++class nsMenuBarContentInsertedEvent : public Runnable
++{
++public:
++ nsMenuBarContentInsertedEvent(nsMenuBar *aMenuBar,
++ nsIContent *aChild,
++ nsIContent *aPrevSibling) :
++ Runnable("nsMenuBarContentInsertedEvent"),
++ mWeakMenuBar(aMenuBar),
++ mChild(aChild),
++ mPrevSibling(aPrevSibling) { }
++
++ NS_IMETHODIMP Run()
++ {
++ if (!mWeakMenuBar) {
++ return NS_OK;
++ }
++
++ static_cast<nsMenuBar *>(mWeakMenuBar.get())->HandleContentInserted(mChild,
++ mPrevSibling);
++ return NS_OK;
++ }
++
++private:
++ nsWeakMenuObject mWeakMenuBar;
++
++ nsCOMPtr<nsIContent> mChild;
++ nsCOMPtr<nsIContent> mPrevSibling;
++};
++
++class nsMenuBarContentRemovedEvent : public Runnable
++{
++public:
++ nsMenuBarContentRemovedEvent(nsMenuBar *aMenuBar,
++ nsIContent *aChild) :
++ Runnable("nsMenuBarContentRemovedEvent"),
++ mWeakMenuBar(aMenuBar),
++ mChild(aChild) { }
++
++ NS_IMETHODIMP Run()
++ {
++ if (!mWeakMenuBar) {
++ return NS_OK;
++ }
++
++ static_cast<nsMenuBar *>(mWeakMenuBar.get())->HandleContentRemoved(mChild);
++ return NS_OK;
++ }
++
++private:
++ nsWeakMenuObject mWeakMenuBar;
++
++ nsCOMPtr<nsIContent> mChild;
++};
++
++class nsMenuBar::DocEventListener final : public nsIDOMEventListener
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMEVENTLISTENER
+
-+ nsMenuBarDocEventListener(nsMenuBar *aOwner) : mOwner(aOwner) { };
++ DocEventListener(nsMenuBar *aOwner) : mOwner(aOwner) { };
+
+private:
-+ ~nsMenuBarDocEventListener() { };
++ ~DocEventListener() { };
+
+ nsMenuBar *mOwner;
+};
+
-+NS_IMPL_ISUPPORTS(nsMenuBarDocEventListener, nsIDOMEventListener)
++NS_IMPL_ISUPPORTS(nsMenuBar::DocEventListener, nsIDOMEventListener)
+
+NS_IMETHODIMP
-+nsMenuBarDocEventListener::HandleEvent(nsIDOMEvent *aEvent)
++nsMenuBar::DocEventListener::HandleEvent(nsIDOMEvent *aEvent)
+{
+ nsAutoString type;
+ nsresult rv = aEvent->GetType(type);
@@ -1355,15 +1455,95 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+ mOwner->Focus();
+ } else if (type.Equals(NS_LITERAL_STRING("blur"))) {
+ mOwner->Blur();
-+ } else if (type.Equals(NS_LITERAL_STRING("keypress"))) {
-+ rv = mOwner->Keypress(aEvent);
++ }
++
++ RefPtr<dom::KeyboardEvent> keyEvent =
++ aEvent->InternalDOMEvent()->AsKeyboardEvent();
++ if (!keyEvent) {
++ return NS_OK;
++ }
++
++ if (type.Equals(NS_LITERAL_STRING("keypress"))) {
++ return mOwner->Keypress(keyEvent);
+ } else if (type.Equals(NS_LITERAL_STRING("keydown"))) {
-+ rv = mOwner->KeyDown(aEvent);
++ return mOwner->KeyDown(keyEvent);
+ } else if (type.Equals(NS_LITERAL_STRING("keyup"))) {
-+ rv = mOwner->KeyUp(aEvent);
++ return mOwner->KeyUp(keyEvent);
++ }
++
++ return NS_OK;
++}
++
++nsMenuBar::nsMenuBar(nsIContent *aMenuBarNode) :
++ nsMenuContainer(new nsNativeMenuDocListener(aMenuBarNode), aMenuBarNode),
++ mTopLevel(nullptr),
++ mServer(nullptr),
++ mIsActive(false)
++{
++ MOZ_COUNT_CTOR(nsMenuBar);
++}
++
++nsresult
++nsMenuBar::Init(nsIWidget *aParent)
++{
++ MOZ_ASSERT(aParent);
++
++ GdkWindow *gdkWin = static_cast<GdkWindow *>(
++ aParent->GetNativeData(NS_NATIVE_WINDOW));
++ if (!gdkWin) {
++ return NS_ERROR_FAILURE;
++ }
++
++ gpointer user_data = nullptr;
++ gdk_window_get_user_data(gdkWin, &user_data);
++ if (!user_data || !GTK_IS_CONTAINER(user_data)) {
++ return NS_ERROR_FAILURE;
++ }
++
++ mTopLevel = gtk_widget_get_toplevel(GTK_WIDGET(user_data));
++ if (!mTopLevel) {
++ return NS_ERROR_FAILURE;
++ }
++
++ g_object_ref(mTopLevel);
++
++ nsAutoCString path;
++ path.Append(NS_LITERAL_CSTRING("/com/canonical/menu/"));
++ char xid[10];
++ sprintf(xid, "%X", static_cast<uint32_t>(
++ GDK_WINDOW_XID(gtk_widget_get_window(mTopLevel))));
++ path.Append(xid);
++
++ mServer = dbusmenu_server_new(path.get());
++ if (!mServer) {
++ return NS_ERROR_FAILURE;
+ }
+
-+ return rv;
++ CreateNativeData();
++ if (!GetNativeData()) {
++ return NS_ERROR_FAILURE;
++ }
++
++ dbusmenu_server_set_root(mServer, GetNativeData());
++
++ mEventListener = new DocEventListener(this);
++
++ mDocument = do_QueryInterface(ContentNode()->OwnerDoc());
++
++ mAccessKey = Preferences::GetInt("ui.key.menuAccessKey");
++ if (mAccessKey == dom::KeyboardEventBinding::DOM_VK_SHIFT) {
++ mAccessKeyMask = eModifierShift;
++ } else if (mAccessKey == dom::KeyboardEventBinding::DOM_VK_CONTROL) {
++ mAccessKeyMask = eModifierCtrl;
++ } else if (mAccessKey == dom::KeyboardEventBinding::DOM_VK_ALT) {
++ mAccessKeyMask = eModifierAlt;
++ } else if (mAccessKey == dom::KeyboardEventBinding::DOM_VK_META) {
++ mAccessKeyMask = eModifierMeta;
++ } else {
++ mAccessKeyMask = eModifierAlt;
++ }
++
++ return NS_OK;
+}
+
+void
@@ -1371,19 +1551,15 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+{
+ uint32_t count = ContentNode()->GetChildCount();
+ for (uint32_t i = 0; i < count; ++i) {
-+ nsIContent *childContent = ContentNode()->GetChildAt(i);
++ nsIContent *childContent = ContentNode()->GetChildAt_Deprecated(i);
+
-+ nsresult rv;
-+ nsMenuObject *child = CreateChild(childContent, &rv);
++ UniquePtr<nsMenuObject> child = CreateChild(childContent);
+
-+ if (child) {
-+ rv = AppendChild(child);
++ if (!child) {
++ continue;
+ }
+
-+ if (NS_FAILED(rv)) {
-+ NS_ERROR("Failed to build menubar");
-+ return;
-+ }
++ AppendChild(Move(child));
+ }
+}
+
@@ -1419,8 +1595,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+void
+nsMenuBar::Focus()
+{
-+ ContentNode()->SetAttr(kNameSpaceID_None, nsNativeMenuAtoms::openedwithkey,
-+ NS_LITERAL_STRING("false"), true);
++ ContentNode()->AsElement()->SetAttr(kNameSpaceID_None,
++ nsNativeMenuAtoms::openedwithkey,
++ NS_LITERAL_STRING("false"), true);
+}
+
+void
@@ -1432,43 +1609,24 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+ dbusmenu_server_set_status(mServer, DBUSMENU_STATUS_NORMAL);
+}
+
-+static bool
-+ShouldHandleKeyEvent(nsIDOMEvent *aEvent)
-+{
-+ bool handled, trusted = false;
-+ aEvent->GetPreventDefault(&handled);
-+ aEvent->GetIsTrusted(&trusted);
-+
-+ if (handled || !trusted) {
-+ return false;
-+ }
-+
-+ return true;
-+}
-+
+nsMenuBar::ModifierFlags
-+nsMenuBar::GetModifiersFromEvent(nsIDOMKeyEvent *aEvent)
++nsMenuBar::GetModifiersFromEvent(dom::KeyboardEvent *aEvent)
+{
+ ModifierFlags modifiers = static_cast<ModifierFlags>(0);
-+ bool modifier;
+
-+ aEvent->GetAltKey(&modifier);
-+ if (modifier) {
++ if (aEvent->AltKey()) {
+ modifiers = static_cast<ModifierFlags>(modifiers | eModifierAlt);
+ }
+
-+ aEvent->GetShiftKey(&modifier);
-+ if (modifier) {
++ if (aEvent->ShiftKey()) {
+ modifiers = static_cast<ModifierFlags>(modifiers | eModifierShift);
+ }
+
-+ aEvent->GetCtrlKey(&modifier);
-+ if (modifier) {
++ if (aEvent->CtrlKey()) {
+ modifiers = static_cast<ModifierFlags>(modifiers | eModifierCtrl);
+ }
+
-+ aEvent->GetMetaKey(&modifier);
-+ if (modifier) {
++ if (aEvent->MetaKey()) {
+ modifiers = static_cast<ModifierFlags>(modifiers | eModifierMeta);
+ }
+
@@ -1476,25 +1634,19 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+}
+
+nsresult
-+nsMenuBar::Keypress(nsIDOMEvent *aEvent)
++nsMenuBar::Keypress(dom::KeyboardEvent *aEvent)
+{
+ if (!ShouldHandleKeyEvent(aEvent)) {
+ return NS_OK;
+ }
+
-+ nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
-+ if (!keyEvent) {
-+ return NS_OK;
-+ }
-+
-+ ModifierFlags modifiers = GetModifiersFromEvent(keyEvent);
++ ModifierFlags modifiers = GetModifiersFromEvent(aEvent);
+ if (((modifiers & mAccessKeyMask) == 0) ||
+ ((modifiers & ~mAccessKeyMask) != 0)) {
+ return NS_OK;
+ }
+
-+ uint32_t charCode;
-+ keyEvent->GetCharCode(&charCode);
++ uint32_t charCode = aEvent->CharCode();
+ if (charCode == 0) {
+ return NS_OK;
+ }
@@ -1507,9 +1659,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+ uint32_t count = ChildCount();
+ for (uint32_t i = 0; i < count; ++i) {
+ nsAutoString accesskey;
-+ ChildAt(i)->ContentNode()->GetAttr(kNameSpaceID_None,
-+ nsGkAtoms::accesskey,
-+ accesskey);
++ ChildAt(i)->ContentNode()->AsElement()->GetAttr(kNameSpaceID_None,
++ nsGkAtoms::accesskey,
++ accesskey);
+ const nsAutoString::char_type *key = accesskey.BeginReading();
+ if (*key == chu || *key == chl) {
+ found = ChildAt(i);
@@ -1521,8 +1673,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+ return NS_OK;
+ }
+
-+ ContentNode()->SetAttr(kNameSpaceID_None, nsNativeMenuAtoms::openedwithkey,
-+ NS_LITERAL_STRING("true"), true);
++ ContentNode()->AsElement()->SetAttr(kNameSpaceID_None,
++ nsNativeMenuAtoms::openedwithkey,
++ NS_LITERAL_STRING("true"), true);
+ static_cast<nsMenu *>(found)->OpenMenu();
+
+ aEvent->StopPropagation();
@@ -1532,20 +1685,14 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+}
+
+nsresult
-+nsMenuBar::KeyDown(nsIDOMEvent *aEvent)
++nsMenuBar::KeyDown(dom::KeyboardEvent *aEvent)
+{
+ if (!ShouldHandleKeyEvent(aEvent)) {
+ return NS_OK;
+ }
+
-+ nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
-+ if (!keyEvent) {
-+ return NS_OK;
-+ }
-+
-+ uint32_t keyCode;
-+ keyEvent->GetKeyCode(&keyCode);
-+ ModifierFlags modifiers = GetModifiersFromEvent(keyEvent);
++ uint32_t keyCode = aEvent->KeyCode();
++ ModifierFlags modifiers = GetModifiersFromEvent(aEvent);
+ if ((keyCode != mAccessKey) || ((modifiers & ~mAccessKeyMask) != 0)) {
+ return NS_OK;
+ }
@@ -1556,19 +1703,13 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+}
+
+nsresult
-+nsMenuBar::KeyUp(nsIDOMEvent *aEvent)
++nsMenuBar::KeyUp(dom::KeyboardEvent *aEvent)
+{
+ if (!ShouldHandleKeyEvent(aEvent)) {
+ return NS_OK;
+ }
+
-+ nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
-+ if (!keyEvent) {
-+ return NS_OK;
-+ }
-+
-+ uint32_t keyCode;
-+ keyEvent->GetKeyCode(&keyCode);
++ uint32_t keyCode = aEvent->KeyCode();
+ if (keyCode == mAccessKey) {
+ dbusmenu_server_set_status(mServer, DBUSMENU_STATUS_NORMAL);
+ }
@@ -1576,85 +1717,43 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+ return NS_OK;
+}
+
-+nsMenuBar::nsMenuBar() :
-+ nsMenuContainer(),
-+ mTopLevel(nullptr),
-+ mServer(nullptr),
-+ mIsActive(false)
-+{
-+ MOZ_COUNT_CTOR(nsMenuBar);
-+}
-+
-+nsresult
-+nsMenuBar::Init(nsIWidget *aParent, nsIContent *aMenuBarNode)
++void
++nsMenuBar::HandleContentInserted(nsIContent *aChild, nsIContent *aPrevSibling)
+{
-+ NS_ENSURE_ARG(aParent);
-+ NS_ENSURE_ARG(aMenuBarNode);
++ UniquePtr<nsMenuObject> child = CreateChild(aChild);
+
-+ GdkWindow *gdkWin = static_cast<GdkWindow *>(
-+ aParent->GetNativeData(NS_NATIVE_WINDOW));
-+ if (!gdkWin) {
-+ return NS_ERROR_FAILURE;
-+ }
-+
-+ gpointer user_data = nullptr;
-+ gdk_window_get_user_data(gdkWin, &user_data);
-+ if (!user_data || !GTK_IS_CONTAINER(user_data)) {
-+ return NS_ERROR_FAILURE;
-+ }
-+
-+ mTopLevel = gtk_widget_get_toplevel(GTK_WIDGET(user_data));
-+ if (!mTopLevel) {
-+ return NS_ERROR_FAILURE;
-+ }
-+
-+ g_object_ref(mTopLevel);
-+
-+ RefPtr<nsNativeMenuDocListener> listener =
-+ nsNativeMenuDocListener::Create(aMenuBarNode);
-+ if (!listener) {
-+ return NS_ERROR_FAILURE;
-+ }
-+
-+ nsMenuObject::Init(listener, aMenuBarNode);
-+
-+ nsAutoCString path;
-+ path.Append(NS_LITERAL_CSTRING("/com/canonical/menu/"));
-+ char xid[10];
-+ sprintf(xid, "%X", static_cast<uint32_t>(
-+ GDK_WINDOW_XID(gtk_widget_get_window(mTopLevel))));
-+ path.Append(xid);
-+
-+ mServer = dbusmenu_server_new(path.get());
-+ if (!mServer) {
-+ return NS_ERROR_FAILURE;
++ if (!child) {
++ return;
+ }
+
-+ CreateNativeData();
-+ if (!GetNativeData()) {
-+ return NS_ERROR_FAILURE;
-+ }
++ InsertChildAfter(Move(child), aPrevSibling);
++}
+
-+ dbusmenu_server_set_root(mServer, GetNativeData());
++void
++nsMenuBar::HandleContentRemoved(nsIContent *aChild)
++{
++ RemoveChild(aChild);
++}
+
-+ mEventListener = new nsMenuBarDocEventListener(this);
++void
++nsMenuBar::OnContentInserted(nsIContent *aContainer, nsIContent *aChild,
++ nsIContent *aPrevSibling)
++{
++ MOZ_ASSERT(aContainer == ContentNode(),
++ "Received an event that wasn't meant for us");
+
-+ mDocument = do_QueryInterface(ContentNode()->OwnerDoc());
++ nsContentUtils::AddScriptRunner(
++ new nsMenuBarContentInsertedEvent(this, aChild, aPrevSibling));
++}
+
-+ mAccessKey = Preferences::GetInt("ui.key.menuAccessKey");
-+ if (mAccessKey == nsIDOMKeyEvent::DOM_VK_SHIFT) {
-+ mAccessKeyMask = eModifierShift;
-+ } else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_CONTROL) {
-+ mAccessKeyMask = eModifierCtrl;
-+ } else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_ALT) {
-+ mAccessKeyMask = eModifierAlt;
-+ } else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_META) {
-+ mAccessKeyMask = eModifierMeta;
-+ } else {
-+ mAccessKeyMask = eModifierAlt;
-+ }
++void
++nsMenuBar::OnContentRemoved(nsIContent *aContainer, nsIContent *aChild)
++{
++ MOZ_ASSERT(aContainer == ContentNode(),
++ "Received an event that wasn't meant for us");
+
-+ return NS_OK;
++ nsContentUtils::AddScriptRunner(
++ new nsMenuBarContentRemovedEvent(this, aChild));
+}
+
+nsMenuBar::~nsMenuBar()
@@ -1693,21 +1792,21 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+ MOZ_COUNT_DTOR(nsMenuBar);
+}
+
-+/* static */ nsMenuBar*
++/* static */ UniquePtr<nsMenuBar>
+nsMenuBar::Create(nsIWidget *aParent, nsIContent *aMenuBarNode)
+{
-+ nsAutoPtr<nsMenuBar> menubar(new nsMenuBar());
-+ if (NS_FAILED(menubar->Init(aParent, aMenuBarNode))) {
++ UniquePtr<nsMenuBar> menubar(new nsMenuBar(aMenuBarNode));
++ if (NS_FAILED(menubar->Init(aParent))) {
+ return nullptr;
+ }
+
-+ return menubar.forget();
++ return Move(menubar);
+}
+
+nsMenuObject::EType
+nsMenuBar::Type() const
+{
-+ return nsMenuObject::eType_MenuBar;
++ return eType_MenuBar;
+}
+
+bool
@@ -1722,34 +1821,16 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+ return static_cast<uint32_t>(GDK_WINDOW_XID(gtk_widget_get_window(mTopLevel)));
+}
+
-+nsAdoptingCString
++nsCString
+nsMenuBar::ObjectPath() const
+{
+ gchar *tmp;
+ g_object_get(mServer, DBUSMENU_SERVER_PROP_DBUS_OBJECT, &tmp, NULL);
-+ nsAdoptingCString result(tmp);
+
-+ return result;
-+}
++ nsCString result;
++ result.Adopt(tmp);
+
-+nsNativeMenuGIORequest&
-+nsMenuBar::BeginRegisterRequest()
-+{
-+ mRegisterRequestCanceller.Start();
-+ return mRegisterRequestCanceller;
-+}
-+
-+void
-+nsMenuBar::EndRegisterRequest()
-+{
-+ NS_ASSERTION(RegisterRequestInProgress(), "No request in progress");
-+ mRegisterRequestCanceller.Finish();
-+}
-+
-+bool
-+nsMenuBar::RegisterRequestInProgress() const
-+{
-+ return mRegisterRequestCanceller.InProgress();
++ return result;
+}
+
+void
@@ -1778,8 +1859,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+ false);
+
+ // Clear this. Not sure if we really need to though
-+ ContentNode()->SetAttr(kNameSpaceID_None, nsNativeMenuAtoms::openedwithkey,
-+ NS_LITERAL_STRING("false"), true);
++ ContentNode()->AsElement()->SetAttr(kNameSpaceID_None,
++ nsNativeMenuAtoms::openedwithkey,
++ NS_LITERAL_STRING("false"), true);
+
+ DocListener()->Start();
+ Build();
@@ -1795,8 +1877,6 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+
+ mIsActive = false;
+
-+ mRegisterRequestCanceller.Cancel();
-+
+ SetShellShowingMenuBar(false);
+ while (ChildCount() > 0) {
+ RemoveChildAt(0);
@@ -1804,44 +1884,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.cpp
+ DocListener()->Stop();
+ DisconnectDocumentEventListeners();
+}
-+
-+void
-+nsMenuBar::OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute)
-+{
-+
-+}
-+
-+void
-+nsMenuBar::OnContentInserted(nsIContent *aContainer, nsIContent *aChild,
-+ nsIContent *aPrevSibling)
-+{
-+ NS_ASSERTION(aContainer == ContentNode(),
-+ "Received an event that wasn't meant for us");
-+
-+ nsresult rv;
-+ nsMenuObject *child = CreateChild(aChild, &rv);
-+
-+ if (child) {
-+ rv = InsertChildAfter(child, aPrevSibling);
-+ }
-+
-+ NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to insert item in to menubar");
-+}
-+
-+void
-+nsMenuBar::OnContentRemoved(nsIContent *aContainer, nsIContent *aChild)
-+{
-+ NS_ASSERTION(aContainer == ContentNode(),
-+ "Received an event that wasn't meant for us");
-+
-+ DebugOnly<nsresult> rv = RemoveChild(aChild);
-+ NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to remove item from menubar");
-+}
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.h
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.h
-@@ -0,0 +1,112 @@
++++ b/widget/gtk/nsMenuBar.h
+@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -1853,23 +1898,27 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.h
+#define __nsMenuBar_h__
+
+#include "mozilla/Attributes.h"
++#include "mozilla/UniquePtr.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+#include "nsDbusmenu.h"
+#include "nsMenuContainer.h"
+#include "nsMenuObject.h"
-+#include "nsNativeMenuUtils.h"
+
+#include <gtk/gtk.h>
+
-+class nsIAtom;
+class nsIContent;
+class nsIDOMEvent;
-+class nsIDOMKeyEvent;
+class nsIWidget;
+class nsMenuBarDocEventListener;
+
++namespace mozilla {
++namespace dom {
++class KeyboardEvent;
++}
++}
++
+/*
+ * The menubar class. There is one of these per window (and the window
+ * owns its menubar). Each menubar has an object path, and the service is
@@ -1880,29 +1929,20 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.h
+class nsMenuBar final : public nsMenuContainer
+{
+public:
-+ ~nsMenuBar();
++ ~nsMenuBar() override;
+
-+ static nsMenuBar* Create(nsIWidget *aParent,
-+ nsIContent *aMenuBarNode);
++ static mozilla::UniquePtr<nsMenuBar> Create(nsIWidget *aParent,
++ nsIContent *aMenuBarNode);
+
-+ nsMenuObject::EType Type() const;
++ nsMenuObject::EType Type() const override;
+
-+ bool IsBeingDisplayed() const;
++ bool IsBeingDisplayed() const override;
+
+ // Get the native window ID for this menubar
+ uint32_t WindowId() const;
+
+ // Get the object path for this menubar
-+ nsAdoptingCString ObjectPath() const;
-+
-+ // Initializes and returns a cancellable request object, used
-+ // by the menuservice when registering this menubar
-+ nsNativeMenuGIORequest& BeginRegisterRequest();
-+
-+ // Finishes the current request to register the menubar
-+ void EndRegisterRequest();
-+
-+ bool RegisterRequestInProgress() const;
++ nsCString ObjectPath() const;
+
+ // Get the top-level GtkWindow handle
+ GtkWidget* TopLevelWindow() { return mTopLevel; }
@@ -1915,13 +1955,10 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.h
+ // with the desktop shell. Will cause the XUL menubar to be shown again
+ void Deactivate();
+
-+ void OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute);
-+ void OnContentInserted(nsIContent *aContainer, nsIContent *aChild,
-+ nsIContent *aPrevSibling);
-+ void OnContentRemoved(nsIContent *aContainer, nsIContent *aChild);
-+
+private:
-+ friend class nsMenuBarDocEventListener;
++ class DocEventListener;
++ friend class nsMenuBarContentInsertedEvent;
++ friend class nsMenuBarContentRemovedEvent;
+
+ enum ModifierFlags {
+ eModifierShift = (1 << 0),
@@ -1930,23 +1967,30 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.h
+ eModifierMeta = (1 << 3)
+ };
+
-+ nsMenuBar();
-+ nsresult Init(nsIWidget *aParent, nsIContent *aMenuBarNode);
++ nsMenuBar(nsIContent *aMenuBarNode);
++ nsresult Init(nsIWidget *aParent);
+ void Build();
+ void DisconnectDocumentEventListeners();
+ void SetShellShowingMenuBar(bool aShowing);
+ void Focus();
+ void Blur();
-+ ModifierFlags GetModifiersFromEvent(nsIDOMKeyEvent *aEvent);
-+ nsresult Keypress(nsIDOMEvent *aEvent);
-+ nsresult KeyDown(nsIDOMEvent *aEvent);
-+ nsresult KeyUp(nsIDOMEvent *aEvent);
++ ModifierFlags GetModifiersFromEvent(mozilla::dom::KeyboardEvent *aEvent);
++ nsresult Keypress(mozilla::dom::KeyboardEvent *aEvent);
++ nsresult KeyDown(mozilla::dom::KeyboardEvent *aEvent);
++ nsresult KeyUp(mozilla::dom::KeyboardEvent *aEvent);
++
++ void HandleContentInserted(nsIContent *aChild,
++ nsIContent *aPrevSibling);
++ void HandleContentRemoved(nsIContent *aChild);
++
++ void OnContentInserted(nsIContent *aContainer, nsIContent *aChild,
++ nsIContent *aPrevSibling) override;
++ void OnContentRemoved(nsIContent *aContainer, nsIContent *aChild) override;
+
+ GtkWidget *mTopLevel;
+ DbusmenuServer *mServer;
+ nsCOMPtr<nsIDOMEventTarget> mDocument;
-+ nsNativeMenuGIORequest mRegisterRequestCanceller;
-+ RefPtr<nsMenuBarDocEventListener> mEventListener;
++ RefPtr<DocEventListener> mEventListener;
+
+ uint32_t mAccessKey;
+ ModifierFlags mAccessKeyMask;
@@ -1954,11 +1998,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuBar.h
+};
+
+#endif /* __nsMenuBar_h__ */
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuContainer.cpp
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuContainer.cpp
-@@ -0,0 +1,174 @@
++++ b/widget/gtk/nsMenuContainer.cpp
+@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -1966,8 +2008,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuContainer.cpp
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
++#include "mozilla/DebugOnly.h"
++#include "mozilla/Move.h"
+#include "nsGkAtoms.h"
-+#include "nsIAtom.h"
+#include "nsIContent.h"
+
+#include "nsDbusmenu.h"
@@ -1977,19 +2020,29 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuContainer.cpp
+
+#include "nsMenuContainer.h"
+
++using namespace mozilla;
++
+const nsMenuContainer::ChildTArray::index_type nsMenuContainer::NoIndex = nsMenuContainer::ChildTArray::NoIndex;
+
-+typedef nsMenuObject* (*nsMenuObjectConstructor)(nsMenuContainer*,
-+ nsIContent*);
++typedef UniquePtr<nsMenuObject> (*nsMenuObjectConstructor)(nsMenuContainer*,
++ nsIContent*);
++
++template<class T>
++static UniquePtr<nsMenuObject> CreateMenuObject(nsMenuContainer *aContainer,
++ nsIContent *aContent)
++{
++ return UniquePtr<T>(new T(aContainer, aContent));
++}
++
+static nsMenuObjectConstructor
+GetMenuObjectConstructor(nsIContent *aContent)
+{
+ if (aContent->IsXULElement(nsGkAtoms::menuitem)) {
-+ return nsMenuItem::Create;
++ return CreateMenuObject<nsMenuItem>;
+ } else if (aContent->IsXULElement(nsGkAtoms::menu)) {
-+ return nsMenu::Create;
++ return CreateMenuObject<nsMenu>;
+ } else if (aContent->IsXULElement(nsGkAtoms::menuseparator)) {
-+ return nsMenuSeparator::Create;
++ return CreateMenuObject<nsMenuSeparator>;
+ }
+
+ return nullptr;
@@ -2001,31 +2054,30 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuContainer.cpp
+ return GetMenuObjectConstructor(aContent) ? true : false;
+}
+
-+nsMenuObject*
-+nsMenuContainer::CreateChild(nsIContent *aContent, nsresult *aRv)
++nsMenuContainer::nsMenuContainer(nsMenuContainer *aParent,
++ nsIContent *aContent) :
++ nsMenuObject(aParent, aContent)
++{
++}
++
++nsMenuContainer::nsMenuContainer(nsNativeMenuDocListener *aListener,
++ nsIContent *aContent) :
++ nsMenuObject(aListener, aContent)
++{
++}
++
++UniquePtr<nsMenuObject>
++nsMenuContainer::CreateChild(nsIContent *aContent)
+{
+ nsMenuObjectConstructor ctor = GetMenuObjectConstructor(aContent);
+ if (!ctor) {
+ // There are plenty of node types we might stumble across that
-+ // aren't supported. This isn't an error though
-+ if (aRv) {
-+ *aRv = NS_OK;
-+ }
++ // aren't supported
+ return nullptr;
+ }
+
-+ nsMenuObject *res = ctor(this, aContent);
-+ if (!res) {
-+ if (aRv) {
-+ *aRv = NS_ERROR_FAILURE;
-+ }
-+ return nullptr;
-+ }
-+
-+ if (aRv) {
-+ *aRv = NS_OK;
-+ }
-+ return res;
++ UniquePtr<nsMenuObject> res = ctor(this, aContent);
++ return Move(res);
+}
+
+size_t
@@ -2045,77 +2097,64 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuContainer.cpp
+ return NoIndex;
+}
+
-+nsresult
++void
+nsMenuContainer::RemoveChildAt(size_t aIndex, bool aUpdateNative)
+{
-+ if (aIndex >= ChildCount()) {
-+ return NS_ERROR_INVALID_ARG;
-+ }
++ MOZ_ASSERT(aIndex < ChildCount());
+
+ if (aUpdateNative) {
-+ if (!dbusmenu_menuitem_child_delete(GetNativeData(),
-+ ChildAt(aIndex)->GetNativeData())) {
-+ return NS_ERROR_FAILURE;
-+ }
++ MOZ_ALWAYS_TRUE(
++ dbusmenu_menuitem_child_delete(GetNativeData(),
++ ChildAt(aIndex)->GetNativeData()));
+ }
+
+ mChildren.RemoveElementAt(aIndex);
-+
-+ return NS_OK;
+}
+
-+nsresult
++void
+nsMenuContainer::RemoveChild(nsIContent *aChild, bool aUpdateNative)
+{
+ size_t index = IndexOf(aChild);
+ if (index == NoIndex) {
-+ return NS_ERROR_INVALID_ARG;
++ return;
+ }
+
-+ return RemoveChildAt(index, aUpdateNative);
++ RemoveChildAt(index, aUpdateNative);
+}
+
-+nsresult
-+nsMenuContainer::InsertChildAfter(nsMenuObject *aChild,
++void
++nsMenuContainer::InsertChildAfter(UniquePtr<nsMenuObject> aChild,
+ nsIContent *aPrevSibling,
+ bool aUpdateNative)
+{
+ size_t index = IndexOf(aPrevSibling);
-+ if (index == NoIndex && aPrevSibling) {
-+ return NS_ERROR_INVALID_ARG;
-+ }
++ MOZ_ASSERT(!aPrevSibling || index != NoIndex);
+
+ ++index;
+
+ if (aUpdateNative) {
+ aChild->CreateNativeData();
-+ if (!dbusmenu_menuitem_child_add_position(GetNativeData(),
-+ aChild->GetNativeData(),
-+ index)) {
-+ return NS_ERROR_FAILURE;
-+ }
++ MOZ_ALWAYS_TRUE(
++ dbusmenu_menuitem_child_add_position(GetNativeData(),
++ aChild->GetNativeData(),
++ index));
+ }
+
-+ return mChildren.InsertElementAt(index, aChild) ? NS_OK : NS_ERROR_FAILURE;
++ MOZ_ALWAYS_TRUE(mChildren.InsertElementAt(index, Move(aChild)));
+}
+
-+nsresult
-+nsMenuContainer::AppendChild(nsMenuObject *aChild, bool aUpdateNative)
++void
++nsMenuContainer::AppendChild(UniquePtr<nsMenuObject> aChild,
++ bool aUpdateNative)
+{
+ if (aUpdateNative) {
+ aChild->CreateNativeData();
-+ if (!dbusmenu_menuitem_child_append(GetNativeData(),
-+ aChild->GetNativeData())) {
-+ return NS_ERROR_FAILURE;
-+ }
++ MOZ_ALWAYS_TRUE(
++ dbusmenu_menuitem_child_append(GetNativeData(),
++ aChild->GetNativeData()));
+ }
+
-+ return mChildren.AppendElement(aChild) ? NS_OK : NS_ERROR_FAILURE;
-+}
-+
-+nsMenuContainer::nsMenuContainer() :
-+ nsMenuObject()
-+{
++ MOZ_ALWAYS_TRUE(mChildren.AppendElement(Move(aChild)));
+}
+
+bool
@@ -2133,11 +2172,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuContainer.cpp
+
+ return aContent;
+}
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuContainer.h
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuContainer.h
-@@ -0,0 +1,66 @@
++++ b/widget/gtk/nsMenuContainer.h
+@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -2148,18 +2185,19 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuContainer.h
+#ifndef __nsMenuContainer_h__
+#define __nsMenuContainer_h__
+
-+#include "nsAutoPtr.h"
++#include "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+
+#include "nsMenuObject.h"
+
+class nsIContent;
++class nsNativeMenuDocListener;
+
+// Base class for containers (menus and menubars)
+class nsMenuContainer : public nsMenuObject
+{
+public:
-+ typedef nsTArray<nsAutoPtr<nsMenuObject> > ChildTArray;
++ typedef nsTArray<mozilla::UniquePtr<nsMenuObject> > ChildTArray;
+
+ // Determine if this container is being displayed on screen. Must be
+ // implemented by subclasses. Must return true if the container is
@@ -2177,38 +2215,39 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuContainer.h
+ static const ChildTArray::index_type NoIndex;
+
+protected:
-+ nsMenuContainer();
++ nsMenuContainer(nsMenuContainer *aParent, nsIContent *aContent);
++ nsMenuContainer(nsNativeMenuDocListener *aListener, nsIContent *aContent);
+
+ // Create a new child element for the specified content node
-+ nsMenuObject* CreateChild(nsIContent *aContent, nsresult *aRv);
++ mozilla::UniquePtr<nsMenuObject> CreateChild(nsIContent *aContent);
+
+ // Return the index of the child for the specified content node
+ size_t IndexOf(nsIContent *aChild) const;
+
+ size_t ChildCount() const { return mChildren.Length(); }
-+ nsMenuObject* ChildAt(size_t aIndex) const { return mChildren[aIndex]; }
++ nsMenuObject* ChildAt(size_t aIndex) const { return mChildren[aIndex].get(); }
+
-+ nsresult RemoveChildAt(size_t aIndex, bool aUpdateNative = true);
++ void RemoveChildAt(size_t aIndex, bool aUpdateNative = true);
+
+ // Remove the child that owns the specified content node
-+ nsresult RemoveChild(nsIContent *aChild, bool aUpdateNative = true);
++ void RemoveChild(nsIContent *aChild, bool aUpdateNative = true);
+
+ // Insert a new child after the child that owns the specified content node
-+ nsresult InsertChildAfter(nsMenuObject *aChild, nsIContent *aPrevSibling,
-+ bool aUpdateNative = true);
++ void InsertChildAfter(mozilla::UniquePtr<nsMenuObject> aChild,
++ nsIContent *aPrevSibling,
++ bool aUpdateNative = true);
+
-+ nsresult AppendChild(nsMenuObject *aChild, bool aUpdateNative = true);
++ void AppendChild(mozilla::UniquePtr<nsMenuObject> aChild,
++ bool aUpdateNative = true);
+
+private:
+ ChildTArray mChildren;
+};
+
+#endif /* __nsMenuContainer_h__ */
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
-@@ -0,0 +1,743 @@
++++ b/widget/gtk/nsMenuItem.cpp
+@@ -0,0 +1,763 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -2217,7 +2256,10 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
++#include "mozilla/Assertions.h"
+#include "mozilla/dom/Element.h"
++#include "mozilla/dom/KeyboardEventBinding.h"
++#include "mozilla/Move.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/TextEvents.h"
+#include "nsAutoPtr.h"
@@ -2227,12 +2269,11 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+#include "nsGtkUtils.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
-+#include "nsIDOMDocument.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMEventTarget.h"
-+#include "nsIDOMKeyEvent.h"
+#include "nsIDOMXULCommandEvent.h"
+#include "nsIRunnable.h"
++#include "nsQueryObject.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+#include "nsStyleContext.h"
@@ -2254,7 +2295,6 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+#include "nsMenuItem.h"
+
+using namespace mozilla;
-+using namespace mozilla::widget;
+
+struct KeyCodeData {
+ const char* str;
@@ -2511,23 +2551,24 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ NS_IMETHODIMP Run()
+ {
+ if (mMenuItem) {
-+ mMenuItem->UncheckSiblings();
++ static_cast<nsMenuItem *>(mMenuItem.get())->UncheckSiblings();
+ }
+ return NS_OK;
+ }
+
+ nsMenuItemUncheckSiblingsRunnable(nsMenuItem *aMenuItem) :
++ Runnable("nsMenuItemUncheckSiblingsRunnable"),
+ mMenuItem(aMenuItem) { };
+
+private:
-+ nsWeakMenuObject<nsMenuItem> mMenuItem;
++ nsWeakMenuObject mMenuItem;
+};
+
+bool
+nsMenuItem::IsCheckboxOrRadioItem() const
+{
-+ return MenuItemType() == eMenuItemType_Radio ||
-+ MenuItemType() == eMenuItemType_CheckBox;
++ return mType == eMenuItemType_Radio ||
++ mType == eMenuItemType_CheckBox;
+}
+
+/* static */ void
@@ -2548,30 +2589,34 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+
+ // We do this to avoid mutating our view of the menu until
+ // after we have finished
-+ nsNativeMenuAutoUpdateBatch batch;
-+
-+ if (!ContentNode()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocheck,
-+ nsGkAtoms::_false, eCaseMatters) &&
-+ (MenuItemType() == eMenuItemType_CheckBox ||
-+ (MenuItemType() == eMenuItemType_Radio && !IsChecked()))) {
-+ ContentNode()->SetAttr(kNameSpaceID_None, nsGkAtoms::checked,
-+ IsChecked() ?
-+ NS_LITERAL_STRING("false") : NS_LITERAL_STRING("true"),
-+ true);
++ nsNativeMenuDocListener::BlockUpdatesScope updatesBlocker;
++
++ if (!ContentNode()->AsElement()->AttrValueIs(kNameSpaceID_None,
++ nsGkAtoms::autocheck,
++ nsGkAtoms::_false,
++ eCaseMatters) &&
++ (mType == eMenuItemType_CheckBox ||
++ (mType == eMenuItemType_Radio && !mIsChecked))) {
++ ContentNode()->AsElement()->SetAttr(kNameSpaceID_None,
++ nsGkAtoms::checked,
++ mIsChecked ?
++ NS_LITERAL_STRING("false")
++ : NS_LITERAL_STRING("true"),
++ true);
+ }
+
+ nsIDocument *doc = ContentNode()->OwnerDoc();
+ nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(ContentNode());
-+ nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
-+ if (domDoc && target) {
-+ nsCOMPtr<nsIDOMEvent> event;
-+ domDoc->CreateEvent(NS_LITERAL_STRING("xulcommandevent"),
-+ getter_AddRefs(event));
-+ nsCOMPtr<nsIDOMXULCommandEvent> command = do_QueryInterface(event);
++ if (target) {
++ ErrorResult rv;
++ RefPtr<dom::Event> event =
++ doc->CreateEvent(NS_LITERAL_STRING("xulcommandevent"),
++ dom::CallerType::System, rv);
++ nsCOMPtr<nsIDOMXULCommandEvent> command = do_QueryObject(event);
+ if (command) {
+ command->InitCommandEvent(NS_LITERAL_STRING("command"),
+ true, true, doc->GetInnerWindow(), 0,
-+ false, false, false, false, nullptr);
++ false, false, false, false, nullptr, 0);
+
+ event->SetTrusted(true);
+ bool dummy;
@@ -2589,11 +2634,12 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+}
+
+void
-+nsMenuItem::CopyAttrFromNodeIfExists(nsIContent *aContent, nsIAtom *aAttribute)
++nsMenuItem::CopyAttrFromNodeIfExists(nsIContent *aContent, nsAtom *aAttribute)
+{
+ nsAutoString value;
-+ if (aContent->GetAttr(kNameSpaceID_None, aAttribute, value)) {
-+ ContentNode()->SetAttr(kNameSpaceID_None, aAttribute, value, true);
++ if (aContent->AsElement()->GetAttr(kNameSpaceID_None, aAttribute, value)) {
++ ContentNode()->AsElement()->SetAttr(kNameSpaceID_None, aAttribute,
++ value, true);
+ }
+}
+
@@ -2604,13 +2650,13 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ return;
+ }
+
-+ SetCheckState(ContentNode()->AttrValueIs(kNameSpaceID_None,
-+ nsGkAtoms::checked,
-+ nsGkAtoms::_true,
-+ eCaseMatters));
++ mIsChecked = ContentNode()->AsElement()->AttrValueIs(kNameSpaceID_None,
++ nsGkAtoms::checked,
++ nsGkAtoms::_true,
++ eCaseMatters);
+ dbusmenu_menuitem_property_set_int(GetNativeData(),
+ DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
-+ IsChecked() ?
++ mIsChecked ?
+ DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED :
+ DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED);
+}
@@ -2618,23 +2664,23 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+void
+nsMenuItem::UpdateTypeAndState()
+{
-+ static nsIContent::AttrValuesArray attrs[] =
++ static mozilla::dom::Element::AttrValuesArray attrs[] =
+ { &nsGkAtoms::checkbox, &nsGkAtoms::radio, nullptr };
-+ int32_t type = ContentNode()->FindAttrValueIn(kNameSpaceID_None,
-+ nsGkAtoms::type,
-+ attrs, eCaseMatters);
++ int32_t type = ContentNode()->AsElement()->FindAttrValueIn(kNameSpaceID_None,
++ nsGkAtoms::type,
++ attrs, eCaseMatters);
+
+ if (type >= 0 && type < 2) {
+ if (type == 0) {
+ dbusmenu_menuitem_property_set(GetNativeData(),
+ DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE,
+ DBUSMENU_MENUITEM_TOGGLE_CHECK);
-+ SetMenuItemType(eMenuItemType_CheckBox);
++ mType = eMenuItemType_CheckBox;
+ } else if (type == 1) {
+ dbusmenu_menuitem_property_set(GetNativeData(),
+ DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE,
+ DBUSMENU_MENUITEM_TOGGLE_RADIO);
-+ SetMenuItemType(eMenuItemType_Radio);
++ mType = eMenuItemType_Radio;
+ }
+
+ UpdateState();
@@ -2643,7 +2689,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE);
+ dbusmenu_menuitem_property_remove(GetNativeData(),
+ DBUSMENU_MENUITEM_PROP_TOGGLE_STATE);
-+ SetMenuItemType(eMenuItemType_Normal);
++ mType = eMenuItemType_Normal;
+ }
+}
+
@@ -2656,7 +2702,8 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ oldKeyContent.swap(mKeyContent);
+
+ nsAutoString key;
-+ ContentNode()->GetAttr(kNameSpaceID_None, nsGkAtoms::key, key);
++ ContentNode()->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::key,
++ key);
+ if (!key.IsEmpty()) {
+ mKeyContent = doc->GetElementById(key);
+ }
@@ -2678,7 +2725,8 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ }
+
+ nsAutoString modifiers;
-+ mKeyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiers);
++ mKeyContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers,
++ modifiers);
+
+ uint32_t modifier = 0;
+
@@ -2696,9 +2744,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ modifier |= GDK_CONTROL_MASK;
+ } else if (nsCRT::strcmp(token, "accel") == 0) {
+ int32_t accel = Preferences::GetInt("ui.key.accelKey");
-+ if (accel == nsIDOMKeyEvent::DOM_VK_META) {
++ if (accel == dom::KeyboardEventBinding::DOM_VK_META) {
+ modifier |= GDK_META_MASK;
-+ } else if (accel == nsIDOMKeyEvent::DOM_VK_ALT) {
++ } else if (accel == dom::KeyboardEventBinding::DOM_VK_ALT) {
+ modifier |= GDK_MOD1_MASK;
+ } else {
+ modifier |= GDK_CONTROL_MASK;
@@ -2712,7 +2760,8 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ }
+
+ nsAutoString keyStr;
-+ mKeyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyStr);
++ mKeyContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::key,
++ keyStr);
+
+ guint key = 0;
+ if (!keyStr.IsEmpty()) {
@@ -2720,7 +2769,8 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ }
+
+ if (key == 0) {
-+ mKeyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, keyStr);
++ mKeyContent->AsElement()->GetAttr(kNameSpaceID_None,
++ nsGkAtoms::keycode, keyStr);
+ if (!keyStr.IsEmpty()) {
+ key = ConvertGeckoKeyNameToGDKKeyval(keyStr);
+ }
@@ -2739,17 +2789,69 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ }
+}
+
++nsMenuBar*
++nsMenuItem::MenuBar()
++{
++ nsMenuObject *tmp = this;
++ while (tmp->Parent()) {
++ tmp = tmp->Parent();
++ }
++
++ MOZ_ASSERT(tmp->Type() == eType_MenuBar, "The top-level should be a menubar");
++
++ return static_cast<nsMenuBar *>(tmp);
++}
++
++void
++nsMenuItem::UncheckSiblings()
++{
++ if (!ContentNode()->AsElement()->AttrValueIs(kNameSpaceID_None,
++ nsGkAtoms::type,
++ nsGkAtoms::radio,
++ eCaseMatters)) {
++ // If we're not a radio button, we don't care
++ return;
++ }
++
++ nsAutoString name;
++ ContentNode()->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name,
++ name);
++
++ nsIContent *parent = ContentNode()->GetParent();
++ if (!parent) {
++ return;
++ }
++
++ uint32_t count = parent->GetChildCount();
++ for (uint32_t i = 0; i < count; ++i) {
++ nsIContent *sibling = parent->GetChildAt_Deprecated(i);
++
++ if (!sibling->IsElement()) {
++ continue;
++ }
++
++ nsAutoString otherName;
++ sibling->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name,
++ otherName);
++
++ if (sibling != ContentNode() && otherName == name &&
++ sibling->AsElement()->AttrValueIs(kNameSpaceID_None,
++ nsGkAtoms::type,
++ nsGkAtoms::radio,
++ eCaseMatters)) {
++ sibling->AsElement()->UnsetAttr(kNameSpaceID_None,
++ nsGkAtoms::checked, true);
++ }
++ }
++}
++
+void
+nsMenuItem::InitializeNativeData()
+{
+ g_signal_connect(G_OBJECT(GetNativeData()),
+ DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK(item_activated_cb), this);
-+
-+ UpdateTypeAndState();
-+ UpdateAccel();
-+ UpdateLabel();
-+ UpdateSensitivity();
++ mNeedsUpdate = true;
+}
+
+void
@@ -2761,7 +2863,8 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ }
+
+ nsAutoString command;
-+ ContentNode()->GetAttr(kNameSpaceID_None, nsGkAtoms::command, command);
++ ContentNode()->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::command,
++ command);
+ if (command.IsEmpty()) {
+ return;
+ }
@@ -2771,12 +2874,16 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ return;
+ }
+
-+ if (commandContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
-+ nsGkAtoms::_true, eCaseMatters)) {
-+ ContentNode()->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled,
-+ NS_LITERAL_STRING("true"), true);
++ if (commandContent->AsElement()->AttrValueIs(kNameSpaceID_None,
++ nsGkAtoms::disabled,
++ nsGkAtoms::_true,
++ eCaseMatters)) {
++ ContentNode()->AsElement()->SetAttr(kNameSpaceID_None,
++ nsGkAtoms::disabled,
++ NS_LITERAL_STRING("true"), true);
+ } else {
-+ ContentNode()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
++ ContentNode()->AsElement()->UnsetAttr(kNameSpaceID_None,
++ nsGkAtoms::disabled, true);
+ }
+
+ CopyAttrFromNodeIfExists(commandContent, nsGkAtoms::checked);
@@ -2788,40 +2895,17 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+void
+nsMenuItem::Update(nsStyleContext *aStyleContext)
+{
-+ UpdateVisibility(aStyleContext);
-+ UpdateIcon(aStyleContext);
-+}
-+
-+void
-+nsMenuItem::UncheckSiblings()
-+{
-+ if (!ContentNode()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
-+ nsGkAtoms::radio, eCaseMatters)) {
-+ // If we're not a radio button, we don't care
-+ return;
-+ }
-+
-+ nsAutoString name;
-+ ContentNode()->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
++ if (mNeedsUpdate) {
++ mNeedsUpdate = false;
+
-+ nsIContent *parent = ContentNode()->GetParent();
-+ if (!parent) {
-+ return;
++ UpdateTypeAndState();
++ UpdateAccel();
++ UpdateLabel();
++ UpdateSensitivity();
+ }
+
-+ uint32_t count = parent->GetChildCount();
-+ for (uint32_t i = 0; i < count; ++i) {
-+ nsIContent *sibling = parent->GetChildAt(i);
-+
-+ nsAutoString otherName;
-+ sibling->GetAttr(kNameSpaceID_None, nsGkAtoms::name, otherName);
-+
-+ if (sibling != ContentNode() && otherName == name &&
-+ sibling->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
-+ nsGkAtoms::radio, eCaseMatters)) {
-+ sibling->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked, true);
-+ }
-+ }
++ UpdateVisibility(aStyleContext);
++ UpdateIcon(aStyleContext);
+}
+
+bool
@@ -2832,19 +2916,6 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ "separator") != 0;
+}
+
-+nsMenuBar*
-+nsMenuItem::MenuBar()
-+{
-+ nsMenuObject *tmp = this;
-+ while (tmp->Parent()) {
-+ tmp = tmp->Parent();
-+ }
-+
-+ MOZ_ASSERT(tmp->Type() == eType_MenuBar, "The top-level should be a menubar");
-+
-+ return static_cast<nsMenuBar *>(tmp);
-+}
-+
+nsMenuObject::PropertyFlags
+nsMenuItem::SupportedProperties() const
+{
@@ -2859,63 +2930,27 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ );
+}
+
-+nsMenuItem::nsMenuItem() :
-+ nsMenuObject()
-+{
-+ MOZ_COUNT_CTOR(nsMenuItem);
-+}
-+
-+nsMenuItem::~nsMenuItem()
-+{
-+ if (DocListener() && mKeyContent) {
-+ DocListener()->UnregisterForContentChanges(mKeyContent);
-+ }
-+
-+ if (GetNativeData()) {
-+ g_signal_handlers_disconnect_by_func(GetNativeData(),
-+ FuncToGpointer(item_activated_cb),
-+ this);
-+ }
-+
-+ MOZ_COUNT_DTOR(nsMenuItem);
-+}
-+
-+nsMenuObject::EType
-+nsMenuItem::Type() const
++void
++nsMenuItem::OnAttributeChanged(nsIContent *aContent, nsAtom *aAttribute)
+{
-+ return nsMenuObject::eType_MenuItem;
-+}
++ MOZ_ASSERT(aContent == ContentNode() || aContent == mKeyContent,
++ "Received an event that wasn't meant for us!");
+
-+/* static */ nsMenuObject*
-+nsMenuItem::Create(nsMenuContainer *aParent, nsIContent *aContent)
-+{
-+ nsAutoPtr<nsMenuItem> menuitem(new nsMenuItem());
-+ if (NS_FAILED(menuitem->Init(aParent, aContent))) {
-+ return nullptr;
++ if (aContent == ContentNode() && aAttribute == nsGkAtoms::checked &&
++ aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
++ nsGkAtoms::checked,
++ nsGkAtoms::_true, eCaseMatters)) {
++ nsContentUtils::AddScriptRunner(
++ new nsMenuItemUncheckSiblingsRunnable(this));
+ }
+
-+ return menuitem.forget();
-+}
-+
-+void
-+nsMenuItem::OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute)
-+{
-+ NS_ASSERTION(aContent == ContentNode() || aContent == mKeyContent,
-+ "Received an event that wasn't meant for us!");
-+
-+ if (Parent()->NeedsRebuild()) {
++ if (mNeedsUpdate) {
+ return;
+ }
+
-+ if (aContent == ContentNode() && aAttribute == nsGkAtoms::checked &&
-+ aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked,
-+ nsGkAtoms::_true, eCaseMatters)) {
-+ if (nsContentUtils::IsSafeToRunScript()) {
-+ UncheckSiblings();
-+ } else {
-+ nsContentUtils::AddScriptRunner(
-+ new nsMenuItemUncheckSiblingsRunnable(this));
-+ }
++ if (!Parent()->IsBeingDisplayed()) {
++ mNeedsUpdate = true;
++ return;
+ }
+
+ if (aContent == ContentNode()) {
@@ -2931,6 +2966,13 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ UpdateTypeAndState();
+ } else if (aAttribute == nsGkAtoms::checked) {
+ UpdateState();
++ } else if (aAttribute == nsGkAtoms::hidden ||
++ aAttribute == nsGkAtoms::collapsed) {
++ RefPtr<nsStyleContext> sc = GetStyleContext();
++ UpdateVisibility(sc);
++ } else if (aAttribute == nsGkAtoms::image) {
++ RefPtr<nsStyleContext> sc = GetStyleContext();
++ UpdateIcon(sc);
+ }
+ } else if (aContent == mKeyContent &&
+ (aAttribute == nsGkAtoms::key ||
@@ -2938,25 +2980,40 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.cpp
+ aAttribute == nsGkAtoms::modifiers)) {
+ UpdateAccel();
+ }
++}
+
-+ if (!Parent()->IsBeingDisplayed() || aContent != ContentNode()) {
-+ return;
++nsMenuItem::nsMenuItem(nsMenuContainer *aParent, nsIContent *aContent) :
++ nsMenuObject(aParent, aContent),
++ mType(eMenuItemType_Normal),
++ mIsChecked(false),
++ mNeedsUpdate(false)
++{
++ MOZ_COUNT_CTOR(nsMenuItem);
++}
++
++nsMenuItem::~nsMenuItem()
++{
++ if (DocListener() && mKeyContent) {
++ DocListener()->UnregisterForContentChanges(mKeyContent);
+ }
+
-+ if (aAttribute == nsGkAtoms::hidden ||
-+ aAttribute == nsGkAtoms::collapsed) {
-+ RefPtr<nsStyleContext> sc = GetStyleContext();
-+ UpdateVisibility(sc);
-+ } else if (aAttribute == nsGkAtoms::image) {
-+ RefPtr<nsStyleContext> sc = GetStyleContext();
-+ UpdateIcon(sc);
++ if (GetNativeData()) {
++ g_signal_handlers_disconnect_by_func(GetNativeData(),
++ FuncToGpointer(item_activated_cb),
++ this);
+ }
++
++ MOZ_COUNT_DTOR(nsMenuItem);
++}
++
++nsMenuObject::EType
++nsMenuItem::Type() const
++{
++ return eType_MenuItem;
+}
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.h
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.h
-@@ -0,0 +1,107 @@
++++ b/widget/gtk/nsMenuItem.h
+@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -2975,10 +3032,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.h
+
+#include <glib.h>
+
-+#define NSMENUITEM_NUMBER_OF_TYPE_BITS 2U
-+#define NSMENUITEM_NUMBER_OF_FLAGS 1U
-+
-+class nsIAtom;
++class nsAtom;
+class nsIContent;
+class nsStyleContext;
+class nsMenuBar;
@@ -2991,14 +3045,10 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.h
+class nsMenuItem final : public nsMenuObject
+{
+public:
-+ ~nsMenuItem();
++ nsMenuItem(nsMenuContainer *aParent, nsIContent *aContent);
++ ~nsMenuItem() override;
+
-+ nsMenuObject::EType Type() const;
-+
-+ static nsMenuObject* Create(nsMenuContainer *aParent,
-+ nsIContent *aContent);
-+
-+ void OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute);
++ nsMenuObject::EType Type() const override;
+
+private:
+ friend class nsMenuItemUncheckSiblingsRunnable;
@@ -3013,62 +3063,41 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuItem.h
+ eMenuItemType_CheckBox
+ };
+
-+ nsMenuItem();
-+
-+ EMenuItemType MenuItemType() const
-+ {
-+ return static_cast<EMenuItemType>(
-+ (GetFlags() &
-+ (((1U << NSMENUITEM_NUMBER_OF_TYPE_BITS) - 1U)
-+ << NSMENUITEM_NUMBER_OF_FLAGS)) >> NSMENUITEM_NUMBER_OF_FLAGS);
-+ }
-+ void SetMenuItemType(EMenuItemType aType)
-+ {
-+ ClearFlags(((1U << NSMENUITEM_NUMBER_OF_TYPE_BITS) - 1U) << NSMENUITEM_NUMBER_OF_FLAGS);
-+ SetFlags(aType << NSMENUITEM_NUMBER_OF_FLAGS);
-+ }
+ bool IsCheckboxOrRadioItem() const;
+
-+ bool IsChecked() const
-+ {
-+ return HasFlags(eMenuItemFlag_ToggleState);
-+ }
-+ void SetCheckState(bool aState)
-+ {
-+ if (aState) {
-+ SetFlags(eMenuItemFlag_ToggleState);
-+ } else {
-+ ClearFlags(eMenuItemFlag_ToggleState);
-+ }
-+ }
-+
+ static void item_activated_cb(DbusmenuMenuitem *menuitem,
+ guint timestamp,
+ gpointer user_data);
+ void Activate(uint32_t aTimestamp);
+
-+ void CopyAttrFromNodeIfExists(nsIContent *aContent, nsIAtom *aAtom);
++ void CopyAttrFromNodeIfExists(nsIContent *aContent, nsAtom *aAtom);
+ void UpdateState();
+ void UpdateTypeAndState();
+ void UpdateAccel();
-+
-+ void InitializeNativeData();
-+ void UpdateContentAttributes();
-+ void Update(nsStyleContext *aStyleContext);
-+ void UncheckSiblings();
-+ bool IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const;
+ nsMenuBar* MenuBar();
-+ nsMenuObject::PropertyFlags SupportedProperties() const;
++ void UncheckSiblings();
++
++ void InitializeNativeData() override;
++ void UpdateContentAttributes() override;
++ void Update(nsStyleContext *aStyleContext) override;
++ bool IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const override;
++ nsMenuObject::PropertyFlags SupportedProperties() const override;
++
++ void OnAttributeChanged(nsIContent *aContent, nsAtom *aAttribute) override;
++
++ EMenuItemType mType;
++
++ bool mIsChecked;
++
++ bool mNeedsUpdate;
+
+ nsCOMPtr<nsIContent> mKeyContent;
+};
+
+#endif /* __nsMenuItem_h__ */
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
-@@ -0,0 +1,694 @@
++++ b/widget/gtk/nsMenuObject.cpp
+@@ -0,0 +1,664 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -3082,6 +3111,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+#include "imgLoader.h"
+#include "imgRequestProxy.h"
+#include "mozilla/ArrayUtils.h"
++#include "mozilla/Assertions.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/Preferences.h"
+#include "nsAttrValue.h"
@@ -3103,13 +3133,11 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+#include "nsStyleConsts.h"
+#include "nsStyleContext.h"
+#include "nsStyleStruct.h"
-+#include "nsThreadUtils.h"
+#include "nsUnicharUtils.h"
+
+#include "nsMenuContainer.h"
+#include "nsNativeMenuAtoms.h"
+#include "nsNativeMenuDocListener.h"
-+#include "nsNativeMenuUtils.h"
+
+#include <gdk/gdk.h>
+#include <glib-object.h>
@@ -3117,6 +3145,11 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+
+#include "nsMenuObject.h"
+
++// X11's None clashes with StyleDisplay::None
++#include "X11UndefineNone.h"
++
++#undef None
++
+using namespace mozilla;
+using mozilla::image::ImageOps;
+
@@ -3129,27 +3162,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+ nullptr
+};
+
-+nsWeakMenuObjectBase* nsWeakMenuObjectBase::sHead;
++nsWeakMenuObject* nsWeakMenuObject::sHead;
+PangoLayout* gPangoLayout = nullptr;
+
-+class nsMenuObjectContainerOpeningRunnable : public Runnable
-+{
-+public:
-+ NS_IMETHODIMP Run()
-+ {
-+ if (mMenuObject) {
-+ mMenuObject->ContainerIsOpening();
-+ }
-+ return NS_OK;
-+ }
-+
-+ nsMenuObjectContainerOpeningRunnable(nsMenuObject *aMenuObject) :
-+ mMenuObject(aMenuObject) { };
-+
-+private:
-+ nsWeakMenuObject<nsMenuObject> mMenuObject;
-+};
-+
+class nsMenuObjectIconLoader final : public imgINotificationObserver
+{
+public:
@@ -3264,8 +3279,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+ imgRequestProxy *imageRequest = nullptr;
+
+ nsAutoString uriString;
-+ if (mOwner->ContentNode()->GetAttr(kNameSpaceID_None, nsGkAtoms::image,
-+ uriString)) {
++ if (mOwner->ContentNode()->AsElement()->GetAttr(kNameSpaceID_None,
++ nsGkAtoms::image,
++ uriString)) {
+ NS_NewURI(getter_AddRefs(uri), uriString);
+ } else {
+ nsIPresShell *shell = doc->GetShell();
@@ -3314,7 +3330,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+
+ if (imageRequest) {
+ mImageRect = imageRect;
-+ imageRequest->Clone(this, getter_AddRefs(mImageRequest));
++ imageRequest->Clone(this, nullptr, getter_AddRefs(mImageRequest));
+ } else {
+ mImageRect.SetEmpty();
+ nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
@@ -3325,11 +3341,11 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+ return;
+ }
+
-+ loader->LoadImage(uri, nullptr, nullptr, mozilla::net::RP_Default,
-+ nullptr, loadGroup, this, nullptr, nullptr,
++ loader->LoadImage(uri, nullptr, nullptr, mozilla::net::RP_Unset,
++ nullptr, 0, loadGroup, this, nullptr, nullptr,
+ nsIRequest::LOAD_NORMAL, nullptr,
+ nsIContentPolicy::TYPE_IMAGE, EmptyString(),
-+ getter_AddRefs(mImageRequest));
++ false, getter_AddRefs(mImageRequest));
+ }
+}
+
@@ -3367,10 +3383,11 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+{
+ static char16_t sBuf[4] = { 0, 0, 0, 0 };
+ if (!sBuf[0]) {
-+ nsAdoptingString ellipsis = Preferences::GetLocalizedString("intl.ellipsis");
++ nsString ellipsis;
++ Preferences::GetLocalizedString("intl.ellipsis", ellipsis);
+ if (!ellipsis.IsEmpty()) {
+ uint32_t l = ellipsis.Length();
-+ const nsAdoptingString::char_type *c = ellipsis.BeginReading();
++ const nsString::char_type *c = ellipsis.BeginReading();
+ uint32_t i = 0;
+ while (i < 3 && i < l) {
+ sBuf[i++] = *(c++);
@@ -3397,60 +3414,26 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+ return sEllipsisWidth;
+}
+
-+void
-+nsMenuObject::InitializeNativeData()
-+{
-+}
-+
-+nsMenuObject::PropertyFlags
-+nsMenuObject::SupportedProperties() const
-+{
-+ return static_cast<nsMenuObject::PropertyFlags>(0);
-+}
-+
-+bool
-+nsMenuObject::IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const
-+{
-+ return true;
-+}
-+
-+void
-+nsMenuObject::UpdateContentAttributes()
-+{
-+}
-+
-+void
-+nsMenuObject::Update(nsStyleContext *aStyleContext)
-+{
-+}
-+
-+bool
-+nsMenuObject::ShouldShowIcon() const
++nsMenuObject::nsMenuObject(nsMenuContainer *aParent, nsIContent *aContent) :
++ mContent(aContent),
++ mListener(aParent->DocListener()),
++ mParent(aParent),
++ mNativeData(nullptr)
+{
-+ // Ideally we want to know the visibility of the anonymous XUL image in
-+ // our menuitem, but this isn't created because we don't have a frame.
-+ // The following works by default (because xul.css hides images in menuitems
-+ // that don't have the "menuitem-with-favicon" class). It's possible a third
-+ // party theme could override this, but, oh well...
-+ const nsAttrValue *classes = mContent->GetClasses();
-+ if (!classes) {
-+ return false;
-+ }
-+
-+ for (uint32_t i = 0; i < classes->GetAtomCount(); ++i) {
-+ if (classes->AtomAt(i) == nsNativeMenuAtoms::menuitem_with_favicon) {
-+ return true;
-+ }
-+ }
-+
-+ return false;
++ MOZ_ASSERT(mContent);
++ MOZ_ASSERT(mListener);
++ MOZ_ASSERT(mParent);
+}
+
-+void
-+nsMenuObject::ClearIcon()
++nsMenuObject::nsMenuObject(nsNativeMenuDocListener *aListener,
++ nsIContent *aContent) :
++ mContent(aContent),
++ mListener(aListener),
++ mParent(nullptr),
++ mNativeData(nullptr)
+{
-+ dbusmenu_menuitem_property_remove(mNativeData,
-+ DBUSMENU_MENUITEM_PROP_ICON_DATA);
++ MOZ_ASSERT(mContent);
++ MOZ_ASSERT(mListener);
+}
+
+void
@@ -3461,10 +3444,11 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+ // label="_Foo__Bar" for dbusmenu
+
+ nsAutoString label;
-+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, label);
++ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::label, label);
+
+ nsAutoString accesskey;
-+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accesskey);
++ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey,
++ accesskey);
+
+ const nsAutoString::char_type *akey = accesskey.BeginReading();
+ char16_t keyLower = ToLowerCase(*akey);
@@ -3506,33 +3490,32 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+ return;
+ }
+
-+ // This *COMPLETELY SUCKS*
++ // This sucks.
+ // This should be done at the point where the menu is drawn (hello Unity),
+ // but unfortunately it doesn't do that and will happily fill your entire
+ // screen width with a menu if you have a bookmark with a really long title.
+ // This leaves us with no other option but to ellipsize here, with no proper
+ // knowledge of Unity's render path, font size etc. This is better than nothing
-+ // BAH! @*&!$
+ nsAutoString truncated;
+ int target = MAX_WIDTH - GetEllipsisWidth();
+ length = label.Length();
+
-+ static nsIContent::AttrValuesArray strings[] = {
++ static mozilla::dom::Element::AttrValuesArray strings[] = {
+ &nsGkAtoms::left, &nsGkAtoms::start,
+ &nsGkAtoms::center, &nsGkAtoms::right,
+ &nsGkAtoms::end, nullptr
+ };
+
-+ int32_t type = mContent->FindAttrValueIn(kNameSpaceID_None,
-+ nsGkAtoms::crop,
-+ strings, eCaseMatters);
++ int32_t type = mContent->AsElement()->FindAttrValueIn(kNameSpaceID_None,
++ nsGkAtoms::crop,
++ strings, eCaseMatters);
+
+ switch (type) {
+ case 0:
+ case 1:
-+ // FIXME: Implement left cropping (do we really care?)
++ // FIXME: Implement left cropping
+ case 2:
-+ // FIXME: Implement center cropping (do we really care?)
++ // FIXME: Implement center cropping
+ case 3:
+ case 4:
+ default:
@@ -3571,9 +3554,10 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+void
+nsMenuObject::UpdateSensitivity()
+{
-+ bool disabled = mContent->AttrValueIs(kNameSpaceID_None,
-+ nsGkAtoms::disabled,
-+ nsGkAtoms::_true, eCaseMatters);
++ bool disabled = mContent->AsElement()->AttrValueIs(kNameSpaceID_None,
++ nsGkAtoms::disabled,
++ nsGkAtoms::_true,
++ eCaseMatters);
+
+ dbusmenu_menuitem_property_set_bool(mNativeData,
+ DBUSMENU_MENUITEM_PROP_ENABLED,
@@ -3603,53 +3587,72 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+already_AddRefed<nsStyleContext>
+nsMenuObject::GetStyleContext()
+{
-+ nsIPresShell *shell = mContent->OwnerDoc()->GetShell();
-+ if (!shell) {
-+ return nullptr;
-+ }
-+
+ RefPtr<nsStyleContext> sc =
-+ nsComputedDOMStyle::GetStyleContextForElementNoFlush(
-+ mContent->AsElement(), nullptr, shell);
++ nsComputedDOMStyle::GetStyleContextNoFlush(
++ mContent->AsElement(), nullptr);
+
+ return sc.forget();
+}
+
-+nsresult
-+nsMenuObject::Init(nsMenuContainer *aParent, nsIContent *aContent)
++void
++nsMenuObject::InitializeNativeData()
+{
-+ NS_ENSURE_ARG(aParent);
-+ NS_ENSURE_ARG(aContent);
++}
+
-+ mParent = aParent;
-+ mContent = aContent;
-+ mListener = aParent->DocListener();
-+ NS_ENSURE_ARG(mListener);
++nsMenuObject::PropertyFlags
++nsMenuObject::SupportedProperties() const
++{
++ return static_cast<nsMenuObject::PropertyFlags>(0);
++}
+
-+ return NS_OK;
++bool
++nsMenuObject::IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const
++{
++ return true;
+}
+
-+nsresult
-+nsMenuObject::Init(nsNativeMenuDocListener *aListener, nsIContent *aContent)
++void
++nsMenuObject::UpdateContentAttributes()
+{
-+ NS_ENSURE_ARG(aListener);
-+ NS_ENSURE_ARG(aContent);
++}
+
-+ mParent = nullptr;
-+ mContent = aContent;
-+ mListener = aListener;
++void
++nsMenuObject::Update(nsStyleContext *aStyleContext)
++{
++}
+
-+ return NS_OK;
++bool
++nsMenuObject::ShouldShowIcon() const
++{
++ // Ideally we want to know the visibility of the anonymous XUL image in
++ // our menuitem, but this isn't created because we don't have a frame.
++ // The following works by default (because xul.css hides images in menuitems
++ // that don't have the "menuitem-with-favicon" class). It's possible a third
++ // party theme could override this, but, oh well...
++ const nsAttrValue *classes = mContent->AsElement()->GetClasses();
++ if (!classes) {
++ return false;
++ }
++
++ for (uint32_t i = 0; i < classes->GetAtomCount(); ++i) {
++ if (classes->AtomAt(i) == nsNativeMenuAtoms::menuitem_with_favicon) {
++ return true;
++ }
++ }
++
++ return false;
+}
+
-+nsMenuObject::nsMenuObject() :
-+ mParent(nullptr), mNativeData(nullptr), mFlags(0)
++void
++nsMenuObject::ClearIcon()
+{
++ dbusmenu_menuitem_property_remove(mNativeData,
++ DBUSMENU_MENUITEM_PROP_ICON_DATA);
+}
+
+nsMenuObject::~nsMenuObject()
+{
-+ nsWeakMenuObjectBase::NotifyDestroyed(this);
++ nsWeakMenuObject::NotifyDestroyed(this);
+
+ if (mIconLoader) {
+ mIconLoader->Destroy();
@@ -3668,7 +3671,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+void
+nsMenuObject::CreateNativeData()
+{
-+ NS_ASSERTION(mNativeData == nullptr, "This node already has a DbusmenuMenuitem. The old one will be leaked");
++ MOZ_ASSERT(mNativeData == nullptr, "This node already has a DbusmenuMenuitem. The old one will be leaked");
+
+ mNativeData = dbusmenu_menuitem_new();
+ InitializeNativeData();
@@ -3682,7 +3685,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+nsresult
+nsMenuObject::AdoptNativeData(DbusmenuMenuitem *aNativeData)
+{
-+ NS_ASSERTION(mNativeData == nullptr, "This node already has a DbusmenuMenuitem. The old one will be leaked");
++ MOZ_ASSERT(mNativeData == nullptr, "This node already has a DbusmenuMenuitem. The old one will be leaked");
+
+ if (!IsCompatibleWithNativeData(aNativeData)) {
+ return NS_ERROR_FAILURE;
@@ -3714,11 +3717,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+void
+nsMenuObject::ContainerIsOpening()
+{
-+ if (!nsContentUtils::IsSafeToRunScript()) {
-+ nsContentUtils::AddScriptRunner(
-+ new nsMenuObjectContainerOpeningRunnable(this));
-+ return;
-+ }
++ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+ UpdateContentAttributes();
+
@@ -3727,47 +3726,45 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.cpp
+}
+
+/* static */ void
-+nsWeakMenuObjectBase::AddWeakReference(nsWeakMenuObjectBase *aWeak)
++nsWeakMenuObject::AddWeakReference(nsWeakMenuObject *aWeak)
+{
-+ aWeak->SetPrevious(sHead);
++ aWeak->mPrev = sHead;
+ sHead = aWeak;
+}
+
+/* static */ void
-+nsWeakMenuObjectBase::RemoveWeakReference(nsWeakMenuObjectBase *aWeak)
++nsWeakMenuObject::RemoveWeakReference(nsWeakMenuObject *aWeak)
+{
+ if (aWeak == sHead) {
-+ sHead = aWeak->GetPrevious();
++ sHead = aWeak->mPrev;
+ return;
+ }
+
-+ nsWeakMenuObjectBase *weak = sHead;
-+ while (weak && weak->GetPrevious() != aWeak) {
-+ weak = weak->GetPrevious();
++ nsWeakMenuObject *weak = sHead;
++ while (weak && weak->mPrev != aWeak) {
++ weak = weak->mPrev;
+ }
+
+ if (weak) {
-+ weak->SetPrevious(aWeak->GetPrevious());
++ weak->mPrev = aWeak->mPrev;
+ }
+}
+
+/* static */ void
-+nsWeakMenuObjectBase::NotifyDestroyed(nsMenuObject *aMenuObject)
++nsWeakMenuObject::NotifyDestroyed(nsMenuObject *aMenuObject)
+{
-+ nsWeakMenuObjectBase *weak = sHead;
++ nsWeakMenuObject *weak = sHead;
+ while (weak) {
-+ if (weak->getBase() == aMenuObject) {
-+ weak->Clear();
++ if (weak->mMenuObject == aMenuObject) {
++ weak->mMenuObject = nullptr;
+ }
+
-+ weak = weak->GetPrevious();
++ weak = weak->mPrev;
+ }
+}
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.h
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.h
-@@ -0,0 +1,242 @@
++++ b/widget/gtk/nsMenuObject.h
+@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -3784,7 +3781,6 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.h
+#include "nsDbusmenu.h"
+#include "nsNativeMenuDocListener.h"
+
-+class nsIAtom;
+class nsIContent;
+class nsStyleContext;
+class nsMenuContainer;
@@ -3817,14 +3813,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.h
+ enum EType {
+ eType_MenuBar,
+ eType_Menu,
-+ eType_MenuItem,
-+ eType_MenuSeparator
-+ };
-+
-+ enum PropertyFlags {
-+#define DBUSMENU_PROPERTY(e, s, b) eProp##e = (1 << b),
-+ DBUSMENU_PROPERTIES
-+#undef DBUSMENU_PROPERTY
++ eType_MenuItem
+ };
+
+ virtual ~nsMenuObject();
@@ -3854,9 +3843,14 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.h
+ void ContainerIsOpening();
+
+protected:
-+ nsMenuObject();
-+ nsresult Init(nsMenuContainer *aParent, nsIContent *aContent);
-+ nsresult Init(nsNativeMenuDocListener *aListener, nsIContent *aContent);
++ nsMenuObject(nsMenuContainer *aParent, nsIContent *aContent);
++ nsMenuObject(nsNativeMenuDocListener *aListener, nsIContent *aContent);
++
++ enum PropertyFlags {
++#define DBUSMENU_PROPERTY(e, s, b) eProp##e = (1 << b),
++ DBUSMENU_PROPERTIES
++#undef DBUSMENU_PROPERTY
++ };
+
+ void UpdateLabel();
+ void UpdateVisibility(nsStyleContext *aStyleContext);
@@ -3865,20 +3859,6 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.h
+
+ already_AddRefed<nsStyleContext> GetStyleContext();
+
-+ uint8_t GetFlags() const { return mFlags; }
-+ bool HasFlags(uint8_t aFlags) const
-+ {
-+ return (mFlags & aFlags) == aFlags;
-+ }
-+ void SetFlags(uint8_t aFlags)
-+ {
-+ mFlags |= aFlags;
-+ }
-+ void ClearFlags(uint8_t aFlags)
-+ {
-+ mFlags &= ~aFlags;
-+ }
-+
+private:
+ friend class nsMenuObjectIconLoader;
+
@@ -3919,102 +3899,44 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuObject.h
+ nsMenuContainer *mParent; // [weak]
+ DbusmenuMenuitem *mNativeData; // [strong]
+ RefPtr<nsMenuObjectIconLoader> mIconLoader;
-+ uint8_t mFlags;
+};
+
-+class nsWeakMenuObjectBase
++// Keep a weak pointer to a menu object
++class nsWeakMenuObject
+{
+public:
-+ ~nsWeakMenuObjectBase()
++ nsWeakMenuObject() : mPrev(nullptr), mMenuObject(nullptr) {}
++
++ nsWeakMenuObject(nsMenuObject *aMenuObject) :
++ mPrev(nullptr), mMenuObject(aMenuObject)
+ {
-+ RemoveWeakReference(this);
++ AddWeakReference(this);
+ }
+
-+ nsMenuObject* getBase() const { return mMenuObject; }
++ ~nsWeakMenuObject() { RemoveWeakReference(this); }
+
-+ static void NotifyDestroyed(nsMenuObject *aMenuObject);
++ nsMenuObject* get() const { return mMenuObject; }
+
-+protected:
-+ nsWeakMenuObjectBase() : mMenuObject(nullptr) { };
++ nsMenuObject* operator->() const { return mMenuObject; }
+
-+ void SetMenuObject(nsMenuObject *aMenuObject)
-+ {
-+ mMenuObject = aMenuObject;
++ explicit operator bool() const { return !!mMenuObject; }
+
-+ mMenuObject ? AddWeakReference(this) : RemoveWeakReference(this);
-+ }
++ static void NotifyDestroyed(nsMenuObject *aMenuObject);
+
+private:
-+ nsWeakMenuObjectBase* GetPrevious() const { return mPrev; }
-+ void SetPrevious(nsWeakMenuObjectBase *aPrev)
-+ {
-+ mPrev = aPrev;
-+ }
-+ void Clear() { mMenuObject = nullptr; }
-+
-+ static void AddWeakReference(nsWeakMenuObjectBase *aWeak);
-+ static void RemoveWeakReference(nsWeakMenuObjectBase *aWeak);
++ static void AddWeakReference(nsWeakMenuObject *aWeak);
++ static void RemoveWeakReference(nsWeakMenuObject *aWeak);
+
-+ nsWeakMenuObjectBase *mPrev;
-+ static nsWeakMenuObjectBase *sHead;
++ nsWeakMenuObject *mPrev;
++ static nsWeakMenuObject *sHead;
+
+ nsMenuObject *mMenuObject;
+};
+
-+// Keep a weak pointer to a menu object. Note, if you need to work
-+// with a pointer to this class, use nsAutoWeakMenuObject instead
-+template<class T>
-+class nsWeakMenuObject : public nsWeakMenuObjectBase
-+{
-+public:
-+ nsWeakMenuObject() :
-+ nsWeakMenuObjectBase() { };
-+
-+ nsWeakMenuObject(T *aMenuObject) :
-+ nsWeakMenuObjectBase()
-+ {
-+ SetMenuObject(aMenuObject);
-+ }
-+
-+ T* get() const { return static_cast<T *>(getBase()); }
-+
-+ T* operator->() const { return get(); }
-+
-+ operator T*() const { return get(); }
-+};
-+
-+template<class T>
-+class nsAutoWeakMenuObject
-+{
-+public:
-+ nsAutoWeakMenuObject() { };
-+
-+ nsAutoWeakMenuObject(T *aMenuObject) :
-+ mPtr(new nsWeakMenuObject<T>(aMenuObject)) { };
-+
-+ nsAutoWeakMenuObject(nsWeakMenuObject<T> *aWeak) :
-+ mPtr(aWeak) { };
-+
-+ T* get() const { return static_cast<T *>(*mPtr); }
-+
-+ T* operator->() const { return get(); }
-+
-+ operator T*() const { return get(); }
-+
-+ nsWeakMenuObject<T>* getWeakPtr() const { return mPtr; }
-+
-+ nsWeakMenuObject<T>* forget() { return mPtr.forget(); }
-+
-+private:
-+ nsAutoPtr<nsWeakMenuObject<T> > mPtr;
-+};
-+
+#endif /* __nsMenuObject_h__ */
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuSeparator.cpp
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuSeparator.cpp
-@@ -0,0 +1,90 @@
++++ b/widget/gtk/nsMenuSeparator.cpp
+@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -4022,6 +3944,8 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuSeparator.cpp
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
++#include "mozilla/Assertions.h"
++#include "mozilla/Move.h"
+#include "nsAutoPtr.h"
+#include "nsCRT.h"
+#include "nsGkAtoms.h"
@@ -4032,6 +3956,8 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuSeparator.cpp
+#include "nsMenuContainer.h"
+#include "nsMenuSeparator.h"
+
++using namespace mozilla;
++
+void
+nsMenuSeparator::InitializeNativeData()
+{
@@ -4063,37 +3989,10 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuSeparator.cpp
+ );
+}
+
-+nsMenuSeparator::nsMenuSeparator()
-+{
-+ MOZ_COUNT_CTOR(nsMenuSeparator);
-+}
-+
-+nsMenuSeparator::~nsMenuSeparator()
-+{
-+ MOZ_COUNT_DTOR(nsMenuSeparator);
-+}
-+
-+nsMenuObject::EType
-+nsMenuSeparator::Type() const
-+{
-+ return nsMenuObject::eType_MenuSeparator;
-+}
-+
-+/* static */ nsMenuObject*
-+nsMenuSeparator::Create(nsMenuContainer *aParent, nsIContent *aContent)
-+{
-+ nsAutoPtr<nsMenuSeparator> sep(new nsMenuSeparator());
-+ if (NS_FAILED(sep->Init(aParent, aContent))) {
-+ return nullptr;
-+ }
-+
-+ return sep.forget();
-+}
-+
+void
-+nsMenuSeparator::OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute)
++nsMenuSeparator::OnAttributeChanged(nsIContent *aContent, nsAtom *aAttribute)
+{
-+ NS_ASSERTION(aContent == ContentNode(), "Received an event that wasn't meant for us!");
++ MOZ_ASSERT(aContent == ContentNode(), "Received an event that wasn't meant for us!");
+
+ if (!Parent()->IsBeingDisplayed()) {
+ return;
@@ -4105,11 +4004,27 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuSeparator.cpp
+ UpdateVisibility(sc);
+ }
+}
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuSeparator.h
-===================================================================
++
++nsMenuSeparator::nsMenuSeparator(nsMenuContainer *aParent,
++ nsIContent *aContent) :
++ nsMenuObject(aParent, aContent)
++{
++ MOZ_COUNT_CTOR(nsMenuSeparator);
++}
++
++nsMenuSeparator::~nsMenuSeparator()
++{
++ MOZ_COUNT_DTOR(nsMenuSeparator);
++}
++
++nsMenuObject::EType
++nsMenuSeparator::Type() const
++{
++ return eType_MenuItem;
++}
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuSeparator.h
-@@ -0,0 +1,41 @@
++++ b/widget/gtk/nsMenuSeparator.h
+@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -4125,37 +4040,31 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsMenuSeparator.h
+#include "nsMenuObject.h"
+
+class nsIContent;
-+class nsIAtom;
++class nsAtom;
+class nsMenuContainer;
+
+// Menu separator class
+class nsMenuSeparator final : public nsMenuObject
+{
+public:
++ nsMenuSeparator(nsMenuContainer *aParent, nsIContent *aContent);
+ ~nsMenuSeparator();
+
-+ nsMenuObject::EType Type() const;
-+
-+ static nsMenuObject* Create(nsMenuContainer *aParent,
-+ nsIContent *aContent);
-+
-+ void OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute);
++ nsMenuObject::EType Type() const override;
+
+private:
-+ nsMenuSeparator();
++ void InitializeNativeData() override;
++ void Update(nsStyleContext *aStyleContext) override;
++ bool IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const override;
++ nsMenuObject::PropertyFlags SupportedProperties() const override;
+
-+ void InitializeNativeData();
-+ void Update(nsStyleContext *aStyleContext);
-+ bool IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const;
-+ nsMenuObject::PropertyFlags SupportedProperties() const;
++ void OnAttributeChanged(nsIContent *aContent, nsAtom *aAttribute) override;
+};
+
+#endif /* __nsMenuSeparator_h__ */
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuAtomList.h
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuAtomList.h
-@@ -0,0 +1,11 @@
++++ b/widget/gtk/nsNativeMenuAtomList.h
+@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -4164,13 +4073,12 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuAtomList.h
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+WIDGET_ATOM2(menuitem_with_favicon, "menuitem-with-favicon")
-+WIDGET_ATOM2(_moz_menupopupstate, "_moz-menupopupstate")
++WIDGET_ATOM2(_moz_menubarkeeplocal, "_moz-menubarkeeplocal")
++WIDGET_ATOM2(_moz_nativemenupopupstate, "_moz-nativemenupopupstate")
+WIDGET_ATOM(openedwithkey)
+WIDGET_ATOM(shellshowingmenubar)
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuAtoms.cpp
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuAtoms.cpp
++++ b/widget/gtk/nsNativeMenuAtoms.cpp
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
@@ -4179,43 +4087,41 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuAtoms.cpp
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
-+#include "nsIAtom.h"
++#include "nsAtom.h"
+#include "nsStaticAtom.h"
+
+#include "nsNativeMenuAtoms.h"
+
+using namespace mozilla;
+
-+#define WIDGET_ATOM(_name) nsIAtom* nsNativeMenuAtoms::_name;
-+#define WIDGET_ATOM2(_name, _value) nsIAtom* nsNativeMenuAtoms::_name;
++#define WIDGET_ATOM(name_) NS_STATIC_ATOM_DEFN(nsNativeMenuAtoms, name_)
++#define WIDGET_ATOM2(name_, value_) NS_STATIC_ATOM_DEFN(nsNativeMenuAtoms, name_)
+#include "nsNativeMenuAtomList.h"
+#undef WIDGET_ATOM
+#undef WIDGET_ATOM2
+
-+#define WIDGET_ATOM(name_) NS_STATIC_ATOM_BUFFER(name_##_buffer, #name_)
-+#define WIDGET_ATOM2(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_)
++#define WIDGET_ATOM(name_) NS_STATIC_ATOM_BUFFER(name_, #name_)
++#define WIDGET_ATOM2(name_, value_) NS_STATIC_ATOM_BUFFER(name_, value_)
+#include "nsNativeMenuAtomList.h"
+#undef WIDGET_ATOM
+#undef WIDGET_ATOM2
+
-+static const nsStaticAtom gAtoms[] = {
-+#define WIDGET_ATOM(name_) NS_STATIC_ATOM(name_##_buffer, &nsNativeMenuAtoms::name_),
-+#define WIDGET_ATOM2(name_, value_) NS_STATIC_ATOM(name_##_buffer, &nsNativeMenuAtoms::name_),
-+#include "nsNativeMenuAtomList.h"
-+#undef WIDGET_ATOM
-+#undef WIDGET_ATOM2
++static const nsStaticAtomSetup sAtomSetup[] = {
++ #define WIDGET_ATOM(name_) NS_STATIC_ATOM_SETUP(nsNativeMenuAtoms, name_)
++ #define WIDGET_ATOM2(name_, value_) NS_STATIC_ATOM_SETUP(nsNativeMenuAtoms, name_)
++ #include "nsNativeMenuAtomList.h"
++ #undef WIDGET_ATOM
++ #undef WIDGET_ATOM2
+};
+
+/* static */ void
+nsNativeMenuAtoms::RegisterAtoms()
+{
-+ NS_RegisterStaticAtoms(gAtoms);
++ NS_RegisterStaticAtoms(sAtomSetup);
+}
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuAtoms.h
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuAtoms.h
-@@ -0,0 +1,25 @@
++++ b/widget/gtk/nsNativeMenuAtoms.h
+@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -4226,26 +4132,26 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuAtoms.h
+#ifndef __nsNativeMenuAtoms_h__
+#define __nsNativeMenuAtoms_h__
+
-+class nsIAtom;
++class nsAtom;
+
+class nsNativeMenuAtoms
+{
+public:
++ nsNativeMenuAtoms() = delete;
++
+ static void RegisterAtoms();
+
-+#define WIDGET_ATOM(_name) static nsIAtom* _name;
-+#define WIDGET_ATOM2(_name, _value) static nsIAtom* _name;
++#define WIDGET_ATOM(name_) NS_STATIC_ATOM_DECL(name_)
++#define WIDGET_ATOM2(name_, value_) NS_STATIC_ATOM_DECL(name_)
+#include "nsNativeMenuAtomList.h"
+#undef WIDGET_ATOM
+#undef WIDGET_ATOM2
+};
+
+#endif /* __nsNativeMenuAtoms_h__ */
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.cpp
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.cpp
-@@ -0,0 +1,370 @@
++++ b/widget/gtk/nsNativeMenuDocListener.cpp
+@@ -0,0 +1,350 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -4257,7 +4163,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.cpp
+#include "mozilla/DebugOnly.h"
+#include "mozilla/dom/Element.h"
+#include "nsContentUtils.h"
-+#include "nsIAtom.h"
++#include "nsAtom.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+
@@ -4267,7 +4173,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.cpp
+
+using namespace mozilla;
+
-+uint32_t nsNativeMenuDocListener::sUpdateDepth = 0;
++uint32_t nsNativeMenuDocListener::sUpdateBlockersCount = 0;
+
+nsNativeMenuDocListenerTArray *gPendingListeners;
+
@@ -4308,9 +4214,96 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.cpp
+
+NS_IMPL_ISUPPORTS(nsNativeMenuDocListener, nsIMutationObserver)
+
++nsNativeMenuDocListener::~nsNativeMenuDocListener()
++{
++ MOZ_ASSERT(mContentToObserverTable.Count() == 0,
++ "Some nodes forgot to unregister listeners. This is bad! (and we're lucky we made it this far)");
++ MOZ_COUNT_DTOR(nsNativeMenuDocListener);
++}
++
++void
++nsNativeMenuDocListener::AttributeChanged(mozilla::dom::Element *aElement,
++ int32_t aNameSpaceID,
++ nsAtom *aAttribute,
++ int32_t aModType,
++ const nsAttrValue* aOldValue)
++{
++ if (sUpdateBlockersCount == 0) {
++ DoAttributeChanged(aElement, aAttribute);
++ return;
++ }
++
++ MutationRecord *m = *mPendingMutations.AppendElement(new MutationRecord);
++ m->mType = MutationRecord::eAttributeChanged;
++ m->mTarget = aElement;
++ m->mAttribute = aAttribute;
++
++ ScheduleFlush(this);
++}
++
++void
++nsNativeMenuDocListener::ContentAppended(nsIContent *aFirstNewContent)
++{
++ for (nsIContent *c = aFirstNewContent; c; c = c->GetNextSibling()) {
++ ContentInserted(c);
++ }
++}
++
++void
++nsNativeMenuDocListener::ContentInserted(nsIContent *aChild)
++{
++ nsIContent* container = aChild->GetParent();
++ if (!container) {
++ return;
++ }
++
++ nsIContent *prevSibling = nsMenuContainer::GetPreviousSupportedSibling(aChild);
++
++ if (sUpdateBlockersCount == 0) {
++ DoContentInserted(container, aChild, prevSibling);
++ return;
++ }
++
++ MutationRecord *m = *mPendingMutations.AppendElement(new MutationRecord);
++ m->mType = MutationRecord::eContentInserted;
++ m->mTarget = container;
++ m->mChild = aChild;
++ m->mPrevSibling = prevSibling;
++
++ ScheduleFlush(this);
++}
++
++void
++nsNativeMenuDocListener::ContentRemoved(nsIContent *aChild,
++ nsIContent *aPreviousSibling)
++{
++ nsIContent* container = aChild->GetParent();
++ if (!container) {
++ return;
++ }
++
++ if (sUpdateBlockersCount == 0) {
++ DoContentRemoved(container, aChild);
++ return;
++ }
++
++ MutationRecord *m = *mPendingMutations.AppendElement(new MutationRecord);
++ m->mType = MutationRecord::eContentRemoved;
++ m->mTarget = container;
++ m->mChild = aChild;
++
++ ScheduleFlush(this);
++}
++
++void
++nsNativeMenuDocListener::NodeWillBeDestroyed(const nsINode *aNode)
++{
++ mDocument = nullptr;
++}
++
+void
+nsNativeMenuDocListener::DoAttributeChanged(nsIContent *aContent,
-+ nsIAtom *aAttribute)
++ nsAtom *aAttribute)
+{
+ DispatchHelper h(this, aContent);
+ if (h.HasObserver()) {
@@ -4340,44 +4333,44 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.cpp
+}
+
+void
-+nsNativeMenuDocListener::DoBeginUpdateBatch(nsIContent *aTarget)
++nsNativeMenuDocListener::DoBeginUpdates(nsIContent *aTarget)
+{
+ DispatchHelper h(this, aTarget);
+ if (h.HasObserver()) {
-+ h.Observer()->BeginUpdateBatch(aTarget);
++ h.Observer()->OnBeginUpdates(aTarget);
+ }
+}
+
+void
-+nsNativeMenuDocListener::DoEndUpdateBatch(nsIContent *aTarget)
++nsNativeMenuDocListener::DoEndUpdates(nsIContent *aTarget)
+{
+ DispatchHelper h(this, aTarget);
+ if (h.HasObserver()) {
-+ h.Observer()->EndUpdateBatch();
++ h.Observer()->OnEndUpdates();
+ }
+}
+
+void
+nsNativeMenuDocListener::FlushPendingMutations()
+{
-+ nsIContent *batchTarget = nullptr;
-+ bool inUpdateBatch = false;
++ nsIContent *currentTarget = nullptr;
++ bool inUpdateSequence = false;
+
+ while (mPendingMutations.Length() > 0) {
+ MutationRecord *m = mPendingMutations[0];
+
-+ if (m->mTarget != batchTarget) {
-+ if (inUpdateBatch) {
-+ DoEndUpdateBatch(batchTarget);
-+ inUpdateBatch = false;
++ if (m->mTarget != currentTarget) {
++ if (inUpdateSequence) {
++ DoEndUpdates(currentTarget);
++ inUpdateSequence = false;
+ }
+
-+ batchTarget = m->mTarget;
++ currentTarget = m->mTarget;
+
+ if (mPendingMutations.Length() > 1 &&
-+ mPendingMutations[1]->mTarget == batchTarget) {
-+ DoBeginUpdateBatch(batchTarget);
-+ inUpdateBatch = true;
++ mPendingMutations[1]->mTarget == currentTarget) {
++ DoBeginUpdates(currentTarget);
++ inUpdateSequence = true;
+ }
+ }
+
@@ -4398,15 +4391,15 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.cpp
+ mPendingMutations.RemoveElementAt(0);
+ }
+
-+ if (inUpdateBatch) {
-+ DoEndUpdateBatch(batchTarget);
++ if (inUpdateSequence) {
++ DoEndUpdates(currentTarget);
+ }
+}
+
+/* static */ void
+nsNativeMenuDocListener::ScheduleFlush(nsNativeMenuDocListener *aListener)
+{
-+ NS_ASSERTION(sUpdateDepth > 0, "Shouldn't be doing this now");
++ MOZ_ASSERT(sUpdateBlockersCount > 0, "Shouldn't be doing this now");
+
+ if (!gPendingListeners) {
+ gPendingListeners = new nsNativeMenuDocListenerTArray;
@@ -4429,20 +4422,21 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.cpp
+}
+
+/* static */ void
-+nsNativeMenuDocListener::EndUpdates()
++nsNativeMenuDocListener::RemoveUpdateBlocker()
+{
-+ if (sUpdateDepth == 1 && gPendingListeners) {
++ if (sUpdateBlockersCount == 1 && gPendingListeners) {
+ while (gPendingListeners->Length() > 0) {
+ (*gPendingListeners)[0]->FlushPendingMutations();
+ gPendingListeners->RemoveElementAt(0);
+ }
+ }
+
-+ NS_ASSERTION(sUpdateDepth > 0, "Negative update depth!");
-+ sUpdateDepth--;
++ MOZ_ASSERT(sUpdateBlockersCount > 0, "Negative update blockers count!");
++ sUpdateBlockersCount--;
+}
+
-+nsNativeMenuDocListener::nsNativeMenuDocListener() :
++nsNativeMenuDocListener::nsNativeMenuDocListener(nsIContent *aRootNode) :
++ mRootNode(aRootNode),
+ mDocument(nullptr),
+ mLastSource(nullptr),
+ mLastTarget(nullptr)
@@ -4450,127 +4444,19 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.cpp
+ MOZ_COUNT_CTOR(nsNativeMenuDocListener);
+}
+
-+nsNativeMenuDocListener::~nsNativeMenuDocListener()
-+{
-+ MOZ_ASSERT(mContentToObserverTable.Count() == 0,
-+ "Some nodes forgot to unregister listeners. This is bad! (and we're lucky we made it this far)");
-+ MOZ_COUNT_DTOR(nsNativeMenuDocListener);
-+}
-+
-+nsresult
-+nsNativeMenuDocListener::Init(nsIContent *aRootNode)
-+{
-+ NS_ENSURE_ARG(aRootNode);
-+
-+ mRootNode = aRootNode;
-+
-+ return NS_OK;
-+}
-+
-+void
-+nsNativeMenuDocListener::AttributeChanged(nsIDocument *aDocument,
-+ mozilla::dom::Element *aElement,
-+ int32_t aNameSpaceID,
-+ nsIAtom *aAttribute,
-+ int32_t aModType,
-+ const nsAttrValue* aOldValue)
-+{
-+ if (sUpdateDepth == 0) {
-+ DoAttributeChanged(aElement, aAttribute);
-+ return;
-+ }
-+
-+ MutationRecord *m = *mPendingMutations.AppendElement(new MutationRecord);
-+ m->mType = MutationRecord::eAttributeChanged;
-+ m->mTarget = aElement;
-+ m->mAttribute = aAttribute;
-+
-+ ScheduleFlush(this);
-+}
-+
-+void
-+nsNativeMenuDocListener::ContentAppended(nsIDocument *aDocument,
-+ nsIContent *aContainer,
-+ nsIContent *aFirstNewContent,
-+ int32_t aNewIndexInContainer)
-+{
-+ for (nsIContent *c = aFirstNewContent; c; c = c->GetNextSibling()) {
-+ ContentInserted(aDocument, aContainer, c, 0);
-+ }
-+}
-+
-+void
-+nsNativeMenuDocListener::ContentInserted(nsIDocument *aDocument,
-+ nsIContent *aContainer,
-+ nsIContent *aChild,
-+ int32_t aIndexInContainer)
-+{
-+ nsIContent *prevSibling = nsMenuContainer::GetPreviousSupportedSibling(aChild);
-+
-+ if (sUpdateDepth == 0) {
-+ DoContentInserted(aContainer, aChild, prevSibling);
-+ return;
-+ }
-+
-+ MutationRecord *m = *mPendingMutations.AppendElement(new MutationRecord);
-+ m->mType = MutationRecord::eContentInserted;
-+ m->mTarget = aContainer;
-+ m->mChild = aChild;
-+ m->mPrevSibling = prevSibling;
-+
-+ ScheduleFlush(this);
-+}
-+
-+void
-+nsNativeMenuDocListener::ContentRemoved(nsIDocument *aDocument,
-+ nsIContent *aContainer,
-+ nsIContent *aChild,
-+ int32_t aIndexInContainer,
-+ nsIContent *aPreviousSibling)
-+{
-+ if (sUpdateDepth == 0) {
-+ DoContentRemoved(aContainer, aChild);
-+ return;
-+ }
-+
-+ MutationRecord *m = *mPendingMutations.AppendElement(new MutationRecord);
-+ m->mType = MutationRecord::eContentRemoved;
-+ m->mTarget = aContainer;
-+ m->mChild = aChild;
-+
-+ ScheduleFlush(this);
-+}
-+
-+void
-+nsNativeMenuDocListener::NodeWillBeDestroyed(const nsINode *aNode)
-+{
-+ mDocument = nullptr;
-+}
-+
-+/* static */ already_AddRefed<nsNativeMenuDocListener>
-+nsNativeMenuDocListener::Create(nsIContent *aRootNode)
-+{
-+ RefPtr<nsNativeMenuDocListener> listener = new nsNativeMenuDocListener();
-+ if (NS_FAILED(listener->Init(aRootNode))) {
-+ return nullptr;
-+ }
-+
-+ return listener.forget();
-+}
-+
+void
+nsNativeMenuDocListener::RegisterForContentChanges(nsIContent *aContent,
+ nsNativeMenuChangeObserver *aObserver)
+{
-+ NS_ASSERTION(aContent, "Need content parameter");
-+ NS_ASSERTION(aObserver, "Need observer parameter");
++ MOZ_ASSERT(aContent, "Need content parameter");
++ MOZ_ASSERT(aObserver, "Need observer parameter");
+ if (!aContent || !aObserver) {
+ return;
+ }
+
+ DebugOnly<nsNativeMenuChangeObserver *> old;
-+ NS_ASSERTION(!mContentToObserverTable.Get(aContent, &old) || old == aObserver,
-+ "Multiple observers for the same content node are not supported");
++ MOZ_ASSERT(!mContentToObserverTable.Get(aContent, &old) || old == aObserver,
++ "Multiple observers for the same content node are not supported");
+
+ mContentToObserverTable.Put(aContent, aObserver);
+}
@@ -4578,7 +4464,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.cpp
+void
+nsNativeMenuDocListener::UnregisterForContentChanges(nsIContent *aContent)
+{
-+ NS_ASSERTION(aContent, "Need content parameter");
++ MOZ_ASSERT(aContent, "Need content parameter");
+ if (!aContent) {
+ return;
+ }
@@ -4616,11 +4502,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.cpp
+ CancelFlush(this);
+ mPendingMutations.Clear();
+}
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.h
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.h
-@@ -0,0 +1,153 @@
++++ b/widget/gtk/nsNativeMenuDocListener.h
+@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -4639,7 +4523,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.h
+#include "nsStubMutationObserver.h"
+#include "nsTArray.h"
+
-+class nsIAtom;
++class nsAtom;
+class nsIContent;
+class nsIDocument;
+class nsNativeMenuChangeObserver;
@@ -4652,13 +4536,8 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.h
+{
+public:
+ NS_DECL_ISUPPORTS
-+ NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
-+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
-+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
-+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
-+ NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
+
-+ static already_AddRefed<nsNativeMenuDocListener> Create(nsIContent *aRootNode);
++ nsNativeMenuDocListener(nsIContent *aRootNode);
+
+ // Register an observer to receive mutation events for the specified
+ // content node. The caller must keep the observer alive until
@@ -4677,8 +4556,32 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.h
+ // to registered observers.
+ void Stop();
+
++ /*
++ * This class is intended to be used inside GObject signal handlers.
++ * It allows us to queue updates until we have finished delivering
++ * events to Gecko, and then we can batch updates to our view of the
++ * menu. This allows us to do menu updates without altering the structure
++ * seen by the OS.
++ */
++ class MOZ_STACK_CLASS BlockUpdatesScope
++ {
++ public:
++ BlockUpdatesScope(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
++ {
++ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
++ nsNativeMenuDocListener::AddUpdateBlocker();
++ }
++
++ ~BlockUpdatesScope()
++ {
++ nsNativeMenuDocListener::RemoveUpdateBlocker();
++ }
++
++ private:
++ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
++ };
++
+private:
-+ friend class nsNativeMenuAutoUpdateBatch;
+ friend class DispatchHelper;
+
+ struct MutationRecord {
@@ -4691,94 +4594,72 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuDocListener.h
+ nsCOMPtr<nsIContent> mTarget;
+ nsCOMPtr<nsIContent> mChild;
+ nsCOMPtr<nsIContent> mPrevSibling;
-+ nsCOMPtr<nsIAtom> mAttribute;
++ RefPtr<nsAtom> mAttribute;
+ };
+
-+ nsNativeMenuDocListener();
+ ~nsNativeMenuDocListener();
-+ nsresult Init(nsIContent *aRootNode);
+
-+ void DoAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute);
++ NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
++ NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
++ NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
++ NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
++ NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
++
++ void DoAttributeChanged(nsIContent *aContent, nsAtom *aAttribute);
+ void DoContentInserted(nsIContent *aContainer,
+ nsIContent *aChild,
+ nsIContent *aPrevSibling);
+ void DoContentRemoved(nsIContent *aContainer, nsIContent *aChild);
-+ void DoBeginUpdateBatch(nsIContent *aTarget);
-+ void DoEndUpdateBatch(nsIContent *aTarget);
++ void DoBeginUpdates(nsIContent *aTarget);
++ void DoEndUpdates(nsIContent *aTarget);
++
+ void FlushPendingMutations();
+ static void ScheduleFlush(nsNativeMenuDocListener *aListener);
+ static void CancelFlush(nsNativeMenuDocListener *aListener);
-+ static void BeginUpdates() { ++sUpdateDepth; }
-+ static void EndUpdates();
++
++ static void AddUpdateBlocker() { ++sUpdateBlockersCount; }
++ static void RemoveUpdateBlocker();
+
+ nsCOMPtr<nsIContent> mRootNode;
+ nsIDocument *mDocument;
+ nsIContent *mLastSource;
+ nsNativeMenuChangeObserver *mLastTarget;
+ nsTArray<nsAutoPtr<MutationRecord> > mPendingMutations;
-+ nsDataHashtable<nsPtrHashKey<nsIContent>, nsNativeMenuChangeObserver*> mContentToObserverTable;
++ nsDataHashtable<nsPtrHashKey<nsIContent>, nsNativeMenuChangeObserver *> mContentToObserverTable;
+
-+ static uint32_t sUpdateDepth;
++ static uint32_t sUpdateBlockersCount;
+};
+
+typedef nsTArray<RefPtr<nsNativeMenuDocListener> > nsNativeMenuDocListenerTArray;
+
++/*
++ * Implemented by classes that want to listen to mutation events from content
++ * nodes.
++ */
+class nsNativeMenuChangeObserver
+{
+public:
-+ virtual void OnAttributeChanged(nsIContent *aContent, nsIAtom *aAttribute)
-+ {
-+ NS_ERROR("Unhandled AttributeChanged() notification");
-+ }
++ virtual void OnAttributeChanged(nsIContent *aContent, nsAtom *aAttribute) {}
+
+ virtual void OnContentInserted(nsIContent *aContainer,
+ nsIContent *aChild,
-+ nsIContent *aPrevSibling)
-+ {
-+ NS_ERROR("Unhandled ContentInserted() notification");
-+ }
-+
-+ virtual void OnContentRemoved(nsIContent *aContainer, nsIContent *aChild)
-+ {
-+ NS_ERROR("Unhandled ContentRemoved() notification");
-+ }
-+
-+ virtual void BeginUpdateBatch(nsIContent *aContent) { };
++ nsIContent *aPrevSibling) {}
+
-+ virtual void EndUpdateBatch() { };
-+};
-+
-+/*
-+ * This class is intended to be used inside GObject signal handlers.
-+ * It allows us to queue updates until we have finished delivering
-+ * events to Gecko, and then we can batch updates to our view of the
-+ * menu. This allows us to do menu updates without altering the structure
-+ * seen by the OS.
-+ */
-+class MOZ_STACK_CLASS nsNativeMenuAutoUpdateBatch
-+{
-+public:
-+ nsNativeMenuAutoUpdateBatch(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
-+ {
-+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-+ nsNativeMenuDocListener::BeginUpdates();
-+ }
++ virtual void OnContentRemoved(nsIContent *aContainer, nsIContent *aChild) {}
+
-+ ~nsNativeMenuAutoUpdateBatch()
-+ {
-+ nsNativeMenuDocListener::EndUpdates();
-+ }
++ // Signals the start of a sequence of more than 1 event for the specified
++ // node. This only happens when events are flushed as all BlockUpdatesScope
++ // instances go out of scope
++ virtual void OnBeginUpdates(nsIContent *aContent) {};
+
-+private:
-+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
++ // Signals the end of a sequence of events
++ virtual void OnEndUpdates() {};
+};
+
+#endif /* __nsNativeMenuDocListener_h__ */
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
-@@ -0,0 +1,506 @@
++++ b/widget/gtk/nsNativeMenuService.cpp
+@@ -0,0 +1,508 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -4786,18 +4667,18 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
++#include "mozilla/Assertions.h"
++#include "mozilla/Move.h"
+#include "mozilla/Preferences.h"
-+#include "mozilla/Services.h"
++#include "mozilla/UniquePtr.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsCRT.h"
+#include "nsGtkUtils.h"
+#include "nsIContent.h"
-+#include "nsIObserverService.h"
+#include "nsIWidget.h"
+#include "nsServiceManagerUtils.h"
+#include "nsWindow.h"
-+#include "nsXPCOM.h"
+#include "prlink.h"
+
+#include "nsDbusmenu.h"
@@ -4814,7 +4695,6 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
+using namespace mozilla;
+
+nsNativeMenuService* nsNativeMenuService::sService = nullptr;
-+bool nsNativeMenuService::sShutdown = false;
+
+extern PangoLayout* gPangoLayout;
+extern nsNativeMenuDocListenerTArray* gPendingListeners;
@@ -4907,17 +4787,104 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
+ return NS_OK;
+}
+
-+NS_IMPL_ISUPPORTS(nsNativeMenuService, nsINativeMenuService, nsIObserver)
++NS_IMPL_ISUPPORTS(nsNativeMenuService, nsINativeMenuService)
++
++nsNativeMenuService::nsNativeMenuService() :
++ mCreateProxyCancellable(nullptr), mDbusProxy(nullptr), mOnline(false)
++{
++}
++
++nsNativeMenuService::~nsNativeMenuService()
++{
++ SetOnline(false);
++
++ if (mCreateProxyCancellable) {
++ g_cancellable_cancel(mCreateProxyCancellable);
++ g_object_unref(mCreateProxyCancellable);
++ mCreateProxyCancellable = nullptr;
++ }
++
++ // Make sure we disconnect map-event handlers
++ while (mMenuBars.Length() > 0) {
++ NotifyNativeMenuBarDestroyed(mMenuBars[0]);
++ }
++
++ Preferences::UnregisterCallback(PrefChangedCallback,
++ "ui.use_unity_menubar");
++
++ if (mDbusProxy) {
++ g_signal_handlers_disconnect_by_func(mDbusProxy,
++ FuncToGpointer(name_owner_changed_cb),
++ NULL);
++ g_object_unref(mDbusProxy);
++ }
++
++ if (gPendingListeners) {
++ delete gPendingListeners;
++ gPendingListeners = nullptr;
++ }
++ if (gPangoLayout) {
++ g_object_unref(gPangoLayout);
++ gPangoLayout = nullptr;
++ }
++
++ MOZ_ASSERT(sService == this);
++ sService = nullptr;
++}
++
++nsresult
++nsNativeMenuService::Init()
++{
++ nsresult rv = nsDbusmenuFunctions::Init();
++ if (NS_FAILED(rv)) {
++ return rv;
++ }
++
++ rv = GDBusInit();
++ if (NS_FAILED(rv)) {
++ return rv;
++ }
++
++ Preferences::RegisterCallback(PrefChangedCallback,
++ "ui.use_unity_menubar");
++
++ mCreateProxyCancellable = g_cancellable_new();
++
++ g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION,
++ static_cast<GDBusProxyFlags>(
++ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
++ G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START),
++ nullptr,
++ "com.canonical.AppMenu.Registrar",
++ "/com/canonical/AppMenu/Registrar",
++ "com.canonical.AppMenu.Registrar",
++ mCreateProxyCancellable, proxy_created_cb,
++ nullptr);
++
++ /* We don't technically know that the shell will draw the menubar until
++ * we know whether anybody owns the name of the menubar service on the
++ * session bus. However, discovering this happens asynchronously so
++ * we optimize for the common case here by assuming that the shell will
++ * draw window menubars if we are running inside Unity. This should
++ * mean that we avoid temporarily displaying the window menubar ourselves
++ */
++ const char *desktop = getenv("XDG_CURRENT_DESKTOP");
++ if (nsCRT::strcmp(desktop, "Unity") == 0) {
++ SetOnline(true);
++ }
++
++ return NS_OK;
++}
+
+/* static */ void
+nsNativeMenuService::EnsureInitialized()
+{
-+ if (!sService) {
-+ nsCOMPtr<nsINativeMenuService> service =
-+ do_GetService("@mozilla.org/widget/nativemenuservice;1");
++ if (sService) {
++ return;
+ }
-+ NS_ASSERTION(sService != nullptr || sShutdown == true,
-+ "Failed to create native menu service");
++ nsCOMPtr<nsINativeMenuService> service =
++ do_GetService("@mozilla.org/widget/nativemenuservice;1");
+}
+
+void
@@ -4952,28 +4919,30 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
+
+ if (!mDbusProxy ||
+ !gtk_widget_get_mapped(aMenuBar->TopLevelWindow()) ||
-+ aMenuBar->RegisterRequestInProgress()) {
++ mMenuBarRegistrationCancellables.Get(aMenuBar, nullptr)) {
+ // Don't go further if we don't have a proxy for the shell menu
+ // service, the window isn't mapped or there is a request in progress.
+ return;
+ }
+
+ uint32_t xid = aMenuBar->WindowId();
-+ nsAdoptingCString path = aMenuBar->ObjectPath();
++ nsCString path = aMenuBar->ObjectPath();
+ if (xid == 0 || path.IsEmpty()) {
+ NS_WARNING("Menubar has invalid XID or object path");
+ return;
+ }
+
++ GCancellable *cancellable = g_cancellable_new();
++ mMenuBarRegistrationCancellables.Put(aMenuBar, cancellable);
++
+ // We keep a weak ref because we can't assume that GDBus cancellation
+ // is reliable (see https://launchpad.net/bugs/953562)
-+ nsAutoWeakMenuObject<nsMenuBar> weakMenuBar(aMenuBar);
+
+ g_dbus_proxy_call(mDbusProxy, "RegisterWindow",
+ g_variant_new("(uo)", xid, path.get()),
+ G_DBUS_CALL_FLAGS_NONE, -1,
-+ aMenuBar->BeginRegisterRequest(),
-+ register_native_menubar_cb, weakMenuBar.forget());
++ cancellable,
++ register_native_menubar_cb, aMenuBar);
+}
+
+/* static */ void
@@ -5002,14 +4971,15 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
+
+ // We need this check because we can't assume that GDBus cancellation
+ // is reliable (see https://launchpad.net/bugs/953562)
-+ if (sShutdown) {
++ nsNativeMenuService *self = nsNativeMenuService::GetSingleton();
++ if (!self) {
+ if (proxy) {
+ g_object_unref(proxy);
+ }
+ return;
+ }
+
-+ nsNativeMenuService::GetSingleton()->OnProxyCreated(proxy);
++ self->OnProxyCreated(proxy);
+}
+
+/* static */ void
@@ -5017,8 +4987,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
+ GAsyncResult *res,
+ gpointer user_data)
+{
-+ nsAutoWeakMenuObject<nsMenuBar> weakMenuBar(
-+ static_cast<nsWeakMenuObject<nsMenuBar> *>(user_data));
++ nsMenuBar *menuBar = static_cast<nsMenuBar *>(user_data);
+
+ GError *error = nullptr;
+ GVariant *results = g_dbus_proxy_call_finish(G_DBUS_PROXY(source_object),
@@ -5038,12 +5007,12 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
+ g_error_free(error);
+ }
+
-+ if (sShutdown || !weakMenuBar) {
++ nsNativeMenuService *self = nsNativeMenuService::GetSingleton();
++ if (!self) {
+ return;
+ }
+
-+ nsNativeMenuService::GetSingleton()->OnNativeMenuBarRegistered(weakMenuBar,
-+ success);
++ self->OnNativeMenuBarRegistered(menuBar, success);
+}
+
+/* static */ gboolean
@@ -5069,7 +5038,9 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
+nsNativeMenuService::OnProxyCreated(GDBusProxy *aProxy)
+{
+ mDbusProxy = aProxy;
-+ mCreateProxyRequest.Finish();
++
++ g_object_unref(mCreateProxyCancellable);
++ mCreateProxyCancellable = nullptr;
+
+ if (!mDbusProxy) {
+ SetOnline(false);
@@ -5086,7 +5057,16 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
+nsNativeMenuService::OnNativeMenuBarRegistered(nsMenuBar *aMenuBar,
+ bool aSuccess)
+{
-+ aMenuBar->EndRegisterRequest();
++ // Don't assume that GDBus cancellation is reliable (ie, |aMenuBar| might
++ // have already been deleted (see https://launchpad.net/bugs/953562)
++ GCancellable *cancellable = nullptr;
++ if (!mMenuBarRegistrationCancellables.Get(aMenuBar, &cancellable)) {
++ return;
++ }
++
++ g_object_unref(cancellable);
++ mMenuBarRegistrationCancellables.Remove(aMenuBar);
++
+ if (!aSuccess) {
+ aMenuBar->Deactivate();
+ }
@@ -5110,92 +5090,44 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
+ OnNameOwnerChanged();
+}
+
-+nsNativeMenuService::nsNativeMenuService() :
-+ mDbusProxy(nullptr), mOnline(false)
-+{
-+}
-+
-+nsNativeMenuService::~nsNativeMenuService()
++NS_IMETHODIMP
++nsNativeMenuService::CreateNativeMenuBar(nsIWidget *aParent,
++ mozilla::dom::Element *aMenuBarNode)
+{
-+ SetOnline(false);
-+
-+ // Make sure we disconnect map-event handlers
-+ while (mMenuBars.Length() > 0) {
-+ NotifyNativeMenuBarDestroyed(mMenuBars[0]);
-+ }
-+
-+ Preferences::UnregisterCallback(PrefChangedCallback,
-+ "ui.use_unity_menubar");
-+
-+ if (mDbusProxy) {
-+ g_signal_handlers_disconnect_by_func(mDbusProxy,
-+ FuncToGpointer(name_owner_changed_cb),
-+ NULL);
-+ g_object_unref(mDbusProxy);
-+ }
-+
-+ if (gPendingListeners) {
-+ delete gPendingListeners;
-+ gPendingListeners = nullptr;
-+ }
-+ if (gPangoLayout) {
-+ g_object_unref(gPangoLayout);
-+ gPangoLayout = nullptr;
-+ }
-+}
++ NS_ENSURE_ARG(aParent);
++ NS_ENSURE_ARG(aMenuBarNode);
+
-+nsresult
-+nsNativeMenuService::Init()
-+{
-+ nsresult rv = nsDbusmenuFunctions::Init();
-+ if (NS_FAILED(rv)) {
-+ return rv;
++ if (aMenuBarNode->AsElement()->AttrValueIs(kNameSpaceID_None,
++ nsNativeMenuAtoms::_moz_menubarkeeplocal,
++ nsGkAtoms::_true,
++ eCaseMatters)) {
++ return NS_OK;
+ }
+
-+ rv = GDBusInit();
-+ if (NS_FAILED(rv)) {
-+ return rv;
++ UniquePtr<nsMenuBar> menubar(nsMenuBar::Create(aParent, aMenuBarNode));
++ if (!menubar) {
++ NS_WARNING("Failed to create menubar");
++ return NS_ERROR_FAILURE;
+ }
+
-+ Preferences::RegisterCallback(PrefChangedCallback,
-+ "ui.use_unity_menubar");
-+
-+ mCreateProxyRequest.Start();
-+
-+ g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION,
-+ static_cast<GDBusProxyFlags>(
-+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
-+ G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
-+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START),
-+ nullptr,
-+ "com.canonical.AppMenu.Registrar",
-+ "/com/canonical/AppMenu/Registrar",
-+ "com.canonical.AppMenu.Registrar",
-+ mCreateProxyRequest, proxy_created_cb,
-+ nullptr);
++ // Unity forgets our window if it is unmapped by the application, which
++ // happens with some extensions that add "minimize to tray" type
++ // functionality. We hook on to the MapNotify event to re-register our menu
++ // with Unity
++ g_signal_connect(G_OBJECT(menubar->TopLevelWindow()),
++ "map-event", G_CALLBACK(map_event_cb),
++ menubar.get());
+
-+ /* We don't technically know that the shell will draw the menubar until
-+ * we know whether anybody owns the name of the menubar service on the
-+ * session bus. However, discovering this happens asynchronously so
-+ * we optimize for the common case here by assuming that the shell will
-+ * draw window menubars if we are running inside Unity. This should
-+ * mean that we avoid temporarily displaying the window menubar ourselves
-+ */
-+ const char *desktop = getenv("XDG_CURRENT_DESKTOP");
-+ if (nsCRT::strcmp(desktop, "Unity") == 0) {
-+ SetOnline(true);
-+ }
++ mMenuBars.AppendElement(menubar.get());
++ RegisterNativeMenuBar(menubar.get());
+
-+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
-+ if (os) {
-+ os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
-+ }
++ static_cast<nsWindow *>(aParent)->SetMenuBar(Move(menubar));
+
+ return NS_OK;
+}
+
+/* static */ already_AddRefed<nsNativeMenuService>
-+nsNativeMenuService::GetInstance()
++nsNativeMenuService::GetInstanceForServiceManager()
+{
+ RefPtr<nsNativeMenuService> service(sService);
+
@@ -5203,23 +5135,13 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
+ return service.forget();
+ }
+
-+ NS_ASSERTION(sShutdown == false,
-+ "Attempted to access menubar service too late");
-+
-+ if (sShutdown) {
-+ return nullptr;
-+ }
-+
-+ sService = new nsNativeMenuService();
-+ NS_ADDREF(sService);
++ service = new nsNativeMenuService();
+
-+ if (NS_FAILED(sService->Init())) {
-+ NS_RELEASE(sService);
-+ sService = nullptr;
++ if (NS_FAILED(service->Init())) {
+ return nullptr;
+ }
+
-+ service = sService;
++ sService = service.get();
+ return service.forget();
+}
+
@@ -5230,23 +5152,6 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
+ return sService;
+}
+
-+NS_IMETHODIMP
-+nsNativeMenuService::Observe(nsISupports *aSubject,
-+ const char *aTopic,
-+ const char16_t *aData)
-+{
-+ if (!nsCRT::strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
-+ sShutdown = true;
-+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
-+ if (os) {
-+ os->RemoveObserver(sService, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
-+ }
-+ NS_IF_RELEASE(sService);
-+ }
-+
-+ return NS_OK;
-+}
-+
+void
+nsNativeMenuService::NotifyNativeMenuBarDestroyed(nsMenuBar *aMenuBar)
+{
@@ -5255,41 +5160,17 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.cpp
+ aMenuBar);
+
+ mMenuBars.RemoveElement(aMenuBar);
-+}
+
-+NS_IMETHODIMP
-+nsNativeMenuService::CreateNativeMenuBar(nsIWidget *aParent,
-+ nsIContent *aMenuBarNode)
-+{
-+ NS_ENSURE_ARG(aParent);
-+ NS_ENSURE_ARG(aMenuBarNode);
-+
-+ nsAutoPtr<nsMenuBar> menubar(nsMenuBar::Create(aParent, aMenuBarNode));
-+ if (!menubar) {
-+ NS_WARNING("Failed to create menubar");
-+ return NS_ERROR_FAILURE;
++ GCancellable *cancellable = nullptr;
++ if (mMenuBarRegistrationCancellables.Get(aMenuBar, &cancellable)) {
++ mMenuBarRegistrationCancellables.Remove(aMenuBar);
++ g_cancellable_cancel(cancellable);
++ g_object_unref(cancellable);
+ }
-+
-+ // Unity forgets our window if it is unmapped by the application, which
-+ // happens with some extensions that add "minimize to tray" type
-+ // functionality. We hook on to the MapNotify event to re-register our menu
-+ // with Unity
-+ g_signal_connect(G_OBJECT(menubar->TopLevelWindow()),
-+ "map-event", G_CALLBACK(map_event_cb),
-+ menubar);
-+
-+ mMenuBars.AppendElement(menubar);
-+ RegisterNativeMenuBar(menubar);
-+
-+ static_cast<nsWindow *>(aParent)->SetMenuBar(menubar.forget());
-+
-+ return NS_OK;
+}
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.h
-===================================================================
--- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.h
-@@ -0,0 +1,88 @@
++++ b/widget/gtk/nsNativeMenuService.h
+@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
@@ -5302,12 +5183,10 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.h
+
+#include "mozilla/Attributes.h"
+#include "nsCOMPtr.h"
++#include "nsDataHashtable.h"
+#include "nsINativeMenuService.h"
-+#include "nsIObserver.h"
+#include "nsTArray.h"
+
-+#include "nsNativeMenuUtils.h"
-+
+#include <gdk/gdk.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
@@ -5323,29 +5202,26 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.h
+ * needs to. The menubar is responsible for notifying the service when the last
+ * reference to it is dropped.
+ */
-+class nsNativeMenuService final : public nsINativeMenuService,
-+ public nsIObserver
++class nsNativeMenuService final : public nsINativeMenuService
+{
+public:
+ NS_DECL_ISUPPORTS
-+ NS_DECL_NSIOBSERVER
-+
-+ NS_IMETHOD CreateNativeMenuBar(nsIWidget* aParent, nsIContent* aMenuBarNode);
+
-+ nsresult Init();
++ NS_IMETHOD CreateNativeMenuBar(nsIWidget* aParent, mozilla::dom::Element* aMenuBarNode) override;
+
+ // Returns the singleton addref'd for the service manager
-+ static already_AddRefed<nsNativeMenuService> GetInstance();
++ static already_AddRefed<nsNativeMenuService> GetInstanceForServiceManager();
+
+ // Returns the singleton without increasing the reference count
+ static nsNativeMenuService* GetSingleton();
+
-+ // Called by a menubar when the last reference to it is dropped
++ // Called by a menubar when it is deleted
+ void NotifyNativeMenuBarDestroyed(nsMenuBar *aMenuBar);
+
+private:
+ nsNativeMenuService();
+ ~nsNativeMenuService();
++ nsresult Init();
+
+ static void EnsureInitialized();
+ void SetOnline(bool aOnline);
@@ -5368,104 +5244,39 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuService.h
+ static void PrefChangedCallback(const char *aPref, void *aClosure);
+ void PrefChanged();
+
-+ nsNativeMenuGIORequest mCreateProxyRequest;
++ GCancellable *mCreateProxyCancellable;
+ GDBusProxy *mDbusProxy;
+ bool mOnline;
+ nsTArray<nsMenuBar *> mMenuBars;
++ nsDataHashtable<nsPtrHashKey<nsMenuBar>, GCancellable*> mMenuBarRegistrationCancellables;
+
+ static bool sShutdown;
+ static nsNativeMenuService *sService;
+};
+
+#endif /* __nsNativeMenuService_h__ */
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuUtils.h
-===================================================================
---- /dev/null
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsNativeMenuUtils.h
-@@ -0,0 +1,59 @@
-+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-+/* vim:expandtab:shiftwidth=4:tabstop=4:
-+ */
-+/* This Source Code Form is subject to the terms of the Mozilla Public
-+ * License, v. 2.0. If a copy of the MPL was not distributed with this
-+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-+
-+#ifndef __nsNativeMenuUtils_h__
-+#define __nsNativeMenuUtils_h__
-+
-+#include <glib-object.h>
-+#include <gio/gio.h>
-+
-+class nsNativeMenuGIORequest
-+{
-+public:
-+ nsNativeMenuGIORequest() : mCancellable(nullptr) { };
-+
-+ ~nsNativeMenuGIORequest() {
-+ Cancel();
-+ }
-+
-+ void Start() {
-+ Cancel();
-+ mCancellable = g_cancellable_new();
-+ }
-+
-+ void Finish() {
-+ if (mCancellable) {
-+ g_object_unref(mCancellable);
-+ mCancellable = nullptr;
-+ }
-+ }
-+
-+ void Cancel() {
-+ if (mCancellable) {
-+ g_cancellable_cancel(mCancellable);
-+ g_object_unref(mCancellable);
-+ mCancellable = nullptr;
-+ }
-+ }
-+
-+ bool InProgress() const {
-+ if (!mCancellable) {
-+ return false;
-+ }
-+
-+ return !g_cancellable_is_cancelled(mCancellable);
-+ }
-+
-+ operator GCancellable*() const {
-+ return mCancellable;
-+ }
-+
-+private:
-+ GCancellable *mCancellable;
-+};
-+
-+#endif /* __nsNativeMenuUtils_h__ */
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsWidgetFactory.cpp
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/widget/gtk/nsWidgetFactory.cpp
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsWidgetFactory.cpp
-@@ -49,6 +49,8 @@
+--- a/widget/gtk/nsWidgetFactory.cpp
++++ b/widget/gtk/nsWidgetFactory.cpp
+@@ -48,6 +48,8 @@
#include "GfxInfoX11.h"
#endif
+#include "nsNativeMenuService.h"
+
#include "nsNativeThemeGTK.h"
+ #include "HeadlessThemeGTK.h"
- #include "nsIComponentRegistrar.h"
-@@ -121,6 +123,9 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxI
+@@ -115,6 +117,9 @@
}
#endif
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsNativeMenuService,
-+ nsNativeMenuService::GetInstance)
++ nsNativeMenuService::GetInstanceForServiceManager)
+
#ifdef NS_PRINTING
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecGTK)
- NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintOptionsGTK, Init)
-@@ -223,6 +228,7 @@ NS_DEFINE_NAMED_CID(NS_IMAGE_TO_PIXBUF_C
+ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSettingsServiceGTK, Init)
+@@ -227,6 +232,7 @@
NS_DEFINE_NAMED_CID(NS_IDLE_SERVICE_CID);
NS_DEFINE_NAMED_CID(NS_GFXINFO_CID);
#endif
@@ -5473,7 +5284,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsWidgetFactory.cpp
static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
-@@ -258,6 +264,7 @@ static const mozilla::Module::CIDEntry k
+@@ -262,6 +268,7 @@
{ &kNS_IDLE_SERVICE_CID, false, nullptr, nsIdleServiceGTKConstructor },
{ &kNS_GFXINFO_CID, false, nullptr, mozilla::widget::GfxInfoConstructor },
#endif
@@ -5481,7 +5292,7 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsWidgetFactory.cpp
{ nullptr }
};
-@@ -295,6 +302,7 @@ static const mozilla::Module::ContractID
+@@ -299,6 +306,7 @@
{ "@mozilla.org/widget/idleservice;1", &kNS_IDLE_SERVICE_CID },
{ "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID },
#endif
@@ -5489,27 +5300,31 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsWidgetFactory.cpp
{ nullptr }
};
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsWindow.cpp
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/widget/gtk/nsWindow.cpp
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsWindow.cpp
-@@ -5175,6 +5175,11 @@ nsWindow::HideWindowChrome(bool aShouldH
- return NS_OK;
+--- a/widget/gtk/nsWindow.cpp
++++ b/widget/gtk/nsWindow.cpp
+@@ -67,6 +67,7 @@
+
+ #include "mozilla/Assertions.h"
+ #include "mozilla/Likely.h"
++#include "mozilla/Move.h"
+ #include "mozilla/Preferences.h"
+ #include "nsIPrefService.h"
+ #include "nsIGConfService.h"
+@@ -5155,6 +5156,11 @@
+ SetWindowDecoration(aShouldHide ? eBorderStyle_none : mBorderStyle);
}
+void
-+nsWindow::SetMenuBar(nsMenuBar *aMenuBar) {
-+ mMenuBar.reset(aMenuBar);
++nsWindow::SetMenuBar(UniquePtr<nsMenuBar> aMenuBar) {
++ mMenuBar = mozilla::Move(aMenuBar);
+}
+
bool
nsWindow::CheckForRollup(gdouble aMouseX, gdouble aMouseY,
bool aIsWheel, bool aAlwaysRollup)
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsWindow.h
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/widget/gtk/nsWindow.h
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsWindow.h
-@@ -35,6 +35,8 @@
+--- a/widget/gtk/nsWindow.h
++++ b/widget/gtk/nsWindow.h
+@@ -40,6 +40,8 @@
#include "IMContextWrapper.h"
@@ -5518,191 +5333,47 @@ Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsWindow.h
#undef LOG
#ifdef MOZ_LOGGING
-@@ -162,6 +164,8 @@ public:
+@@ -163,6 +165,8 @@
nsIScreen* aTargetScreen = nullptr) override;
- NS_IMETHOD HideWindowChrome(bool aShouldHide) override;
+ virtual void HideWindowChrome(bool aShouldHide) override;
-+ void SetMenuBar(nsMenuBar *aMenuBar);
++ void SetMenuBar(mozilla::UniquePtr<nsMenuBar> aMenuBar);
+
/**
* GetLastUserInputTime returns a timestamp for the most recent user input
* event. This is intended for pointer grab requests (including drags).
-@@ -569,6 +573,8 @@ private:
- RefPtr<mozilla::widget::IMContextWrapper> mIMContext;
+@@ -600,6 +604,8 @@
mozilla::UniquePtr<mozilla::CurrentX11TimeGetter> mCurrentTimeGetter;
+ static CSDSupportLevel sCSDSupportLevel;
+
+ mozilla::UniquePtr<nsMenuBar> mMenuBar;
};
- class nsChildWindow : public nsWindow {
-Index: firefox-52.0~b9+build2/mozilla/xpfe/appshell/nsWebShellWindow.cpp
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/xpfe/appshell/nsWebShellWindow.cpp
-+++ firefox-52.0~b9+build2/mozilla/xpfe/appshell/nsWebShellWindow.cpp
-@@ -58,6 +58,7 @@
- #include "nsIScreen.h"
-
- #include "nsIContent.h" // for menus
-+#include "nsIAtom.h"
- #include "nsIScriptSecurityManager.h"
-
- // For calculating size
-@@ -73,7 +74,7 @@
-
- #include "nsPIWindowRoot.h"
-
--#ifdef XP_MACOSX
-+#if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
- #include "nsINativeMenuService.h"
- #define USE_NATIVE_MENUS
- #endif
-@@ -498,6 +499,11 @@ static void LoadNativeMenus(nsIDOMDocume
-
- if (menubarNode) {
- nsCOMPtr<nsIContent> menubarContent(do_QueryInterface(menubarNode));
-+#ifdef MOZ_WIDGET_GTK
-+ nsCOMPtr<nsIAtom> atom = NS_Atomize(NS_LITERAL_CSTRING("_moz-menubarkeeplocal"));
-+ if (menubarContent->AttrValueIs(kNameSpaceID_None, atom, nsGkAtoms::_true, eCaseMatters))
-+ return;
-+#endif
- nms->CreateNativeMenuBar(aParentWindow, menubarContent);
- } else {
- nms->CreateNativeMenuBar(aParentWindow, nullptr);
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/moz.build
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/widget/gtk/moz.build
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/moz.build
-@@ -24,10 +24,18 @@ UNIFIED_SOURCES += [
- 'nsAppShell.cpp',
- 'nsBidiKeyboard.cpp',
- 'nsColorPicker.cpp',
-+ 'nsDbusmenu.cpp',
- 'nsFilePicker.cpp',
- 'nsGtkKeyUtils.cpp',
- 'nsImageToPixbuf.cpp',
- 'nsLookAndFeel.cpp',
-+ 'nsMenuBar.cpp',
-+ 'nsMenuContainer.cpp',
-+ 'nsMenuItem.cpp',
-+ 'nsMenuObject.cpp',
-+ 'nsMenuSeparator.cpp',
-+ 'nsNativeMenuAtoms.cpp',
-+ 'nsNativeMenuDocListener.cpp',
- 'nsNativeThemeGTK.cpp',
- 'nsScreenGtk.cpp',
- 'nsScreenManagerGtk.cpp',
-@@ -40,6 +48,8 @@ UNIFIED_SOURCES += [
- ]
-
- SOURCES += [
-+ 'nsMenu.cpp', # conflicts with X11 headers
-+ 'nsNativeMenuService.cpp',
- 'nsWindow.cpp', # conflicts with X11 headers
- ]
-
-@@ -104,6 +114,7 @@ FINAL_LIBRARY = 'xul'
-
- LOCAL_INCLUDES += [
- '/layout/generic',
-+ '/layout/style',
- '/layout/xul',
- '/other-licenses/atk-1.0',
- '/widget',
-Index: firefox-52.0~b9+build2/mozilla/browser/base/content/browser.js
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/browser/base/content/browser.js
-+++ firefox-52.0~b9+build2/mozilla/browser/base/content/browser.js
-@@ -5079,6 +5079,8 @@ function getTogglableToolbars() {
- let toolbarNodes = Array.slice(gNavToolbox.childNodes);
- toolbarNodes = toolbarNodes.concat(gNavToolbox.externalToolbars);
- toolbarNodes = toolbarNodes.filter(node => node.getAttribute("toolbarname"));
-+ if (document.documentElement.getAttribute("shellshowingmenubar") == "true")
-+ toolbarNodes = toolbarNodes.filter(node => node.id != "toolbar-menubar");
- return toolbarNodes;
- }
-
-Index: firefox-52.0~b9+build2/mozilla/widget/moz.build
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/widget/moz.build
-+++ firefox-52.0~b9+build2/mozilla/widget/moz.build
-@@ -37,10 +37,12 @@ elif toolkit == 'cocoa':
+ #endif /* __nsWindow_h__ */
+--- a/widget/moz.build
++++ b/widget/moz.build
+@@ -64,9 +64,9 @@
+ 'nsISystemStatusBar.idl',
'nsITaskbarProgress.idl',
]
- EXPORTS += [
+- EXPORTS += [
- 'nsINativeMenuService.h',
- 'nsIPrintDialogService.h',
- ]
-
+- ]
++
+if toolkit in ('cocoa', 'gtk2', 'gtk3'):
+ EXPORTS += ['nsINativeMenuService.h']
-+
- TEST_DIRS += ['tests']
-
- # Don't build the DSO under the 'build' directory as windows does.
-Index: firefox-52.0~b9+build2/mozilla/modules/libpref/init/all.js
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/modules/libpref/init/all.js
-+++ firefox-52.0~b9+build2/mozilla/modules/libpref/init/all.js
-@@ -229,6 +229,9 @@ pref("dom.compartment_per_addon", true);
- pref("browser.sessionhistory.max_total_viewers", -1);
-
- pref("ui.use_native_colors", true);
-+#ifdef MOZ_WIDGET_GTK
-+pref("ui.use_unity_menubar", true);
-+#endif
- pref("ui.click_hold_context_menus", false);
- // Duration of timeout of incremental search in menus (ms). 0 means infinite.
- pref("ui.menu.incremental_search.timeout", 1000);
-Index: firefox-52.0~b9+build2/mozilla/widget/gtk/nsScreenGtk.cpp
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/widget/gtk/nsScreenGtk.cpp
-+++ firefox-52.0~b9+build2/mozilla/widget/gtk/nsScreenGtk.cpp
-@@ -15,6 +15,7 @@
- #include <gtk/gtk.h>
- #include <dlfcn.h>
- #include "gfxPlatformGtk.h"
-+#include "nsIWidget.h"
-
- static uint32_t sScreenId = 0;
-
-Index: firefox-52.0~b9+build2/mozilla/layout/build/moz.build
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/layout/build/moz.build
-+++ firefox-52.0~b9+build2/mozilla/layout/build/moz.build
-@@ -77,6 +77,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'go
- LOCAL_INCLUDES += [
- '/dom/system/gonk',
- ]
-+elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
-+ LOCAL_INCLUDES += [
-+ '/widget/gtk',
-+ ]
- if CONFIG['MOZ_WEBSPEECH']:
- LOCAL_INCLUDES += [
-Index: firefox-52.0~b9+build2/mozilla/layout/build/nsLayoutStatics.cpp
-===================================================================
---- firefox-52.0~b9+build2.orig/mozilla/layout/build/nsLayoutStatics.cpp
-+++ firefox-52.0~b9+build2/mozilla/layout/build/nsLayoutStatics.cpp
-@@ -132,6 +132,10 @@ using namespace mozilla::system;
- #include "mozilla/StaticPresData.h"
- #include "mozilla/dom/WebIDLGlobalNameHash.h"
+ TEST_DIRS += ['tests']
-+#ifdef MOZ_WIDGET_GTK
-+#include "nsNativeMenuAtoms.h"
-+#endif
-+
- using namespace mozilla;
- using namespace mozilla::net;
- using namespace mozilla::dom;
-@@ -166,6 +170,9 @@ nsLayoutStatics::Initialize()
- nsTextServicesDocument::RegisterAtoms();
- nsHTMLTags::RegisterAtoms();
- nsRDFAtoms::RegisterAtoms();
-+#ifdef MOZ_WIDGET_GTK
-+ nsNativeMenuAtoms::RegisterAtoms();
-+#endif
+--- a/xpfe/appshell/nsWebShellWindow.cpp
++++ b/xpfe/appshell/nsWebShellWindow.cpp
+@@ -71,7 +71,7 @@
- NS_SealStaticAtomTable();
+ #include "gfxPlatform.h"
+-#ifdef XP_MACOSX
++#if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
+ #include "nsINativeMenuService.h"
+ #define USE_NATIVE_MENUS
+ #endif