diff options
-rw-r--r-- | .SRCINFO | 22 | ||||
-rw-r--r-- | 0001-Bug-1338655-Don-t-try-to-build-mp4parse-bindings.-r-.patch | 48 | ||||
-rw-r--r-- | PKGBUILD | 79 | ||||
-rw-r--r-- | fix-wifi-scanner.diff | 16 | ||||
-rw-r--r-- | no-crmf.diff | 39 | ||||
-rw-r--r-- | rust-i686.patch | 16 | ||||
-rw-r--r-- | thunderbird-60.2.1-buildfix.patch | 42 | ||||
-rw-r--r-- | thunderbird-install-dir.patch | 12 | ||||
-rw-r--r-- | unity-menubar.patch | 3725 |
9 files changed, 1773 insertions, 2226 deletions
@@ -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 - @@ -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, ¶m)) { - 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 |