summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--waterfox-g/debian/_service4
-rwxr-xr-xwaterfox-g/debian/build.sh81
-rw-r--r--waterfox-g/debian/changelog39
-rw-r--r--waterfox-g/debian/control6
-rw-r--r--waterfox-g/debian/mozconfig42
-rw-r--r--waterfox-g/debian/mozconfig_LANG19
-rw-r--r--waterfox-g/debian/patches/debian-hacks/Relax-cargo-version-requirement.patch21
-rw-r--r--waterfox-g/debian/patches/debian-hacks/Relax-nodejs-minimum-version.patch21
-rw-r--r--waterfox-g/debian/patches/fis-csd-global-menu.patch7
-rw-r--r--waterfox-g/debian/patches/fix-langpack-id.patch39
-rw-r--r--waterfox-g/debian/patches/fix-wayland-build.patch4
-rw-r--r--waterfox-g/debian/patches/g-kde.patch1414
-rw-r--r--waterfox-g/debian/patches/global_menu.patch5382
-rw-r--r--waterfox-g/debian/patches/libavcodec58_91.patch9
-rw-r--r--waterfox-g/debian/patches/mach-depends.patch17
-rw-r--r--waterfox-g/debian/patches/mozilla-ntlm-full-path.patch17
-rw-r--r--waterfox-g/debian/patches/nongnome-proxies.patch13
-rw-r--r--waterfox-g/debian/patches/series5
-rw-r--r--waterfox-g/debian/patches/waterfox-branded-icons.patch19
-rwxr-xr-xwaterfox-g/debian/rules39
20 files changed, 885 insertions, 6313 deletions
diff --git a/waterfox-g/debian/_service b/waterfox-g/debian/_service
index 3bcea87..d03384b 100644
--- a/waterfox-g/debian/_service
+++ b/waterfox-g/debian/_service
@@ -1,7 +1,7 @@
<services>
<service name="tar_scm">
<param name="scm">git</param>
- <param name="url">https://bgstack15.ddns.net/cgit/stackrpms</param>
+ <param name="url">https://gitlab.com/bgstack15/stackrpms.git</param>
<param name="subdir">waterfox-g/debian</param>
<param name="filename">debian</param>
<param name="revision">waterfox-g-bump</param>
@@ -21,7 +21,7 @@
<param name="filename">waterfox-g</param>
<param name="versionformat">@PARENT_TAG@</param>
<param name="versionrewrite-pattern">G(.*)</param>
- <param name="revision">G5.1.10</param>
+ <param name="revision">G6.0.2</param>
</service>
<service mode="buildtime" name="tar">
<param name="obsinfo">waterfox-g.obsinfo</param>
diff --git a/waterfox-g/debian/build.sh b/waterfox-g/debian/build.sh
new file mode 100755
index 0000000..b7a1f9e
--- /dev/null
+++ b/waterfox-g/debian/build.sh
@@ -0,0 +1,81 @@
+#!/bin/bash
+
+# Upstream modifies version_display.txt using script included in GitHub Actions (version=git_tag),
+# without patching. Debhelper won't allow us to do that by same way and
+# patching won't be much convenient, so we need to create new files.
+mkdir -p "$(pwd)"/debian/app_version
+
+cp "$(pwd)"/browser/config/version.txt "$(pwd)"/debian/app_version/version.txt
+echo "$WF_VERSION" >"$(pwd)"/debian/app_version/version_display.txt
+
+# Tune flags
+OPT_EXTRA_FLAGS="-march=x86-64 -mtune=k8"
+CFLAGS+=" $OPT_EXTRA_FLAGS"
+CXXFLAGS+=" $OPT_EXTRA_FLAGS"
+
+# LTO needs more open files
+ulimit -n 4096
+# Do 3-tier PGO
+LDFLAGS+=" -Wl,--no-keep-memory -Wl,--no-mmap-output-file"
+export LDFLAGS
+if test `lsb_release -sc` = "bionic" || test `lsb_release -sc` = "focal" || test `lsb_release -sc` = "buster"; then
+export NODEJS=/usr/lib/nodejs-mozilla/bin/node
+fi
+
+if test `lsb_release -sc` = "bionic"; then
+export NASM=/usr/lib/nasm-mozilla/bin/nasm
+fi
+
+# For successfull LTO build, we need to use matching LLVM version
+if test `lsb_release -sc` = "bionic" || test `lsb_release -sc` = "focal" || test `lsb_release -sc` = "buster" || test `lsb_release -sc` = "bullseye"; then
+export PATH=/usr/lib/llvm-13/bin/:$PATH
+fi
+
+if test `lsb_release -sc` = "jammy"; then
+export PATH=/usr/lib/llvm-15/bin/:$PATH
+fi
+
+export CC=clang
+export CXX=clang++
+export AR=llvm-ar
+export NM=llvm-nm
+export RANLIB=llvm-ranlib
+export LLVM_PROFDATA=llvm-profdata
+export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
+export GEN_PGO=1
+./mach build
+
+echo "Profiling instrumented browser..."
+./mach package
+JARLOG_FILE="$(pwd)/jarlog" xvfb-run -s "-screen 0 1920x1080x24 -nolisten local" ./mach python build/pgo/profileserver.py
+
+stat -c "Profile data found (%s bytes)" merged.profdata
+test -s merged.profdata
+
+stat -c "Jar log found (%s bytes)" jarlog
+test -s jarlog
+
+echo "Removing instrumented browser..."
+./mach clobber
+
+echo "Building optimized browser..."
+unset GEN_PGO
+export USE_PGO=1
+./mach build
+
+# Build langpacks
+mkdir -p "$(pwd)"/extensions
+# langpack-build can not be done in parallel easily (see https://bugzilla.mozilla.org/show_bug.cgi?id=1660943)
+# Therefore, we have to have a separate obj-dir for each language
+# We do this, by creating a mozconfig-template with the necessary switches
+# and a placeholder obj-dir, which gets copied and modified for each language
+sed -r '/^(ja-JP-mac|en-US|)$/d;s/ .*$//' debian/locales.shipped | cut -f1 -d":" |
+ xargs -n 1 -P $JOBS -I {} /bin/sh -c '
+ locale=$1
+ cp debian/mozconfig_LANG ${PWD}/mozconfig_$locale
+ sed -i "s|obj_LANG|obj_$locale|" ${PWD}/mozconfig_$locale
+ export MOZCONFIG=${PWD}/mozconfig_$locale
+ ./mach build config/nsinstall langpack-$locale
+ cp -L ../obj_$locale/dist/linux-*/xpi/waterfox-g-$WF_VERSION.$locale.langpack.xpi \
+ "$(pwd)"/extensions/langpack-$locale@l10n.waterfox.net.xpi
+' -- {}
diff --git a/waterfox-g/debian/changelog b/waterfox-g/debian/changelog
index 192982b..d85c707 100644
--- a/waterfox-g/debian/changelog
+++ b/waterfox-g/debian/changelog
@@ -1,8 +1,43 @@
-waterfox-g (5.1.10-1+stackrpms) obs; urgency=medium
+waterfox-g (6.0.2-1+stackrpms) obs; urgency=medium
* Fork to remove KDE bits
- -- B. Stack <bgstack15@gmail.com> Sun, 06 Aug 2023 11:41:53 -0400
+ -- B. Stack <bgstack15@gmail.com> Mon, 09 Oct 2023 12:43:32 -0400
+
+waterfox-g-kpe (6.0.2-0) obs; urgency=medium
+
+ * Critical security fix
+
+ -- hawkeye116477 <hawkeye116477@gmail.com> Fri, 29 Sep 2023 17:43:11 +0200
+
+waterfox-g-kpe (6.0.1-0) obs; urgency=medium
+
+ * Update privacy preferences.
+ * Prevent new searches and opening bookmarks from opening in a new tab by default.
+
+ -- hawkeye116477 <hawkeye116477@gmail.com> Tue, 26 Sep 2023 22:38:10 +0200
+
+waterfox-g-kpe (6.0-0) obs; urgency=medium
+
+ * Waterfox uses DNS over Oblivious HTTP1, a privacy preserving method for DNS queries.
+ * You can now move and remove the extension button from the navigation bar.
+ * Linux, alongside Windows, now supports DRM. That means you can watch your favourite shows on the streaming platform of your choice.
+ * Waterfox preferences have been gone through with a fine tooth comb, with a lot of assistance from yokoffing, who maintains Betterfox.js.
+
+ -- hawkeye116477 <hawkeye116477@gmail.com> Wed, 20 Sep 2023 17:47:57 +0200
+
+waterfox-g-kpe (5.1.13-0) obs; urgency=medium
+
+ * High severity security fix
+
+ -- hawkeye116477 <hawkeye116477@gmail.com> Sat, 16 Sep 2023 23:48:21 +0200
+
+waterfox-g-kpe (5.1.12-0) obs; urgency=medium
+
+ * Various security fixes
+
+ -- hawkeye116477 <hawkeye116477@gmail.com> Sat, 02 Sep 2023 14:07:45 +0200
+
waterfox-g-kpe (5.1.10-0) obs; urgency=medium
diff --git a/waterfox-g/debian/control b/waterfox-g/debian/control
index 819860e..15d7853 100644
--- a/waterfox-g/debian/control
+++ b/waterfox-g/debian/control
@@ -3,11 +3,7 @@ Section: web
Priority: optional
Maintainer: B. Stack <bgstack15@gmail.com>
XSBC-Original-Maintainer: hawkeye116477 <hawkeye116477@gmail.com>
-Build-Depends: debhelper (>= 9), autoconf2.13, libgtk-3-dev (>= 3.14), libdbus-glib-1-dev, libpulse-dev, libasound2-dev, yasm (>= 1.1), build-essential, libxt-dev, python3 (>= 3.5), zip, unzip, cargo (>= 0.59), libgl1-mesa-dev, libnotify-dev (>= 0.4), binutils-avr, libfreetype6-dev (>= 2.0.1), libfontconfig1-dev, pkg-config, libtinfo-dev | libncurses-dev, clang (>= 5.0) | clang-10 | clang-11 | clang-12 | clang-13, llvm-dev (>= 5.0) | llvm-10-dev | llvm-11-dev | llvm-12-dev | llvm-13-dev, lld (>= 5.0) | lld-10 | lld-11 | lld-12 | lld-13, rustc (>= 1.59.0~), libxext-dev, libglib2.0-dev (>= 2.18), libpango1.0-dev (>= 1.14.0), libstartup-notification0-dev, libcurl4-openssl-dev, libiw-dev, mesa-common-dev, libxrender-dev, dbus-x11, xvfb, libx11-dev, libx11-xcb-dev, libfile-fcntllock-perl, apt-utils, locales, autotools-dev, libjpeg-dev, zlib1g-dev, libreadline-dev, dpkg-dev (>= 1.16.1.1~), libevent-dev (>= 1.4.1), libjsoncpp-dev, xfonts-base, xauth, lsb-release, cbindgen (>= 0.24.2), nodejs (>= 10.24.1) | nodejs-mozilla (>= 10.24.1), libjack-dev, nasm (>= 2.14) | nasm-mozilla (>= 2.14), libclang-dev (>= 5.0) | libclang-10-dev | libclang-11-dev | libclang-12-dev | libclang-13-dev, libstdc++6 (>= 7.0) | gcc-mozilla (>= 7), bc
-Build-Conflicts: graphicsmagick-imagemagick-compat,
- liboss4-salsa-dev,
- libhildonmime-dev,
- libosso-dev
+Build-Depends: debhelper (>= 9), libgtk-3-dev, libdbus-glib-1-dev, libpulse-dev, libasound2-dev, yasm, build-essential, libxt-dev, python3 (>= 3.7) | python3.7, zip, unzip, cargo (>= 0.66), libgl1-mesa-dev, binutils-avr, clang (>= 5.0) | clang-13 | clang-14 | clang-15, llvm-dev (>= 5.0) | llvm-13-dev | llvm-14-dev | llvm-15-dev, lld (>= 5.0) | lld-13 | lld-14 | lld-15, rustc (>= 1.66.0~), libxext-dev, libglib2.0-dev, libstartup-notification0-dev, libcurl4-openssl-dev, libiw-dev, mesa-common-dev, libxrender-dev, dbus-x11, xvfb, libx11-dev, libx11-xcb-dev, apt-utils, locales, autotools-dev, libjpeg-dev, zlib1g-dev, libreadline-dev, dpkg-dev, libevent-dev, libjsoncpp-dev, xfonts-base, xauth, lsb-release, cbindgen (>= 0.24.3~), nodejs (>= 12.22.1) | nodejs-mozilla (>= 12.22.1), libjack-dev, nasm (>= 2.14) | nasm-mozilla (>= 2.14), libclang-dev (>= 5.0) | libclang-13-dev | libclang-14-dev | libclang-15-dev, libstdc++6 (>= 7.0) | gcc-mozilla (>= 7), bc, libpci-dev
Standards-Version: 3.9.7
Homepage: https://www.waterfox.net/
diff --git a/waterfox-g/debian/mozconfig b/waterfox-g/debian/mozconfig
index da1d8d4..850281e 100644
--- a/waterfox-g/debian/mozconfig
+++ b/waterfox-g/debian/mozconfig
@@ -1,44 +1,19 @@
-if test `lsb_release -sc` = "bionic" || test `lsb_release -sc` = "focal" || test `lsb_release -sc` = "stretch" || test `lsb_release -sc` = "buster"; then
-export NODEJS=/usr/lib/nodejs-mozilla/bin/node
-fi
-
-if test `lsb_release -sc` = "bionic" || test `lsb_release -sc` = "stretch"; then
-export NASM=/usr/lib/nasm-mozilla/bin/nasm
-fi
-
-# For successfull LTO build, we need to use matching LLVM version
-if test `lsb_release -sc` = "bionic" || test `lsb_release -sc` = "focal" || test `lsb_release -sc` = "impish" || test `lsb_release -sc` = "stretch" || test `lsb_release -sc` = "buster" || test `lsb_release -sc` = "bullseye"; then
-export PATH=/usr/lib/llvm-12/bin/:$PATH
-fi
-
-if test `lsb_release -sc` = "kinetic"; then
-export PATH=/usr/lib/llvm-14/bin/:$PATH
-fi
-
-export CC=clang
-export CXX=clang++
-export AR=llvm-ar
-export NM=llvm-nm
-export RANLIB=llvm-ranlib
-export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
-
ac_add_options --prefix=/usr
-export MOZ_PGO=1
-
mk_add_options AUTOCLOBBER=1
ac_add_options --disable-debug
ac_add_options --disable-debug-symbols
ac_add_options --disable-crashreporter
ac_add_options --disable-profiling
-ac_add_options --disable-verify-mar
ac_add_options --disable-dmd
ac_add_options --disable-geckodriver
ac_add_options --disable-bootstrap
ac_add_options --disable-updater
ac_add_options --disable-elf-hack
+ac_add_options --disable-tests
+ac_add_options --enable-unverified-updates
ac_add_options --enable-pulseaudio
ac_add_options --enable-alsa
ac_add_options --enable-jack
@@ -48,9 +23,16 @@ ac_add_options --enable-default-toolkit=cairo-gtk3-wayland
ac_add_options --enable-hardening
ac_add_options --enable-optimize
ac_add_options --enable-rust-simd
-ac_add_options --enable-lto
ac_add_options --enable-linker=lld
ac_add_options --enable-jxl
+if test "$GEN_PGO"; then
+ ac_add_options --enable-profile-generate=cross
+elif test "$USE_PGO"; then
+ ac_add_options --enable-lto=cross
+ ac_add_options --enable-profile-use=cross
+ ac_add_options --with-pgo-profile-path="$PWD"/merged.profdata
+ ac_add_options --with-pgo-jarlog="$PWD"/jarlog
+fi
ac_add_options --with-app-name=waterfox-g
ac_add_options --with-app-basename=Waterfox
@@ -58,12 +40,14 @@ ac_add_options --with-branding=waterfox/browser/branding
ac_add_options --with-unsigned-addon-scopes=app,system
ac_add_options --without-wasm-sandboxed-libraries
ac_add_options --allow-addon-sideload
-ac_add_options --with-version-file-path=$topsrcdir/debian/app_version
+ac_add_options --with-version-file-path="$topsrcdir"/debian/app_version
export MOZ_REQUIRE_SIGNING=
export MOZ_INCLUDE_SOURCE_INFO=1
export MOZ_APP_REMOTINGNAME=waterfox-g
+# stackrpms,2
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
X=$(($(nproc --all)/2))
mk_add_options MOZ_MAKE_FLAGS="-j${X%.*}"
+mk_add_options MOZ_OBJDIR="${PWD}"/obj
diff --git a/waterfox-g/debian/mozconfig_LANG b/waterfox-g/debian/mozconfig_LANG
index 1af22f3..f10957e 100644
--- a/waterfox-g/debian/mozconfig_LANG
+++ b/waterfox-g/debian/mozconfig_LANG
@@ -1,22 +1,3 @@
-if test `lsb_release -sc` = "bionic" || test `lsb_release -sc` = "focal" || test `lsb_release -sc` = "stretch" || test `lsb_release -sc` = "buster"; then
-export NODEJS=/usr/lib/nodejs-mozilla/bin/node
-fi
-
-if test `lsb_release -sc` = "bionic" || test `lsb_release -sc` = "stretch"; then
-export NASM=/usr/lib/nasm-mozilla/bin/nasm
-fi
-
-# For successfull LTO build, we need to use matching LLVM version
-if test `lsb_release -sc` = "bionic" || test `lsb_release -sc` = "focal" || test `lsb_release -sc` = "impish" || test `lsb_release -sc` = "stretch" || test `lsb_release -sc` = "buster" || test `lsb_release -sc` = "bullseye"; then
-export PATH=/usr/lib/llvm-12/bin/:$PATH
-fi
-
-if test `lsb_release -sc` = "kinetic"; then
-export PATH=/usr/lib/llvm-14/bin/:$PATH
-fi
-
-export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
-
ac_add_options --enable-linker=lld
ac_add_options --with-app-name=waterfox-g
diff --git a/waterfox-g/debian/patches/debian-hacks/Relax-cargo-version-requirement.patch b/waterfox-g/debian/patches/debian-hacks/Relax-cargo-version-requirement.patch
new file mode 100644
index 0000000..313f1d4
--- /dev/null
+++ b/waterfox-g/debian/patches/debian-hacks/Relax-cargo-version-requirement.patch
@@ -0,0 +1,21 @@
+From: Mike Hommey <mh@glandium.org>
+Date: Wed, 2 Aug 2023 08:46:33 +0900
+Subject: Relax cargo version requirement
+
+---
+ build/moz.configure/rust.configure | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/build/moz.configure/rust.configure b/build/moz.configure/rust.configure
+index 7a2fd1a..9801f82 100644
+--- a/build/moz.configure/rust.configure
++++ b/build/moz.configure/rust.configure
+@@ -180,7 +180,7 @@ def rust_compiler(rustc_info, cargo_info, target):
+ )
+ )
+ rustc_min_version = Version(MINIMUM_RUST_VERSION)
+- cargo_min_version = rustc_min_version
++ cargo_min_version = Version("1.65.0")
+
+ version = rustc_info.version
+ is_nightly = "nightly" in version.version
diff --git a/waterfox-g/debian/patches/debian-hacks/Relax-nodejs-minimum-version.patch b/waterfox-g/debian/patches/debian-hacks/Relax-nodejs-minimum-version.patch
new file mode 100644
index 0000000..8839a39
--- /dev/null
+++ b/waterfox-g/debian/patches/debian-hacks/Relax-nodejs-minimum-version.patch
@@ -0,0 +1,21 @@
+From: Mike Hommey <mh@glandium.org>
+Date: Wed, 21 Sep 2022 08:48:27 +0900
+Subject: Relax nodejs minimum version
+
+---
+ python/mozbuild/mozbuild/nodeutil.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/python/mozbuild/mozbuild/nodeutil.py b/python/mozbuild/mozbuild/nodeutil.py
+index 8ec724a..efc7b7f 100644
+--- a/python/mozbuild/mozbuild/nodeutil.py
++++ b/python/mozbuild/mozbuild/nodeutil.py
+@@ -11,7 +11,7 @@ from mozboot.util import get_tools_dir
+ from mozfile import which
+ from six import PY3
+
+-NODE_MIN_VERSION = StrictVersion("12.22.12")
++NODE_MIN_VERSION = StrictVersion("12.22")
+ NPM_MIN_VERSION = StrictVersion("6.14.16")
+
+
diff --git a/waterfox-g/debian/patches/fis-csd-global-menu.patch b/waterfox-g/debian/patches/fis-csd-global-menu.patch
index 6eca9e8..6f2d2dc 100644
--- a/waterfox-g/debian/patches/fis-csd-global-menu.patch
+++ b/waterfox-g/debian/patches/fis-csd-global-menu.patch
@@ -1,8 +1,8 @@
diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css
-index 03a778809dc8..8f0267b45328 100644
+index 4279c9ccfc83..8573feb7cdfb 100644
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
-@@ -334,6 +334,13 @@ toolbar[customizing] #whats-new-menu-button {
+@@ -359,6 +359,13 @@ toolbar[customizing] #whats-new-menu-button {
visibility: hidden;
}
@@ -16,6 +16,3 @@ index 03a778809dc8..8f0267b45328 100644
:root[tabsintitlebar] .titlebar-buttonbox {
position: relative;
}
---
-2.37.3
-
diff --git a/waterfox-g/debian/patches/fix-langpack-id.patch b/waterfox-g/debian/patches/fix-langpack-id.patch
index c409fd8..4159530 100644
--- a/waterfox-g/debian/patches/fix-langpack-id.patch
+++ b/waterfox-g/debian/patches/fix-langpack-id.patch
@@ -1,8 +1,8 @@
diff --git a/browser/components/preferences/tests/browser_browser_languages_subdialog.js b/browser/components/preferences/tests/browser_browser_languages_subdialog.js
-index a204f697f904..4b87ab85156a 100644
+index 8b57bf08a83b..dd6f22094a93 100644
--- a/browser/components/preferences/tests/browser_browser_languages_subdialog.js
+++ b/browser/components/preferences/tests/browser_browser_languages_subdialog.js
-@@ -14,7 +14,7 @@ const DICTIONARY_ID_PL = "pl@dictionaries.addons.mozilla.org";
+@@ -13,7 +13,7 @@ const DICTIONARY_ID_PL = "pl@dictionaries.addons.mozilla.org";
const TELEMETRY_CATEGORY = "intl.ui.browserLanguage";
function langpackId(locale) {
@@ -11,7 +11,7 @@ index a204f697f904..4b87ab85156a 100644
}
function getManifestData(locale, version = "2.0") {
-@@ -670,7 +670,7 @@ add_task(async function testInstallFromAMO() {
+@@ -669,7 +669,7 @@ add_task(async function testInstallFromAMO() {
is(getMainPaneLocales(), "en-US,pl,search", "en-US and pl now available");
// Disable the Polish langpack.
@@ -21,7 +21,7 @@ index a204f697f904..4b87ab85156a 100644
({ dialogDoc, available, selected } = await openDialog(doc, true));
diff --git a/browser/locales/Makefile.in b/browser/locales/Makefile.in
-index e4b60a039956..208871cb6f75 100644
+index 1b8f21be1ca6..7ca5d391219a 100644
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -21,9 +21,9 @@ PWD := $(CURDIR)
@@ -36,32 +36,29 @@ index e4b60a039956..208871cb6f75 100644
endif
# For Nightly, we know where to get the builds from to do local repacks
ifdef NIGHTLY_BUILD
-diff --git a/intl/locale/LangPackMatcher.jsm b/intl/locale/LangPackMatcher.jsm
-index 0913ef9fda38..2b923b59df6d 100644
---- a/intl/locale/LangPackMatcher.jsm
-+++ b/intl/locale/LangPackMatcher.jsm
-@@ -370,7 +370,7 @@ async function getAvailableLocales() {
+diff --git a/intl/locale/LangPackMatcher.sys.mjs b/intl/locale/LangPackMatcher.sys.mjs
+index 977398b082e8..fd7c72939369 100644
+--- a/intl/locale/LangPackMatcher.sys.mjs
++++ b/intl/locale/LangPackMatcher.sys.mjs
+@@ -374,7 +374,7 @@ async function getAvailableLocales() {
// If defaultLocale isn't lastFallbackLocale, then we still need the langpack
// for lastFallbackLocale for it to be useful.
if (defaultLocale != lastFallbackLocale) {
- let lastFallbackId = `langpack-${lastFallbackLocale}@firefox.mozilla.org`;
+ let lastFallbackId = `langpack-${lastFallbackLocale}@l10n.waterfox.net`;
- let lastFallbackInstalled = await AddonManager.getAddonByID(lastFallbackId);
- if (!lastFallbackInstalled) {
- return availableLocales.filter(locale => locale != lastFallbackLocale);
-diff --git a/intl/locale/tests/LangPackMatcherTestUtils.jsm b/intl/locale/tests/LangPackMatcherTestUtils.jsm
-index 38345e1ec2d6..9898c733747c 100644
---- a/intl/locale/tests/LangPackMatcherTestUtils.jsm
-+++ b/intl/locale/tests/LangPackMatcherTestUtils.jsm
-@@ -43,7 +43,7 @@ function getAddonAndLocalAPIsMocker(testScope, sandbox) {
+ let lastFallbackInstalled = await lazy.AddonManager.getAddonByID(
+ lastFallbackId
+ );
+diff --git a/intl/locale/tests/LangPackMatcherTestUtils.sys.mjs b/intl/locale/tests/LangPackMatcherTestUtils.sys.mjs
+index 4b18f1be134e..1e1008b55edd 100644
+--- a/intl/locale/tests/LangPackMatcherTestUtils.sys.mjs
++++ b/intl/locale/tests/LangPackMatcherTestUtils.sys.mjs
+@@ -38,7 +38,7 @@ export function getAddonAndLocalAPIsMocker(testScope, sandbox) {
);
resolve(
availableLangpacks.map(locale => ({
- guid: `langpack-${locale}@firefox.mozilla.org`,
+ guid: `langpack-${locale}@l10n.waterfox.net`,
type: "language",
+ hash: locale,
target_locale: locale,
- current_compatible_version: {
---
-2.37.3
-
diff --git a/waterfox-g/debian/patches/fix-wayland-build.patch b/waterfox-g/debian/patches/fix-wayland-build.patch
index 703b1a8..f0309fa 100644
--- a/waterfox-g/debian/patches/fix-wayland-build.patch
+++ b/waterfox-g/debian/patches/fix-wayland-build.patch
@@ -6,9 +6,11 @@ Description: Fix FTBFS on bionic. Compiler errors:
Author: Rico Tzschichholz <ricotz@ubuntu.com>
+diff --git a/widget/gtk/WaylandBuffer.cpp b/widget/gtk/WaylandBuffer.cpp
+index f3fc4093623f..78d213d5d302 100644
--- a/widget/gtk/WaylandBuffer.cpp
+++ b/widget/gtk/WaylandBuffer.cpp
-@@ -258,7 +258,7 @@
+@@ -197,7 +197,7 @@ void WaylandBufferSHM::DumpToFile(const char* aHint) {
/* static */
RefPtr<WaylandBufferDMABUF> WaylandBufferDMABUF::Create(
diff --git a/waterfox-g/debian/patches/g-kde.patch b/waterfox-g/debian/patches/g-kde.patch
index 2bff3cc..6fb67eb 100644
--- a/waterfox-g/debian/patches/g-kde.patch
+++ b/waterfox-g/debian/patches/g-kde.patch
@@ -2,13 +2,13 @@ Merged few patches into one patch and fixed for Waterfox
Original authors of patch for Firefox:
Wolfgang Rosenauer <wolfgang@rosenauer.org>
Lubos Lunak <lunak@suse.com>
-Original patches => http://www.rosenauer.org/hg/mozilla/file/firefox91
+Original patches => http://www.rosenauer.org/hg/mozilla/file/firefox115
diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js
-index 627e63440af2..6d300a8124fa 100644
+index 95ed194f4bdd..90460d8bab88 100644
--- a/browser/components/preferences/main.js
+++ b/browser/components/preferences/main.js
-@@ -316,6 +316,13 @@ var gMainPane = {
+@@ -300,6 +300,13 @@ var gMainPane = {
}, backoffTimes[this._backoffIndex]);
}
@@ -22,7 +22,7 @@ index 627e63440af2..6d300a8124fa 100644
this.initBrowserContainers();
this.buildContentProcessCountMenuList();
-@@ -1349,6 +1356,17 @@ var gMainPane = {
+@@ -1771,6 +1778,17 @@ var gMainPane = {
}
try {
shellSvc.setDefaultBrowser(true, false);
@@ -38,10 +38,10 @@ index 627e63440af2..6d300a8124fa 100644
+ process.run(false, args, args.length);
+ }
} catch (ex) {
- Cu.reportError(ex);
+ console.error(ex);
return;
diff --git a/browser/components/shell/moz.build b/browser/components/shell/moz.build
-index eedbb0d938fe..9b364941b850 100644
+index eb88cb287dc3..95277533781a 100644
--- a/browser/components/shell/moz.build
+++ b/browser/components/shell/moz.build
@@ -36,6 +36,8 @@ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
@@ -256,10 +256,10 @@ index 000000000000..26b5dbac47dd
+
+#endif // nsunixshellservice_h____
diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp
-index 6d0bebd2ee68..c138e63b23dc 100644
+index c04744a8b936..c2f1cd5a1e1a 100644
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
-@@ -93,6 +93,7 @@
+@@ -95,6 +95,7 @@
#ifdef MOZ_BACKGROUNDTASKS
# include "mozilla/BackgroundTasks.h"
#endif
@@ -267,16 +267,15 @@ index 6d0bebd2ee68..c138e63b23dc 100644
#ifdef DEBUG
# include <map>
-@@ -4772,6 +4773,17 @@ nsresult Preferences::InitInitialObjects(bool aIsStartup) {
+@@ -4911,6 +4912,16 @@ nsresult Preferences::InitInitialObjects(bool aIsStartup) {
#endif
};
-+ if(nsKDEUtils::kdeSession()) { // TODO what if some setup actually requires the helper?
-+ for(int i = 0;
-+ i < MOZ_ARRAY_LENGTH(specialFiles);
-+ ++i ) {
-+ if( *specialFiles[ i ] == '\0' ) {
-+ specialFiles[ i ] = "kde.js";
++ if (nsKDEUtils::kdeSession()) { // TODO what if some setup actually requires
++ // the helper?
++ for (int i = 0; i < MOZ_ARRAY_LENGTH(specialFiles); ++i) {
++ if (*specialFiles[i] == '\0') {
++ specialFiles[i] = "kde.js";
+ break;
+ }
+ }
@@ -285,7 +284,7 @@ index 6d0bebd2ee68..c138e63b23dc 100644
rv = pref_LoadPrefsInDir(defaultPrefDir, specialFiles,
ArrayLength(specialFiles));
if (NS_FAILED(rv)) {
-@@ -4846,7 +4858,7 @@ nsresult Preferences::InitInitialObjects(bool aIsStartup) {
+@@ -4985,7 +4996,7 @@ nsresult Preferences::InitInitialObjects(bool aIsStartup) {
}
// Do we care if a file provided by this process fails to load?
@@ -295,10 +294,10 @@ index 6d0bebd2ee68..c138e63b23dc 100644
}
diff --git a/modules/libpref/moz.build b/modules/libpref/moz.build
-index 1f021d409c51..171d034cc03a 100644
+index e8f8b97170d3..831001cee4b1 100644
--- a/modules/libpref/moz.build
+++ b/modules/libpref/moz.build
-@@ -125,6 +125,10 @@ UNIFIED_SOURCES += [
+@@ -126,6 +126,10 @@ UNIFIED_SOURCES += [
"SharedPrefMap.cpp",
]
@@ -310,7 +309,7 @@ index 1f021d409c51..171d034cc03a 100644
GeneratedFile(
diff --git a/python/mozbuild/mozpack/chrome/flags.py b/python/mozbuild/mozpack/chrome/flags.py
-index 0fe6ee99bd51..f7acdafe9790 100644
+index 6b096c862aaa..2b46d9294b93 100644
--- a/python/mozbuild/mozpack/chrome/flags.py
+++ b/python/mozbuild/mozpack/chrome/flags.py
@@ -234,6 +234,7 @@ class Flags(OrderedDict):
@@ -322,10 +321,10 @@ index 0fe6ee99bd51..f7acdafe9790 100644
RE = re.compile(r"([!<>=]+)")
diff --git a/python/mozbuild/mozpack/chrome/manifest.py b/python/mozbuild/mozpack/chrome/manifest.py
-index a733685f95a3..f64b17fb7b17 100644
+index 14c11d4c1daa..41b9969e7277 100644
--- a/python/mozbuild/mozpack/chrome/manifest.py
+++ b/python/mozbuild/mozpack/chrome/manifest.py
-@@ -44,6 +44,7 @@ class ManifestEntry(object):
+@@ -43,6 +43,7 @@ class ManifestEntry(object):
"process",
"contentaccessible",
"backgroundtask",
@@ -334,7 +333,7 @@ index a733685f95a3..f64b17fb7b17 100644
def __init__(self, base, *flags):
diff --git a/toolkit/components/downloads/moz.build b/toolkit/components/downloads/moz.build
-index d4172e2d73ad..8bd0577bc535 100644
+index 3818e8c0db1e..b70986db8111 100644
--- a/toolkit/components/downloads/moz.build
+++ b/toolkit/components/downloads/moz.build
@@ -51,5 +51,9 @@ if CONFIG["MOZ_PLACES"]:
@@ -347,11 +346,11 @@ index d4172e2d73ad..8bd0577bc535 100644
+
with Files("**"):
BUG_COMPONENT = ("Toolkit", "Downloads API")
-diff --git a/toolkit/mozapps/downloads/HelperAppDlg.jsm b/toolkit/mozapps/downloads/HelperAppDlg.jsm
-index 0a5a4a460bf3..5381240f52c6 100644
---- a/toolkit/mozapps/downloads/HelperAppDlg.jsm
-+++ b/toolkit/mozapps/downloads/HelperAppDlg.jsm
-@@ -1260,26 +1260,56 @@ nsUnknownContentTypeDialog.prototype = {
+diff --git a/toolkit/mozapps/downloads/HelperAppDlg.sys.mjs b/toolkit/mozapps/downloads/HelperAppDlg.sys.mjs
+index af6993416e1a..39f1f7b6dfec 100644
+--- a/toolkit/mozapps/downloads/HelperAppDlg.sys.mjs
++++ b/toolkit/mozapps/downloads/HelperAppDlg.sys.mjs
+@@ -1246,26 +1246,56 @@ nsUnknownContentTypeDialog.prototype = {
this.chosenApp = params.handlerApp;
}
} else if ("@mozilla.org/applicationchooser;1" in Cc) {
@@ -429,19 +428,19 @@ index 0a5a4a460bf3..5381240f52c6 100644
var nsIFilePicker = Ci.nsIFilePicker;
var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
diff --git a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
-index ef110b1287bf..e29e8a5b62de 100644
+index 982faf7d7968..d5417c058050 100644
--- a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
+++ b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
-@@ -15,6 +15,8 @@
- #include "nsNetUtil.h"
+@@ -16,6 +16,8 @@
#include "nsISupportsPrimitives.h"
#include "nsIGSettingsService.h"
+ #include "nsReadableUtils.h"
+#include "nsPrintfCString.h"
+#include "nsKDEUtils.h"
using namespace mozilla;
-@@ -38,6 +40,8 @@ class nsUnixSystemProxySettings final : public nsISystemProxySettings {
+@@ -39,6 +41,8 @@ class nsUnixSystemProxySettings final : public nsISystemProxySettings {
nsACString& aResult);
nsresult SetProxyResultFromGSettings(const char* aKeyBase, const char* aType,
nsACString& aResult);
@@ -450,7 +449,7 @@ index ef110b1287bf..e29e8a5b62de 100644
};
NS_IMPL_ISUPPORTS(nsUnixSystemProxySettings, nsISystemProxySettings)
-@@ -379,6 +383,9 @@ nsresult nsUnixSystemProxySettings::GetProxyForURI(const nsACString& aSpec,
+@@ -393,6 +397,9 @@ nsresult nsUnixSystemProxySettings::GetProxyForURI(const nsACString& aSpec,
const nsACString& aHost,
const int32_t aPort,
nsACString& aResult) {
@@ -460,44 +459,40 @@ index ef110b1287bf..e29e8a5b62de 100644
if (mProxySettings) {
nsresult rv = GetProxyFromGSettings(aScheme, aHost, aPort, aResult);
if (NS_SUCCEEDED(rv)) return rv;
-@@ -387,6 +394,32 @@ nsresult nsUnixSystemProxySettings::GetProxyForURI(const nsACString& aSpec,
+@@ -401,6 +408,28 @@ nsresult nsUnixSystemProxySettings::GetProxyForURI(const nsACString& aSpec,
return GetProxyFromEnvironment(aScheme, aHost, aPort, aResult);
}
-+nsresult
-+nsUnixSystemProxySettings::GetProxyFromKDE(const nsACString& aScheme,
-+ const nsACString& aHost,
-+ PRInt32 aPort,
-+ nsACString& aResult)
-+{
++nsresult nsUnixSystemProxySettings::GetProxyFromKDE(const nsACString& aScheme,
++ const nsACString& aHost,
++ PRInt32 aPort,
++ nsACString& aResult) {
+ nsAutoCString url;
+ url = aScheme;
+ url += "://";
+ url += aHost;
-+ if( aPort >= 0 )
-+ {
++ if (aPort >= 0) {
+ url += ":";
+ url += nsPrintfCString("%d", aPort);
+ }
+ nsTArray<nsCString> command;
-+ command.AppendElement( "GETPROXY"_ns );
-+ command.AppendElement( url );
++ command.AppendElement("GETPROXY"_ns);
++ command.AppendElement(url);
+ nsTArray<nsCString> result;
-+ if( !nsKDEUtils::command( command, &result ) || result.Length() != 1 )
++ if (!nsKDEUtils::command(command, &result) || result.Length() != 1)
+ return NS_ERROR_FAILURE;
+ aResult = result[0];
+ return NS_OK;
+}
+
-+
NS_IMPL_COMPONENT_FACTORY(nsUnixSystemProxySettings) {
auto result = MakeRefPtr<nsUnixSystemProxySettings>();
result->Init();
diff --git a/toolkit/xre/moz.build b/toolkit/xre/moz.build
-index 6475c0296aac..83e0184d4938 100644
+index dc7695e7acc5..3d7656515967 100644
--- a/toolkit/xre/moz.build
+++ b/toolkit/xre/moz.build
-@@ -97,7 +97,9 @@ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "uikit":
+@@ -96,7 +96,9 @@ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "uikit":
"UIKitDirProvider.mm",
]
elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
@@ -509,10 +504,10 @@ index 6475c0296aac..83e0184d4938 100644
CXXFLAGS += CONFIG["MOZ_X11_SM_CFLAGS"]
diff --git a/toolkit/xre/nsKDEUtils.cpp b/toolkit/xre/nsKDEUtils.cpp
new file mode 100644
-index 000000000000..a5242b6c6699
+index 000000000000..693280deefff
--- /dev/null
+++ b/toolkit/xre/nsKDEUtils.cpp
-@@ -0,0 +1,321 @@
+@@ -0,0 +1,286 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -536,310 +531,275 @@ index 000000000000..a5242b6c6699
+// copied from X11/X.h as a hack since for an unknown
+// reason it's not picked up from X11/X.h
+#ifndef None
-+#define None 0L /* universal null resource or null atom */
++# define None 0L /* universal null resource or null atom */
+#endif
+
-+//#define DEBUG_KDE
++// #define DEBUG_KDE
+#ifdef DEBUG_KDE
-+#define KWATERFOXHELPER "kwaterfoxhelper"
++# define KWATERFOXHELPER "kwaterfoxhelper"
+#else
+// not need for lib64, it's a binary
-+#define KWATERFOXHELPER "/usr/lib/waterfox/kwaterfoxhelper"
++# define KWATERFOXHELPER "/usr/lib/waterfox/kwaterfoxhelper"
+#endif
+
+#define KWATERFOXHELPER_VERSION 6
-+#define MAKE_STR2( n ) #n
-+#define MAKE_STR( n ) MAKE_STR2( n )
++#define MAKE_STR2(n) #n
++#define MAKE_STR(n) MAKE_STR2(n)
+
-+static bool getKdeSession()
-+{
-+ if (PR_GetEnv("KDE_FULL_SESSION"))
-+ {
-+ return true;
-+ }
-+ return false;
++static bool getKdeSession() {
++ if (PR_GetEnv("KDE_FULL_SESSION")) {
++ return true;
++ }
++ return false;
+}
+
-+static bool getKdeSupport()
-+ {
-+ nsTArray<nsCString> command;
-+ command.AppendElement( "CHECK"_ns );
-+ command.AppendElement( "KWATERFOXHELPER_VERSION"_ns );
-+ bool kde = nsKDEUtils::command( command );
++static bool getKdeSupport() {
++ nsTArray<nsCString> command;
++ command.AppendElement("CHECK"_ns);
++ command.AppendElement("KWATERFOXHELPER_VERSION"_ns);
++ bool kde = nsKDEUtils::command(command);
+#ifdef DEBUG_KDE
-+ fprintf( stderr, "KDE RUNNING %d\n", kde );
++ fprintf(stderr, "KDE RUNNING %d\n", kde);
+#endif
-+ return kde;
-+ }
++ return kde;
++}
+
-+nsKDEUtils::nsKDEUtils()
-+ : commandFile( NULL )
-+ , replyFile( NULL )
-+ {
-+ }
++nsKDEUtils::nsKDEUtils() : commandFile(NULL), replyFile(NULL) {}
+
-+nsKDEUtils::~nsKDEUtils()
-+ {
-+// closeHelper(); not actually useful, exiting will close the fd too
-+ }
++nsKDEUtils::~nsKDEUtils() {
++ // closeHelper(); not actually useful, exiting will close the fd too
++}
+
-+nsKDEUtils* nsKDEUtils::self()
-+ {
-+ static nsKDEUtils s;
-+ return &s;
-+ }
++nsKDEUtils* nsKDEUtils::self() {
++ static nsKDEUtils s;
++ return &s;
++}
+
+static bool helperRunning = false;
+static bool helperFailed = false;
+
-+bool nsKDEUtils::kdeSession()
-+ {
-+ static bool session = getKdeSession();
-+ return session;
-+ }
++bool nsKDEUtils::kdeSession() {
++ static bool session = getKdeSession();
++ return session;
++}
+
-+bool nsKDEUtils::kdeSupport()
-+ {
-+ static bool support = kdeSession() && getKdeSupport();
-+ return support && helperRunning;
-+ }
++bool nsKDEUtils::kdeSupport() {
++ static bool support = kdeSession() && getKdeSupport();
++ return support && helperRunning;
++}
+
-+struct nsKDECommandData
-+ {
-+ FILE* file;
-+ nsTArray<nsCString>* output;
-+ GMainLoop* loop;
-+ bool success;
-+ };
++struct nsKDECommandData {
++ FILE* file;
++ nsTArray<nsCString>* output;
++ GMainLoop* loop;
++ bool success;
++};
+
-+static gboolean kdeReadFunc( GIOChannel*, GIOCondition, gpointer data )
-+ {
-+ nsKDECommandData* p = static_cast< nsKDECommandData* >( data );
-+ char buf[ 8192 ]; // TODO big enough
-+ bool command_done = false;
-+ bool command_failed = false;
-+ while( !command_done && !command_failed && fgets( buf, 8192, p->file ) != NULL )
-+ { // TODO what if the kernel splits a line into two chunks?
-+//#ifdef DEBUG_KDE
-+// fprintf( stderr, "READ: %s %d\n", buf, feof( p->file ));
-+//#endif
-+ if( char* eol = strchr( buf, '\n' ))
-+ *eol = '\0';
-+ command_done = ( strcmp( buf, "\\1" ) == 0 );
-+ command_failed = ( strcmp( buf, "\\0" ) == 0 );
-+ nsAutoCString line( buf );
-+ line.ReplaceSubstring( "\\n", "\n" );
-+ line.ReplaceSubstring( "\\" "\\", "\\" ); // \\ -> \ , i.e. unescape
-+ if( p->output && !( command_done || command_failed ))
-+ p->output->AppendElement( nsCString( buf )); // TODO utf8?
-+ }
-+ bool quit = false;
-+ if( feof( p->file ) || command_failed )
-+ {
-+ quit = true;
-+ p->success = false;
-+ }
-+ if( command_done )
-+ { // reading one reply finished
-+ quit = true;
-+ p->success = true;
-+ }
-+ if( quit )
-+ {
-+ if( p->loop )
-+ g_main_loop_quit( p->loop );
-+ return FALSE;
-+ }
-+ return TRUE;
-+ }
++static gboolean kdeReadFunc(GIOChannel*, GIOCondition, gpointer data) {
++ nsKDECommandData* p = static_cast<nsKDECommandData*>(data);
++ char buf[8192]; // TODO big enough
++ bool command_done = false;
++ bool command_failed = false;
++ while (!command_done && !command_failed &&
++ fgets(buf, 8192, p->file) !=
++ NULL) { // TODO what if the kernel splits a line into two chunks?
++ // #ifdef DEBUG_KDE
++ // fprintf( stderr, "READ: %s %d\n", buf, feof( p->file ));
++ // #endif
++ if (char* eol = strchr(buf, '\n')) *eol = '\0';
++ command_done = (strcmp(buf, "\\1") == 0);
++ command_failed = (strcmp(buf, "\\0") == 0);
++ nsAutoCString line(buf);
++ line.ReplaceSubstring("\\n", "\n");
++ line.ReplaceSubstring(
++ "\\"
++ "\\",
++ "\\"); // \\ -> \ , i.e. unescape
++ if (p->output && !(command_done || command_failed))
++ p->output->AppendElement(nsCString(buf)); // TODO utf8?
++ }
++ bool quit = false;
++ if (feof(p->file) || command_failed) {
++ quit = true;
++ p->success = false;
++ }
++ if (command_done) { // reading one reply finished
++ quit = true;
++ p->success = true;
++ }
++ if (quit) {
++ if (p->loop) g_main_loop_quit(p->loop);
++ return FALSE;
++ }
++ return TRUE;
++}
+
-+bool nsKDEUtils::command( const nsTArray<nsCString>& command, nsTArray<nsCString>* output )
-+ {
-+ return self()->internalCommand( command, NULL, false, output );
-+ }
++bool nsKDEUtils::command(const nsTArray<nsCString>& command,
++ nsTArray<nsCString>* output) {
++ return self()->internalCommand(command, NULL, false, output);
++}
+
-+bool nsKDEUtils::command( nsIArray* command, nsIArray** output)
-+ {
-+ nsTArray<nsCString> in;
-+ PRUint32 length;
-+ command->GetLength( &length );
-+ for ( PRUint32 i = 0; i < length; i++ )
-+ {
-+ nsCOMPtr<nsISupportsCString> str = do_QueryElementAt( command, i );
-+ if( str )
-+ {
-+ nsAutoCString s;
-+ str->GetData( s );
-+ in.AppendElement( s );
-+ }
-+ }
++bool nsKDEUtils::command(nsIArray* command, nsIArray** output) {
++ nsTArray<nsCString> in;
++ PRUint32 length;
++ command->GetLength(&length);
++ for (PRUint32 i = 0; i < length; i++) {
++ nsCOMPtr<nsISupportsCString> str = do_QueryElementAt(command, i);
++ if (str) {
++ nsAutoCString s;
++ str->GetData(s);
++ in.AppendElement(s);
++ }
++ }
+
-+ nsTArray<nsCString> out;
-+ bool ret = self()->internalCommand( in, NULL, false, &out );
++ nsTArray<nsCString> out;
++ bool ret = self()->internalCommand(in, NULL, false, &out);
+
-+ if ( !output ) return ret;
++ if (!output) return ret;
+
-+ nsCOMPtr<nsIMutableArray> result = do_CreateInstance( NS_ARRAY_CONTRACTID );
-+ if ( !result ) return false;
++ nsCOMPtr<nsIMutableArray> result = do_CreateInstance(NS_ARRAY_CONTRACTID);
++ if (!result) return false;
+
-+ for ( PRUint32 i = 0; i < out.Length(); i++ )
-+ {
-+ nsCOMPtr<nsISupportsCString> rstr = do_CreateInstance( NS_SUPPORTS_CSTRING_CONTRACTID );
-+ if ( !rstr ) return false;
++ for (PRUint32 i = 0; i < out.Length(); i++) {
++ nsCOMPtr<nsISupportsCString> rstr =
++ do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
++ if (!rstr) return false;
+
-+ rstr->SetData( out[i] );
-+ result->AppendElement( rstr );
-+ }
++ rstr->SetData(out[i]);
++ result->AppendElement(rstr);
++ }
+
-+ NS_ADDREF( *output = result);
-+ return ret;
-+ }
++ NS_ADDREF(*output = result);
++ return ret;
++}
+
++bool nsKDEUtils::commandBlockUi(const nsTArray<nsCString>& command,
++ GtkWindow* parent,
++ nsTArray<nsCString>* output) {
++ return self()->internalCommand(command, parent, true, output);
++}
+
-+bool nsKDEUtils::commandBlockUi( const nsTArray<nsCString>& command, GtkWindow* parent, nsTArray<nsCString>* output )
-+ {
-+ return self()->internalCommand( command, parent, true, output );
-+ }
++bool nsKDEUtils::internalCommand(const nsTArray<nsCString>& command,
++ GtkWindow* parent, bool blockUi,
++ nsTArray<nsCString>* output) {
++ if (!startHelper()) return false;
++ feedCommand(command);
++ // do not store the data in 'this' but in extra structure, just in case there
++ // is reentrancy (can there be? the event loop is re-entered)
++ nsKDECommandData data;
++ data.file = replyFile;
++ data.output = output;
++ data.success = false;
++ if (blockUi) {
++ data.loop = g_main_loop_new(NULL, FALSE);
++ GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
++ if (parent && gtk_window_get_group(parent))
++ gtk_window_group_add_window(gtk_window_get_group(parent),
++ GTK_WINDOW(window));
++ gtk_widget_realize(window);
++ gtk_widget_set_sensitive(window, TRUE);
++ gtk_grab_add(window);
++ GIOChannel* channel = g_io_channel_unix_new(fileno(data.file));
++ g_io_add_watch(channel,
++ static_cast<GIOCondition>(G_IO_IN | G_IO_ERR | G_IO_HUP),
++ kdeReadFunc, &data);
++ g_io_channel_unref(channel);
++ g_main_loop_run(data.loop);
++ g_main_loop_unref(data.loop);
++ gtk_grab_remove(window);
++ gtk_widget_destroy(window);
++ } else {
++ data.loop = NULL;
++ while (kdeReadFunc(NULL, static_cast<GIOCondition>(0), &data))
++ ;
++ }
++ return data.success;
++}
+
-+bool nsKDEUtils::internalCommand( const nsTArray<nsCString>& command, GtkWindow* parent, bool blockUi,
-+ nsTArray<nsCString>* output )
-+ {
-+ if( !startHelper())
-+ return false;
-+ feedCommand( command );
-+ // do not store the data in 'this' but in extra structure, just in case there
-+ // is reentrancy (can there be? the event loop is re-entered)
-+ nsKDECommandData data;
-+ data.file = replyFile;
-+ data.output = output;
-+ data.success = false;
-+ if( blockUi )
-+ {
-+ data.loop = g_main_loop_new( NULL, FALSE );
-+ GtkWidget* window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
-+ if( parent && gtk_window_get_group(parent) )
-+ gtk_window_group_add_window( gtk_window_get_group(parent), GTK_WINDOW( window ));
-+ gtk_widget_realize( window );
-+ gtk_widget_set_sensitive( window, TRUE );
-+ gtk_grab_add( window );
-+ GIOChannel* channel = g_io_channel_unix_new( fileno( data.file ));
-+ g_io_add_watch( channel, static_cast< GIOCondition >( G_IO_IN | G_IO_ERR | G_IO_HUP ), kdeReadFunc, &data );
-+ g_io_channel_unref( channel );
-+ g_main_loop_run( data.loop );
-+ g_main_loop_unref( data.loop );
-+ gtk_grab_remove( window );
-+ gtk_widget_destroy( window );
-+ }
-+ else
-+ {
-+ data.loop = NULL;
-+ while( kdeReadFunc( NULL, static_cast< GIOCondition >( 0 ), &data ))
-+ ;
-+ }
-+ return data.success;
++bool nsKDEUtils::startHelper() {
++ if (helperRunning) return true;
++ if (helperFailed) return false;
++ helperFailed = true;
++ int fdcommand[2];
++ int fdreply[2];
++ if (pipe(fdcommand) < 0) return false;
++ if (pipe(fdreply) < 0) {
++ close(fdcommand[0]);
++ close(fdcommand[1]);
++ return false;
++ }
++ char* args[2] = {const_cast<char*>(KWATERFOXHELPER), NULL};
++ switch (fork()) {
++ case -1: {
++ close(fdcommand[0]);
++ close(fdcommand[1]);
++ close(fdreply[0]);
++ close(fdreply[1]);
++ return false;
+ }
-+
-+bool nsKDEUtils::startHelper()
++ case 0: // child
+ {
-+ if( helperRunning )
-+ return true;
-+ if( helperFailed )
-+ return false;
-+ helperFailed = true;
-+ int fdcommand[ 2 ];
-+ int fdreply[ 2 ];
-+ if( pipe( fdcommand ) < 0 )
-+ return false;
-+ if( pipe( fdreply ) < 0 )
-+ {
-+ close( fdcommand[ 0 ] );
-+ close( fdcommand[ 1 ] );
-+ return false;
-+ }
-+ char* args[ 2 ] = { const_cast< char* >( KWATERFOXHELPER ), NULL };
-+ switch( fork())
-+ {
-+ case -1:
-+ {
-+ close( fdcommand[ 0 ] );
-+ close( fdcommand[ 1 ] );
-+ close( fdreply[ 0 ] );
-+ close( fdreply[ 1 ] );
-+ return false;
-+ }
-+ case 0: // child
-+ {
-+ if( dup2( fdcommand[ 0 ], STDIN_FILENO ) < 0 )
-+ _exit( 1 );
-+ if( dup2( fdreply[ 1 ], STDOUT_FILENO ) < 0 )
-+ _exit( 1 );
-+ int maxfd = 1024; // close all other fds
-+ struct rlimit rl;
-+ if( getrlimit( RLIMIT_NOFILE, &rl ) == 0 )
-+ maxfd = rl.rlim_max;
-+ for( int i = 3;
-+ i < maxfd;
-+ ++i )
-+ close( i );
++ if (dup2(fdcommand[0], STDIN_FILENO) < 0) _exit(1);
++ if (dup2(fdreply[1], STDOUT_FILENO) < 0) _exit(1);
++ int maxfd = 1024; // close all other fds
++ struct rlimit rl;
++ if (getrlimit(RLIMIT_NOFILE, &rl) == 0) maxfd = rl.rlim_max;
++ for (int i = 3; i < maxfd; ++i) close(i);
+#ifdef DEBUG_KDE
-+ execvp( KWATERFOXHELPER, args );
++ execvp(KWATERFOXHELPER, args);
+#else
-+ execv( KWATERFOXHELPER, args );
++ execv(KWATERFOXHELPER, args);
+#endif
-+ _exit( 1 ); // failed
-+ }
-+ default: // parent
-+ {
-+ commandFile = fdopen( fdcommand[ 1 ], "w" );
-+ replyFile = fdopen( fdreply[ 0 ], "r" );
-+ close( fdcommand[ 0 ] );
-+ close( fdreply[ 1 ] );
-+ if( commandFile == NULL || replyFile == NULL )
-+ {
-+ closeHelper();
-+ return false;
-+ }
-+ // ok, helper ready, getKdeRunning() will check if it works
-+ }
-+ }
-+ helperFailed = false;
-+ helperRunning = true;
-+ return true;
++ _exit(1); // failed
+ }
-+
-+void nsKDEUtils::closeHelper()
++ default: // parent
+ {
-+ if( commandFile != NULL )
-+ fclose( commandFile ); // this will also make the helper quit
-+ if( replyFile != NULL )
-+ fclose( replyFile );
-+ helperRunning = false;
++ commandFile = fdopen(fdcommand[1], "w");
++ replyFile = fdopen(fdreply[0], "r");
++ close(fdcommand[0]);
++ close(fdreply[1]);
++ if (commandFile == NULL || replyFile == NULL) {
++ closeHelper();
++ return false;
++ }
++ // ok, helper ready, getKdeRunning() will check if it works
+ }
++ }
++ helperFailed = false;
++ helperRunning = true;
++ return true;
++}
+
-+void nsKDEUtils::feedCommand( const nsTArray<nsCString>& command )
-+ {
-+ for( int i = 0;
-+ i < command.Length();
-+ ++i )
-+ {
-+ nsCString line = command[ i ];
-+ line.ReplaceSubstring( "\\", "\\" "\\" ); // \ -> \\ , i.e. escape
-+ line.ReplaceSubstring( "\n", "\\n" );
++void nsKDEUtils::closeHelper() {
++ if (commandFile != NULL)
++ fclose(commandFile); // this will also make the helper quit
++ if (replyFile != NULL) fclose(replyFile);
++ helperRunning = false;
++}
++
++void nsKDEUtils::feedCommand(const nsTArray<nsCString>& command) {
++ for (int i = 0; i < command.Length(); ++i) {
++ nsCString line = command[i];
++ line.ReplaceSubstring("\\",
++ "\\"
++ "\\"); // \ -> \\ , i.e. escape
++ line.ReplaceSubstring("\n", "\\n");
+#ifdef DEBUG_KDE
-+ fprintf( stderr, "COMM: %s\n", line.get());
++ fprintf(stderr, "COMM: %s\n", line.get());
+#endif
-+ fputs( line.get(), commandFile );
-+ fputs( "\n", commandFile );
-+ }
-+ fputs( "\\E\n", commandFile ); // done as \E, so it cannot happen in normal data
-+ fflush( commandFile );
-+ }
++ fputs(line.get(), commandFile);
++ fputs("\n", commandFile);
++ }
++ fputs("\\E\n",
++ commandFile); // done as \E, so it cannot happen in normal data
++ fflush(commandFile);
++}
diff --git a/toolkit/xre/nsKDEUtils.h b/toolkit/xre/nsKDEUtils.h
new file mode 100644
-index 000000000000..8729aebcaa1c
+index 000000000000..ed607357c1fe
--- /dev/null
+++ b/toolkit/xre/nsKDEUtils.h
-@@ -0,0 +1,48 @@
+@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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
@@ -856,65 +816,72 @@ index 000000000000..8729aebcaa1c
+
+class nsIArray;
+
-+class NS_EXPORT nsKDEUtils
-+ {
-+ public:
-+ /* Returns true if running inside a KDE session (regardless of whether there is KDE
-+ support available for Waterfox). This should be used e.g. when determining
-+ dialog button order but not for code that requires the KDE support. */
-+ static bool kdeSession();
-+ /* Returns true if running inside a KDE session and KDE support is available
-+ for Waterfox. This should be used everywhere where the external helper is needed. */
-+ static bool kdeSupport();
-+ /* Executes the given helper command, returns true if helper returned success. */
-+ static bool command( const nsTArray<nsCString>& command, nsTArray<nsCString>* output = NULL );
-+ static bool command( nsIArray* command, nsIArray** output = NULL );
-+ /* Like command(), but additionally blocks the parent widget like if there was
-+ a modal dialog shown and enters the event loop (i.e. there are still paint updates,
-+ this is for commands that take long). */
-+ static bool commandBlockUi( const nsTArray<nsCString>& command, GtkWindow* parent, nsTArray<nsCString>* output = NULL );
-+
-+ private:
-+ nsKDEUtils();
-+ ~nsKDEUtils();
-+ static nsKDEUtils* self();
-+ bool startHelper();
-+ void closeHelper();
-+ void feedCommand( const nsTArray<nsCString>& command );
-+ bool internalCommand( const nsTArray<nsCString>& command, GtkWindow* parent, bool isParent,
-+ nsTArray<nsCString>* output );
-+ FILE* commandFile;
-+ FILE* replyFile;
-+ };
-+
-+#endif // nsKDEUtils
++class NS_EXPORT nsKDEUtils {
++ public:
++ /* Returns true if running inside a KDE session (regardless of whether there
++ is KDE support available for Waterfox). This should be used e.g. when
++ determining dialog button order but not for code that requires the KDE
++ support. */
++ static bool kdeSession();
++ /* Returns true if running inside a KDE session and KDE support is available
++ for Waterfox. This should be used everywhere where the external helper is
++ needed. */
++ static bool kdeSupport();
++ /* Executes the given helper command, returns true if helper returned success.
++ */
++ static bool command(const nsTArray<nsCString>& command,
++ nsTArray<nsCString>* output = NULL);
++ static bool command(nsIArray* command, nsIArray** output = NULL);
++ /* Like command(), but additionally blocks the parent widget like if there was
++ a modal dialog shown and enters the event loop (i.e. there are still paint
++ updates, this is for commands that take long). */
++ static bool commandBlockUi(const nsTArray<nsCString>& command,
++ GtkWindow* parent,
++ nsTArray<nsCString>* output = NULL);
++
++ private:
++ nsKDEUtils();
++ ~nsKDEUtils();
++ static nsKDEUtils* self();
++ bool startHelper();
++ void closeHelper();
++ void feedCommand(const nsTArray<nsCString>& command);
++ bool internalCommand(const nsTArray<nsCString>& command, GtkWindow* parent,
++ bool isParent, nsTArray<nsCString>* output);
++ FILE* commandFile;
++ FILE* replyFile;
++};
++
++#endif // nsKDEUtils
diff --git a/uriloader/exthandler/HandlerServiceParent.cpp b/uriloader/exthandler/HandlerServiceParent.cpp
-index dbcc95d95646..5f2542a95065 100644
+index ab77657dd5f3..18b4d8556069 100644
--- a/uriloader/exthandler/HandlerServiceParent.cpp
+++ b/uriloader/exthandler/HandlerServiceParent.cpp
-@@ -12,7 +12,7 @@
- #include "ContentHandlerService.h"
- #include "nsStringEnumerator.h"
+@@ -18,7 +18,7 @@
+ #include "nsComponentManagerUtils.h"
+ #include "nsServiceManagerUtils.h"
#ifdef MOZ_WIDGET_GTK
-# include "unix/nsGNOMERegistry.h"
+# include "unix/nsCommonRegistry.h"
#endif
using mozilla::dom::ContentHandlerService;
-@@ -304,7 +304,7 @@ mozilla::ipc::IPCResult HandlerServiceParent::RecvExistsForProtocolOS(
+@@ -310,8 +310,8 @@ mozilla::ipc::IPCResult HandlerServiceParent::RecvExistsForProtocolOS(
}
#ifdef MOZ_WIDGET_GTK
// Check the GNOME registry for a protocol handler
-- *aHandlerExists = nsGNOMERegistry::HandlerExists(aProtocolScheme.get());
-+ *aHandlerExists = nsCommonRegistry::HandlerExists(aProtocolScheme.get());
+- *aHandlerExists =
+- nsGNOMERegistry::HandlerExists(PromiseFlatCString(aProtocolScheme).get());
++ *aHandlerExists = nsCommonRegistry::HandlerExists(
++ PromiseFlatCString(aProtocolScheme).get());
#else
*aHandlerExists = false;
#endif
diff --git a/uriloader/exthandler/moz.build b/uriloader/exthandler/moz.build
-index 92647a9b3478..fc5068cd2069 100644
+index 0fb126a7f3f7..8cc0006f3045 100644
--- a/uriloader/exthandler/moz.build
+++ b/uriloader/exthandler/moz.build
-@@ -83,7 +83,9 @@ else:
+@@ -86,7 +86,9 @@ else:
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
UNIFIED_SOURCES += [
@@ -924,7 +891,7 @@ index 92647a9b3478..fc5068cd2069 100644
"unix/nsMIMEInfoUnix.cpp",
]
elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
-@@ -135,6 +137,7 @@ LOCAL_INCLUDES += [
+@@ -134,6 +136,7 @@ LOCAL_INCLUDES += [
"/dom/ipc",
"/netwerk/base",
"/netwerk/protocol/http",
@@ -934,10 +901,10 @@ index 92647a9b3478..fc5068cd2069 100644
if CONFIG["MOZ_ENABLE_DBUS"]:
diff --git a/uriloader/exthandler/unix/nsCommonRegistry.cpp b/uriloader/exthandler/unix/nsCommonRegistry.cpp
new file mode 100644
-index 000000000000..630ab6147db3
+index 000000000000..3371a756e2c2
--- /dev/null
+++ b/uriloader/exthandler/unix/nsCommonRegistry.cpp
-@@ -0,0 +1,53 @@
+@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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
@@ -950,50 +917,39 @@ index 000000000000..630ab6147db3
+#include "nsString.h"
+#include "nsKDEUtils.h"
+
-+/* static */ bool
-+nsCommonRegistry::HandlerExists(const char *aProtocolScheme)
-+{
-+ if( nsKDEUtils::kdeSupport())
-+ return nsKDERegistry::HandlerExists( aProtocolScheme );
-+ return nsGNOMERegistry::HandlerExists( aProtocolScheme );
++/* static */ bool nsCommonRegistry::HandlerExists(const char* aProtocolScheme) {
++ if (nsKDEUtils::kdeSupport())
++ return nsKDERegistry::HandlerExists(aProtocolScheme);
++ return nsGNOMERegistry::HandlerExists(aProtocolScheme);
+}
+
-+/* static */ nsresult
-+nsCommonRegistry::LoadURL(nsIURI *aURL)
-+{
-+ if( nsKDEUtils::kdeSupport())
-+ return nsKDERegistry::LoadURL( aURL );
-+ return nsGNOMERegistry::LoadURL( aURL );
++/* static */ nsresult nsCommonRegistry::LoadURL(nsIURI* aURL) {
++ if (nsKDEUtils::kdeSupport()) return nsKDERegistry::LoadURL(aURL);
++ return nsGNOMERegistry::LoadURL(aURL);
+}
+
-+/* static */ void
-+nsCommonRegistry::GetAppDescForScheme(const nsACString& aScheme,
-+ nsAString& aDesc)
-+{
-+ if( nsKDEUtils::kdeSupport())
-+ return nsKDERegistry::GetAppDescForScheme( aScheme, aDesc );
-+ return nsGNOMERegistry::GetAppDescForScheme( aScheme, aDesc );
++/* static */ void nsCommonRegistry::GetAppDescForScheme(
++ const nsACString& aScheme, nsAString& aDesc) {
++ if (nsKDEUtils::kdeSupport())
++ return nsKDERegistry::GetAppDescForScheme(aScheme, aDesc);
++ return nsGNOMERegistry::GetAppDescForScheme(aScheme, aDesc);
+}
+
-+
+/* static */ already_AddRefed<nsMIMEInfoBase>
-+nsCommonRegistry::GetFromExtension(const nsACString& aFileExt)
-+{
-+ if( nsKDEUtils::kdeSupport())
-+ return nsKDERegistry::GetFromExtension( aFileExt );
-+ return nsGNOMERegistry::GetFromExtension( aFileExt );
++nsCommonRegistry::GetFromExtension(const nsACString& aFileExt) {
++ if (nsKDEUtils::kdeSupport())
++ return nsKDERegistry::GetFromExtension(aFileExt);
++ return nsGNOMERegistry::GetFromExtension(aFileExt);
+}
+
-+/* static */ already_AddRefed<nsMIMEInfoBase>
-+nsCommonRegistry::GetFromType(const nsACString& aMIMEType)
-+{
-+ if( nsKDEUtils::kdeSupport())
-+ return nsKDERegistry::GetFromType( aMIMEType );
-+ return nsGNOMERegistry::GetFromType( aMIMEType );
++/* static */ already_AddRefed<nsMIMEInfoBase> nsCommonRegistry::GetFromType(
++ const nsACString& aMIMEType) {
++ if (nsKDEUtils::kdeSupport()) return nsKDERegistry::GetFromType(aMIMEType);
++ return nsGNOMERegistry::GetFromType(aMIMEType);
+}
diff --git a/uriloader/exthandler/unix/nsCommonRegistry.h b/uriloader/exthandler/unix/nsCommonRegistry.h
new file mode 100644
-index 000000000000..85b3d9cee25e
+index 000000000000..075413e2fbb1
--- /dev/null
+++ b/uriloader/exthandler/unix/nsCommonRegistry.h
@@ -0,0 +1,28 @@
@@ -1009,28 +965,28 @@ index 000000000000..85b3d9cee25e
+
+class nsMIMEInfoBase;
+
-+class nsCommonRegistry
-+{
++class nsCommonRegistry {
+ public:
-+ static bool HandlerExists(const char *aProtocolScheme);
++ static bool HandlerExists(const char* aProtocolScheme);
+
-+ static nsresult LoadURL(nsIURI *aURL);
++ static nsresult LoadURL(nsIURI* aURL);
+
-+ static void GetAppDescForScheme(const nsACString& aScheme,
-+ nsAString& aDesc);
++ static void GetAppDescForScheme(const nsACString& aScheme, nsAString& aDesc);
+
-+ static already_AddRefed<nsMIMEInfoBase> GetFromExtension(const nsACString& aFileExt);
++ static already_AddRefed<nsMIMEInfoBase> GetFromExtension(
++ const nsACString& aFileExt);
+
-+ static already_AddRefed<nsMIMEInfoBase> GetFromType(const nsACString& aMIMEType);
++ static already_AddRefed<nsMIMEInfoBase> GetFromType(
++ const nsACString& aMIMEType);
+};
+
+#endif
diff --git a/uriloader/exthandler/unix/nsKDERegistry.cpp b/uriloader/exthandler/unix/nsKDERegistry.cpp
new file mode 100644
-index 000000000000..f78e64c7e9a3
+index 000000000000..082035566f0b
--- /dev/null
+++ b/uriloader/exthandler/unix/nsKDERegistry.cpp
-@@ -0,0 +1,89 @@
+@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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
@@ -1044,88 +1000,74 @@ index 000000000000..f78e64c7e9a3
+#include "nsMIMEInfoUnix.h"
+#include "nsKDEUtils.h"
+
-+/* static */ bool
-+nsKDERegistry::HandlerExists(const char *aProtocolScheme)
-+{
-+ nsTArray<nsCString> command;
-+ command.AppendElement( "HANDLEREXISTS"_ns );
-+ command.AppendElement( nsAutoCString( aProtocolScheme ));
-+ return nsKDEUtils::command( command );
++/* static */ bool nsKDERegistry::HandlerExists(const char* aProtocolScheme) {
++ nsTArray<nsCString> command;
++ command.AppendElement("HANDLEREXISTS"_ns);
++ command.AppendElement(nsAutoCString(aProtocolScheme));
++ return nsKDEUtils::command(command);
+}
+
-+/* static */ nsresult
-+nsKDERegistry::LoadURL(nsIURI *aURL)
-+{
-+ nsTArray<nsCString> command;
-+ command.AppendElement( "OPEN"_ns );
-+ nsCString url;
-+ aURL->GetSpec( url );
-+ command.AppendElement( url );
-+ bool rv = nsKDEUtils::command( command );
-+ if (!rv)
-+ return NS_ERROR_FAILURE;
++/* static */ nsresult nsKDERegistry::LoadURL(nsIURI* aURL) {
++ nsTArray<nsCString> command;
++ command.AppendElement("OPEN"_ns);
++ nsCString url;
++ aURL->GetSpec(url);
++ command.AppendElement(url);
++ bool rv = nsKDEUtils::command(command);
++ if (!rv) return NS_ERROR_FAILURE;
+
-+ return NS_OK;
++ return NS_OK;
+}
+
-+/* static */ void
-+nsKDERegistry::GetAppDescForScheme(const nsACString& aScheme,
-+ nsAString& aDesc)
-+{
-+ nsTArray<nsCString> command;
-+ command.AppendElement( "GETAPPDESCFORSCHEME"_ns );
-+ command.AppendElement( aScheme );
-+ nsTArray<nsCString> output;
-+ if( nsKDEUtils::command( command, &output ) && output.Length() == 1 )
-+ CopyUTF8toUTF16( output[ 0 ], aDesc );
++/* static */ void nsKDERegistry::GetAppDescForScheme(const nsACString& aScheme,
++ nsAString& aDesc) {
++ nsTArray<nsCString> command;
++ command.AppendElement("GETAPPDESCFORSCHEME"_ns);
++ command.AppendElement(aScheme);
++ nsTArray<nsCString> output;
++ if (nsKDEUtils::command(command, &output) && output.Length() == 1)
++ CopyUTF8toUTF16(output[0], aDesc);
+}
+
-+
-+/* static */ already_AddRefed<nsMIMEInfoBase>
-+nsKDERegistry::GetFromExtension(const nsACString& aFileExt)
-+{
-+ NS_ASSERTION(aFileExt[0] != '.', "aFileExt shouldn't start with a dot");
-+ nsTArray<nsCString> command;
-+ command.AppendElement( "GETFROMEXTENSION"_ns );
-+ command.AppendElement( aFileExt );
-+ return GetFromHelper( command );
++/* static */ already_AddRefed<nsMIMEInfoBase> nsKDERegistry::GetFromExtension(
++ const nsACString& aFileExt) {
++ NS_ASSERTION(aFileExt[0] != '.', "aFileExt shouldn't start with a dot");
++ nsTArray<nsCString> command;
++ command.AppendElement("GETFROMEXTENSION"_ns);
++ command.AppendElement(aFileExt);
++ return GetFromHelper(command);
+}
+
-+/* static */ already_AddRefed<nsMIMEInfoBase>
-+nsKDERegistry::GetFromType(const nsACString& aMIMEType)
-+{
-+ nsTArray<nsCString> command;
-+ command.AppendElement( "GETFROMTYPE"_ns );
-+ command.AppendElement( aMIMEType );
-+ return GetFromHelper( command );
++/* static */ already_AddRefed<nsMIMEInfoBase> nsKDERegistry::GetFromType(
++ const nsACString& aMIMEType) {
++ nsTArray<nsCString> command;
++ command.AppendElement("GETFROMTYPE"_ns);
++ command.AppendElement(aMIMEType);
++ return GetFromHelper(command);
+}
+
-+/* static */ already_AddRefed<nsMIMEInfoBase>
-+nsKDERegistry::GetFromHelper(const nsTArray<nsCString>& command)
-+{
-+ nsTArray<nsCString> output;
-+ if( nsKDEUtils::command( command, &output ) && output.Length() == 3 )
-+ {
-+ nsCString mimetype = output[ 0 ];
-+ RefPtr<nsMIMEInfoUnix> mimeInfo = new nsMIMEInfoUnix( mimetype );
-+ NS_ENSURE_TRUE(mimeInfo, nullptr);
-+ nsCString description = output[ 1 ];
-+ mimeInfo->SetDescription(NS_ConvertUTF8toUTF16(description));
-+ nsCString handlerAppName = output[ 2 ];
-+ mozilla::StaticPrefs::browser_download_improvements_to_download_panel()
-+ ? mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk)
-+ : mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
-+ mimeInfo->SetDefaultDescription(NS_ConvertUTF8toUTF16(handlerAppName));
-+ return mimeInfo.forget();
-+ }
-+ return nullptr;
++/* static */ already_AddRefed<nsMIMEInfoBase> nsKDERegistry::GetFromHelper(
++ const nsTArray<nsCString>& command) {
++ nsTArray<nsCString> output;
++ if (nsKDEUtils::command(command, &output) && output.Length() == 3) {
++ nsCString mimetype = output[0];
++ RefPtr<nsMIMEInfoUnix> mimeInfo = new nsMIMEInfoUnix(mimetype);
++ NS_ENSURE_TRUE(mimeInfo, nullptr);
++ nsCString description = output[1];
++ mimeInfo->SetDescription(NS_ConvertUTF8toUTF16(description));
++ nsCString handlerAppName = output[2];
++ mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
++ mimeInfo->SetDefaultDescription(NS_ConvertUTF8toUTF16(handlerAppName));
++ return mimeInfo.forget();
++ }
++ return nullptr;
+}
diff --git a/uriloader/exthandler/unix/nsKDERegistry.h b/uriloader/exthandler/unix/nsKDERegistry.h
new file mode 100644
-index 000000000000..5b07eebc6d62
+index 000000000000..c6a41b331b2b
--- /dev/null
+++ b/uriloader/exthandler/unix/nsKDERegistry.h
-@@ -0,0 +1,34 @@
+@@ -0,0 +1,35 @@
+/* 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/. */
@@ -1138,30 +1080,31 @@ index 000000000000..5b07eebc6d62
+#include "nsTArray.h"
+
+class nsMIMEInfoBase;
-+//class nsAutoCString;
-+//class nsCString;
++// class nsAutoCString;
++// class nsCString;
+
-+class nsKDERegistry
-+{
++class nsKDERegistry {
+ public:
-+ static bool HandlerExists(const char *aProtocolScheme);
++ static bool HandlerExists(const char* aProtocolScheme);
+
-+ static nsresult LoadURL(nsIURI *aURL);
++ static nsresult LoadURL(nsIURI* aURL);
+
-+ static void GetAppDescForScheme(const nsACString& aScheme,
-+ nsAString& aDesc);
++ static void GetAppDescForScheme(const nsACString& aScheme, nsAString& aDesc);
+
-+ static already_AddRefed<nsMIMEInfoBase> GetFromExtension(const nsACString& aFileExt);
++ static already_AddRefed<nsMIMEInfoBase> GetFromExtension(
++ const nsACString& aFileExt);
+
-+ static already_AddRefed<nsMIMEInfoBase> GetFromType(const nsACString& aMIMEType);
-+ private:
-+ static already_AddRefed<nsMIMEInfoBase> GetFromHelper(const nsTArray<nsCString>& command);
++ static already_AddRefed<nsMIMEInfoBase> GetFromType(
++ const nsACString& aMIMEType);
+
++ private:
++ static already_AddRefed<nsMIMEInfoBase> GetFromHelper(
++ const nsTArray<nsCString>& command);
+};
+
-+#endif //nsKDERegistry_h__
++#endif // nsKDERegistry_h__
diff --git a/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp b/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
-index 7cbefcce3e94..84083348c8f1 100644
+index 330c4411597f..c96c1f3ca5a0 100644
--- a/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
+++ b/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
@@ -5,16 +5,19 @@
@@ -1177,7 +1120,7 @@ index 7cbefcce3e94..84083348c8f1 100644
# include "nsDBusHandlerApp.h"
#endif
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
-+#include "nsKDEUtils.h"
++# include "nsKDEUtils.h"
+#endif
nsresult nsMIMEInfoUnix::LoadUriInternal(nsIURI* aURI) {
@@ -1186,7 +1129,7 @@ index 7cbefcce3e94..84083348c8f1 100644
}
NS_IMETHODIMP
-@@ -27,15 +30,15 @@ nsMIMEInfoUnix::GetHasDefaultHandler(bool* _retval) {
+@@ -29,15 +32,15 @@ nsMIMEInfoUnix::GetHasDefaultHandler(bool* _retval) {
*_retval = false;
if (mClass == eProtocolInfo) {
@@ -1205,32 +1148,30 @@ index 7cbefcce3e94..84083348c8f1 100644
}
}
if (mimeInfo) *_retval = true;
-@@ -55,6 +58,23 @@ nsresult nsMIMEInfoUnix::LaunchDefaultWithFile(nsIFile* aFile) {
+@@ -59,6 +62,21 @@ nsresult nsMIMEInfoUnix::LaunchDefaultWithFile(nsIFile* aFile) {
nsAutoCString nativePath;
aFile->GetNativePath(nativePath);
-+ if( nsKDEUtils::kdeSupport()) {
++ if (nsKDEUtils::kdeSupport()) {
+ bool supports;
-+ if( NS_SUCCEEDED( GetHasDefaultHandler( &supports )) && supports ) {
++ if (NS_SUCCEEDED(GetHasDefaultHandler(&supports)) && supports) {
+ nsTArray<nsCString> command;
-+ command.AppendElement( "OPEN"_ns );
-+ command.AppendElement( nativePath );
-+ command.AppendElement( "MIMETYPE"_ns );
-+ command.AppendElement( mSchemeOrType );
-+ if( nsKDEUtils::command( command ))
-+ return NS_OK;
++ command.AppendElement("OPEN"_ns);
++ command.AppendElement(nativePath);
++ command.AppendElement("MIMETYPE"_ns);
++ command.AppendElement(mSchemeOrType);
++ if (nsKDEUtils::command(command)) return NS_OK;
+ }
-+ if (!mDefaultApplication)
-+ return NS_ERROR_FILE_NOT_FOUND;
++ if (!GetDefaultApplication()) return NS_ERROR_FILE_NOT_FOUND;
+
-+ return LaunchWithIProcess(mDefaultApplication, nativePath);
++ return LaunchWithIProcess(GetDefaultApplication(), nativePath);
+ }
+
nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
if (!giovfs) {
return NS_ERROR_FAILURE;
diff --git a/uriloader/exthandler/unix/nsOSHelperAppService.cpp b/uriloader/exthandler/unix/nsOSHelperAppService.cpp
-index b9e7aed3cb5c..367ad9ee2421 100644
+index 7f6eaa46f2ee..f7627e790c47 100644
--- a/uriloader/exthandler/unix/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/unix/nsOSHelperAppService.cpp
@@ -10,7 +10,7 @@
@@ -1242,7 +1183,7 @@ index b9e7aed3cb5c..367ad9ee2421 100644
# ifdef MOZ_BUILD_APP_IS_BROWSER
# include "nsIToolkitShellService.h"
# include "nsIGNOMEShellService.h"
-@@ -1030,7 +1030,7 @@ nsresult nsOSHelperAppService::OSProtocolHandlerExists(
+@@ -1106,7 +1106,7 @@ nsresult nsOSHelperAppService::OSProtocolHandlerExists(
if (!XRE_IsContentProcess()) {
#ifdef MOZ_WIDGET_GTK
// Check the GNOME registry for a protocol handler
@@ -1251,7 +1192,7 @@ index b9e7aed3cb5c..367ad9ee2421 100644
#else
*aHandlerExists = false;
#endif
-@@ -1050,7 +1050,7 @@ nsresult nsOSHelperAppService::OSProtocolHandlerExists(
+@@ -1126,7 +1126,7 @@ nsresult nsOSHelperAppService::OSProtocolHandlerExists(
NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(
const nsACString& aScheme, nsAString& _retval) {
#ifdef MOZ_WIDGET_GTK
@@ -1260,29 +1201,29 @@ index b9e7aed3cb5c..367ad9ee2421 100644
return _retval.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
#else
return NS_ERROR_NOT_AVAILABLE;
-@@ -1153,7 +1153,7 @@ already_AddRefed<nsMIMEInfoBase> nsOSHelperAppService::GetFromExtension(
+@@ -1231,7 +1231,7 @@ already_AddRefed<nsMIMEInfoBase> nsOSHelperAppService::GetFromExtension(
#ifdef MOZ_WIDGET_GTK
- LOG(("Looking in GNOME registry\n"));
+ LOG("Looking in GNOME registry\n");
RefPtr<nsMIMEInfoBase> gnomeInfo =
- nsGNOMERegistry::GetFromExtension(aFileExt);
+ nsCommonRegistry::GetFromExtension(aFileExt);
if (gnomeInfo) {
- LOG(("Got MIMEInfo from GNOME registry\n"));
+ LOG("Got MIMEInfo from GNOME registry\n");
return gnomeInfo.forget();
-@@ -1266,7 +1266,7 @@ already_AddRefed<nsMIMEInfoBase> nsOSHelperAppService::GetFromType(
+@@ -1344,7 +1344,7 @@ already_AddRefed<nsMIMEInfoBase> nsOSHelperAppService::GetFromType(
#ifdef MOZ_WIDGET_GTK
if (handler.IsEmpty()) {
- RefPtr<nsMIMEInfoBase> gnomeInfo = nsGNOMERegistry::GetFromType(aMIMEType);
+ RefPtr<nsMIMEInfoBase> gnomeInfo = nsCommonRegistry::GetFromType(aMIMEType);
if (gnomeInfo) {
- LOG(
- ("Got MIMEInfo from GNOME registry without extensions; setting them "
+ LOG("Got MIMEInfo from GNOME registry without extensions; setting them "
+ "to %s\n",
diff --git a/widget/gtk/moz.build b/widget/gtk/moz.build
-index bf64f7ebdc65..f94b4e017e5e 100644
+index 90574c130f59..ef530396aa0b 100644
--- a/widget/gtk/moz.build
+++ b/widget/gtk/moz.build
-@@ -169,6 +169,7 @@ LOCAL_INCLUDES += [
+@@ -159,6 +159,7 @@ LOCAL_INCLUDES += [
"/layout/xul",
"/other-licenses/atk-1.0",
"/third_party/cups/include",
@@ -1291,7 +1232,7 @@ index bf64f7ebdc65..f94b4e017e5e 100644
"/widget/headless",
]
diff --git a/widget/gtk/nsFilePicker.cpp b/widget/gtk/nsFilePicker.cpp
-index c73130496ae7..9e7b9aed56f1 100644
+index 22d0f46b9563..b2a68711eb34 100644
--- a/widget/gtk/nsFilePicker.cpp
+++ b/widget/gtk/nsFilePicker.cpp
@@ -5,6 +5,7 @@
@@ -1302,7 +1243,7 @@ index c73130496ae7..9e7b9aed56f1 100644
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
-@@ -27,6 +28,8 @@
+@@ -28,6 +29,8 @@
#include "WidgetUtilsGtk.h"
#include "nsFilePicker.h"
@@ -1311,40 +1252,41 @@ index c73130496ae7..9e7b9aed56f1 100644
#undef LOG
#ifdef MOZ_LOGGING
-@@ -241,7 +244,9 @@ NS_IMETHODIMP
+@@ -242,7 +245,8 @@ NS_IMETHODIMP
nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter) {
if (aFilter.EqualsLiteral("..apps")) {
// No platform specific thing we can do here, really....
- return NS_OK;
+ // Unless it's KDE.
-+ if( mMode != modeOpen || !nsKDEUtils::kdeSupport())
-+ return NS_OK;
++ if (mMode != modeOpen || !nsKDEUtils::kdeSupport()) return NS_OK;
}
nsAutoCString filter, name;
-@@ -351,6 +356,29 @@ nsFilePicker::Open(nsIFilePickerShownCallback* aCallback) {
+@@ -352,6 +356,31 @@ nsFilePicker::Open(nsIFilePickerShownCallback* aCallback) {
// Can't show two dialogs concurrently with the same filepicker
if (mRunning) return NS_ERROR_NOT_AVAILABLE;
+ // KDE file picker is not handled via callback
-+ if( nsKDEUtils::kdeSupport()) {
++ if (nsKDEUtils::kdeSupport()) {
+ mCallback = aCallback;
+ mRunning = true;
+ NS_ADDREF_THIS();
-+ g_idle_add([](gpointer data) -> gboolean {
-+ nsFilePicker* queuedPicker = (nsFilePicker*) data;
-+ int16_t result;
-+ queuedPicker->kdeFileDialog(&result);
-+ if (queuedPicker->mCallback) {
-+ queuedPicker->mCallback->Done(result);
-+ queuedPicker->mCallback = nullptr;
-+ } else {
-+ queuedPicker->mResult = result;
-+ }
-+ queuedPicker->mRunning = false;
-+ NS_RELEASE(queuedPicker);
-+ return G_SOURCE_REMOVE;
-+ }, this);
++ g_idle_add(
++ [](gpointer data) -> gboolean {
++ nsFilePicker* queuedPicker = (nsFilePicker*)data;
++ nsIFilePicker::ResultCode result;
++ queuedPicker->kdeFileDialog(&result);
++ if (queuedPicker->mCallback) {
++ queuedPicker->mCallback->Done(result);
++ queuedPicker->mCallback = nullptr;
++ } else {
++ queuedPicker->mResult = result;
++ }
++ queuedPicker->mRunning = false;
++ NS_RELEASE(queuedPicker);
++ return G_SOURCE_REMOVE;
++ },
++ this);
+
+ return NS_OK;
+ }
@@ -1352,76 +1294,68 @@ index c73130496ae7..9e7b9aed56f1 100644
NS_ConvertUTF16toUTF8 title(mTitle);
GtkWindow* parent_widget =
-@@ -580,6 +608,234 @@ void nsFilePicker::Done(void* file_chooser, gint response) {
+@@ -633,6 +662,205 @@ void nsFilePicker::Done(void* file_chooser, gint response) {
NS_RELEASE_THIS();
}
-+nsCString nsFilePicker::kdeMakeFilter( int index )
-+ {
-+ nsCString buf = mFilters[ index ];
-+ for( PRUint32 i = 0;
-+ i < buf.Length();
-+ ++i )
-+ if( buf[ i ] == ';' ) // KDE separates just using spaces
-+ buf.SetCharAt( ' ', i );
-+ if (!mFilterNames[index].IsEmpty())
-+ {
-+ buf += "|";
-+ buf += mFilterNames[index].get();
-+ }
-+ return buf;
-+ }
++nsCString nsFilePicker::kdeMakeFilter(int index) {
++ nsCString buf = mFilters[index];
++ for (PRUint32 i = 0; i < buf.Length(); ++i)
++ if (buf[i] == ';') // KDE separates just using spaces
++ buf.SetCharAt(' ', i);
++ if (!mFilterNames[index].IsEmpty()) {
++ buf += "|";
++ buf += mFilterNames[index].get();
++ }
++ return buf;
++}
+
-+static PRInt32 windowToXid( nsIWidget* widget )
-+ {
-+ GtkWindow *parent_widget = GTK_WINDOW(widget->GetNativeData(NS_NATIVE_SHELLWIDGET));
-+ GdkWindow* gdk_window = gtk_widget_get_window( gtk_widget_get_toplevel( GTK_WIDGET( parent_widget )));
-+ return GDK_WINDOW_XID( gdk_window );
-+ }
++static PRInt32 windowToXid(nsIWidget* widget) {
++ GtkWindow* parent_widget =
++ GTK_WINDOW(widget->GetNativeData(NS_NATIVE_SHELLWIDGET));
++ GdkWindow* gdk_window =
++ gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(parent_widget)));
++ return GDK_WINDOW_XID(gdk_window);
++}
+
-+NS_IMETHODIMP nsFilePicker::kdeFileDialog(PRInt16 *aReturn)
-+ {
-+ NS_ENSURE_ARG_POINTER(aReturn);
-+
-+ if( mMode == modeOpen && mFilters.Length() == 1 && mFilters[ 0 ].EqualsLiteral( "..apps" ))
-+ return kdeAppsDialog( aReturn );
-+
-+ nsCString title;
-+ title.Adopt(ToNewUTF8String(mTitle));
-+
-+ const char* arg = NULL;
-+ if( mAllowURLs )
-+ {
-+ switch( mMode )
-+ {
-+ case nsIFilePicker::modeOpen:
-+ case nsIFilePicker::modeOpenMultiple:
-+ arg = "GETOPENURL";
-+ break;
-+ case nsIFilePicker::modeSave:
-+ arg = "GETSAVEURL";
-+ break;
-+ case nsIFilePicker::modeGetFolder:
-+ arg = "GETDIRECTORYURL";
-+ break;
-+ }
-+ }
-+ else
-+ {
-+ switch( mMode )
-+ {
-+ case nsIFilePicker::modeOpen:
-+ case nsIFilePicker::modeOpenMultiple:
-+ arg = "GETOPENFILENAME";
-+ break;
-+ case nsIFilePicker::modeSave:
-+ arg = "GETSAVEFILENAME";
-+ break;
-+ case nsIFilePicker::modeGetFolder:
-+ arg = "GETDIRECTORYFILENAME";
-+ break;
-+ }
-+ }
++NS_IMETHODIMP nsFilePicker::kdeFileDialog(nsIFilePicker::ResultCode* aReturn) {
++ NS_ENSURE_ARG_POINTER(aReturn);
++
++ if (mMode == modeOpen && mFilters.Length() == 1 &&
++ mFilters[0].EqualsLiteral("..apps"))
++ return kdeAppsDialog(aReturn);
++
++ nsCString title;
++ title.Adopt(ToNewUTF8String(mTitle));
++
++ const char* arg = NULL;
++ if (mAllowURLs) {
++ switch (mMode) {
++ case nsIFilePicker::modeOpen:
++ case nsIFilePicker::modeOpenMultiple:
++ arg = "GETOPENURL";
++ break;
++ case nsIFilePicker::modeSave:
++ arg = "GETSAVEURL";
++ break;
++ case nsIFilePicker::modeGetFolder:
++ arg = "GETDIRECTORYURL";
++ break;
++ }
++ } else {
++ switch (mMode) {
++ case nsIFilePicker::modeOpen:
++ case nsIFilePicker::modeOpenMultiple:
++ arg = "GETOPENFILENAME";
++ break;
++ case nsIFilePicker::modeSave:
++ arg = "GETSAVEFILENAME";
++ break;
++ case nsIFilePicker::modeGetFolder:
++ arg = "GETDIRECTORYFILENAME";
++ break;
++ }
++ }
+
+ nsAutoCString directory;
+ if (mDisplayDirectory) {
@@ -1430,182 +1364,161 @@ index c73130496ae7..9e7b9aed56f1 100644
+ mPrevDisplayDirectory->GetNativePath(directory);
+ }
+
-+ nsAutoCString startdir;
++ nsAutoCString startdir;
+ if (!directory.IsEmpty()) {
+ startdir = directory;
+ }
+ if (mMode == nsIFilePicker::modeSave) {
-+ if( !startdir.IsEmpty())
-+ {
++ if (!startdir.IsEmpty()) {
+ startdir += "/";
+ startdir += ToNewUTF8String(mDefault);
-+ }
-+ else
++ } else
+ startdir = ToNewUTF8String(mDefault);
+ }
+
-+ nsAutoCString filters;
-+ PRInt32 count = mFilters.Length();
-+ if( count == 0 ) //just in case
-+ filters = "*";
-+ else
-+ {
-+ filters = kdeMakeFilter( 0 );
-+ for (PRInt32 i = 1; i < count; ++i)
-+ {
-+ filters += "\n";
-+ filters += kdeMakeFilter( i );
-+ }
-+ }
++ nsAutoCString filters;
++ PRInt32 count = mFilters.Length();
++ if (count == 0) // just in case
++ filters = "*";
++ else {
++ filters = kdeMakeFilter(0);
++ for (PRInt32 i = 1; i < count; ++i) {
++ filters += "\n";
++ filters += kdeMakeFilter(i);
++ }
++ }
+
-+ nsTArray<nsCString> command;
-+ command.AppendElement( nsAutoCString( arg ));
-+ command.AppendElement( startdir );
-+ if( mMode != nsIFilePicker::modeGetFolder )
-+ {
-+ command.AppendElement( filters );
-+ nsAutoCString selected;
-+ selected.AppendInt( mSelectedType );
-+ command.AppendElement( selected );
-+ }
-+ command.AppendElement( title );
-+ if( mMode == nsIFilePicker::modeOpenMultiple )
-+ command.AppendElement( "MULTIPLE"_ns );
-+ if( PRInt32 xid = windowToXid( mParentWidget ))
-+ {
-+ command.AppendElement( "PARENT"_ns );
-+ nsAutoCString parent;
-+ parent.AppendInt( xid );
-+ command.AppendElement( parent );
-+ }
++ nsTArray<nsCString> command;
++ command.AppendElement(nsAutoCString(arg));
++ command.AppendElement(startdir);
++ if (mMode != nsIFilePicker::modeGetFolder) {
++ command.AppendElement(filters);
++ nsAutoCString selected;
++ selected.AppendInt(mSelectedType);
++ command.AppendElement(selected);
++ }
++ command.AppendElement(title);
++ if (mMode == nsIFilePicker::modeOpenMultiple)
++ command.AppendElement("MULTIPLE"_ns);
++ if (PRInt32 xid = windowToXid(mParentWidget)) {
++ command.AppendElement("PARENT"_ns);
++ nsAutoCString parent;
++ parent.AppendInt(xid);
++ command.AppendElement(parent);
++ }
+
-+ nsTArray<nsCString> output;
-+ if( nsKDEUtils::commandBlockUi( command, GTK_WINDOW(mParentWidget->GetNativeData(NS_NATIVE_SHELLWIDGET)), &output ))
-+ {
-+ *aReturn = nsIFilePicker::returnOK;
-+ mFiles.Clear();
-+ if( mMode != nsIFilePicker::modeGetFolder )
-+ {
-+ mSelectedType = atoi( output[ 0 ].get());
-+ output.RemoveElementAt( 0 );
-+ }
-+ if (mMode == nsIFilePicker::modeOpenMultiple)
-+ {
-+ mFileURL.Truncate();
-+ PRUint32 count = output.Length();
-+ for( PRUint32 i = 0;
-+ i < count;
-+ ++i )
-+ {
-+ nsCOMPtr<nsIFile> localfile;
-+ nsresult rv = NS_NewNativeLocalFile( output[ i ],
-+ PR_FALSE,
-+ getter_AddRefs(localfile));
-+ if (NS_SUCCEEDED(rv))
-+ mFiles.AppendObject(localfile);
-+ }
-+ }
-+ else
-+ {
-+ if( output.Length() == 0 )
-+ mFileURL = nsCString();
-+ else if( mAllowURLs )
-+ mFileURL = output[ 0 ];
-+ else // GetFile() actually requires it to be url even for local files :-/
-+ {
-+ nsCOMPtr<nsIFile> localfile;
-+ nsresult rv = NS_NewNativeLocalFile( output[ 0 ],
-+ PR_FALSE,
-+ getter_AddRefs(localfile));
-+ if (NS_SUCCEEDED(rv))
-+ rv = net_GetURLSpecFromActualFile(localfile, mFileURL);
-+ }
-+ }
-+ // Remember last used directory.
-+ nsCOMPtr<nsIFile> file;
-+ GetFile(getter_AddRefs(file));
-+ if (file) {
-+ nsCOMPtr<nsIFile> dir;
-+ file->GetParent(getter_AddRefs(dir));
-+ nsCOMPtr<nsIFile> localDir(do_QueryInterface(dir));
-+ if (localDir) {
-+ localDir.swap(mPrevDisplayDirectory);
++ nsTArray<nsCString> output;
++ if (nsKDEUtils::commandBlockUi(
++ command,
++ GTK_WINDOW(mParentWidget->GetNativeData(NS_NATIVE_SHELLWIDGET)),
++ &output)) {
++ *aReturn = nsIFilePicker::returnOK;
++ mFiles.Clear();
++ if (mMode != nsIFilePicker::modeGetFolder) {
++ mSelectedType = atoi(output[0].get());
++ output.RemoveElementAt(0);
+ }
-+ }
-+ if (mMode == nsIFilePicker::modeSave)
-+ {
-+ nsCOMPtr<nsIFile> file;
-+ GetFile(getter_AddRefs(file));
-+ if (file)
-+ {
-+ bool exists = false;
-+ file->Exists(&exists);
-+ if (exists) // TODO do overwrite check in the helper app
-+ *aReturn = nsIFilePicker::returnReplace;
-+ }
-+ }
-+ }
-+ else
-+ {
-+ *aReturn = nsIFilePicker::returnCancel;
-+ }
-+ return NS_OK;
++ if (mMode == nsIFilePicker::modeOpenMultiple) {
++ mFileURL.Truncate();
++ PRUint32 count = output.Length();
++ for (PRUint32 i = 0; i < count; ++i) {
++ nsCOMPtr<nsIFile> localfile;
++ nsresult rv = NS_NewNativeLocalFile(output[i], PR_FALSE,
++ getter_AddRefs(localfile));
++ if (NS_SUCCEEDED(rv)) mFiles.AppendObject(localfile);
++ }
++ } else {
++ if (output.Length() == 0)
++ mFileURL = nsCString();
++ else if (mAllowURLs)
++ mFileURL = output[0];
++ else // GetFile() actually requires it to be url even for local files :-/
++ {
++ nsCOMPtr<nsIFile> localfile;
++ nsresult rv = NS_NewNativeLocalFile(output[0], PR_FALSE,
++ getter_AddRefs(localfile));
++ if (NS_SUCCEEDED(rv))
++ rv = net_GetURLSpecFromActualFile(localfile, mFileURL);
++ }
+ }
++ // Remember last used directory.
++ nsCOMPtr<nsIFile> file;
++ GetFile(getter_AddRefs(file));
++ if (file) {
++ nsCOMPtr<nsIFile> dir;
++ file->GetParent(getter_AddRefs(dir));
++ nsCOMPtr<nsIFile> localDir(dir);
++ if (localDir) {
++ localDir.swap(mPrevDisplayDirectory);
++ }
++ }
++ if (mMode == nsIFilePicker::modeSave) {
++ nsCOMPtr<nsIFile> file;
++ GetFile(getter_AddRefs(file));
++ if (file) {
++ bool exists = false;
++ file->Exists(&exists);
++ if (exists) // TODO do overwrite check in the helper app
++ *aReturn = nsIFilePicker::returnReplace;
++ }
++ }
++ } else {
++ *aReturn = nsIFilePicker::returnCancel;
++ }
++ return NS_OK;
++}
+
++NS_IMETHODIMP nsFilePicker::kdeAppsDialog(nsIFilePicker::ResultCode* aReturn) {
++ NS_ENSURE_ARG_POINTER(aReturn);
+
-+NS_IMETHODIMP nsFilePicker::kdeAppsDialog(PRInt16 *aReturn)
-+ {
-+ NS_ENSURE_ARG_POINTER(aReturn);
-+
-+ nsCString title;
-+ title.Adopt(ToNewUTF8String(mTitle));
++ nsCString title;
++ title.Adopt(ToNewUTF8String(mTitle));
+
-+ nsTArray<nsCString> command;
-+ command.AppendElement( "APPSDIALOG"_ns );
-+ command.AppendElement( title );
-+ if( PRInt32 xid = windowToXid( mParentWidget ))
-+ {
-+ command.AppendElement( "PARENT"_ns );
-+ nsAutoCString parent;
-+ parent.AppendInt( xid );
-+ command.AppendElement( parent );
-+ }
++ nsTArray<nsCString> command;
++ command.AppendElement("APPSDIALOG"_ns);
++ command.AppendElement(title);
++ if (PRInt32 xid = windowToXid(mParentWidget)) {
++ command.AppendElement("PARENT"_ns);
++ nsAutoCString parent;
++ parent.AppendInt(xid);
++ command.AppendElement(parent);
++ }
+
-+ nsTArray<nsCString> output;
-+ if( nsKDEUtils::commandBlockUi( command, GTK_WINDOW(mParentWidget->GetNativeData(NS_NATIVE_SHELLWIDGET)), &output ))
-+ {
-+ *aReturn = nsIFilePicker::returnOK;
-+ mFileURL = output.Length() > 0 ? output[ 0 ] : nsCString();
-+ }
-+ else
-+ {
-+ *aReturn = nsIFilePicker::returnCancel;
-+ }
-+ return NS_OK;
-+ }
++ nsTArray<nsCString> output;
++ if (nsKDEUtils::commandBlockUi(
++ command,
++ GTK_WINDOW(mParentWidget->GetNativeData(NS_NATIVE_SHELLWIDGET)),
++ &output)) {
++ *aReturn = nsIFilePicker::returnOK;
++ mFileURL = output.Length() > 0 ? output[0] : nsCString();
++ } else {
++ *aReturn = nsIFilePicker::returnCancel;
++ }
++ return NS_OK;
++}
+
// All below functions available as of GTK 3.20+
void* nsFilePicker::GtkFileChooserNew(const gchar* title, GtkWindow* parent,
GtkFileChooserAction action,
diff --git a/widget/gtk/nsFilePicker.h b/widget/gtk/nsFilePicker.h
-index 9b3110aa0048..be9d559c7bf4 100644
+index 496df4937277..a4c1862ec042 100644
--- a/widget/gtk/nsFilePicker.h
+++ b/widget/gtk/nsFilePicker.h
-@@ -72,6 +72,12 @@ class nsFilePicker : public nsBaseFilePicker {
+@@ -74,6 +74,12 @@ class nsFilePicker : public nsBaseFilePicker {
private:
static nsIFile* mPrevDisplayDirectory;
+ bool kdeRunning();
+ bool getKdeRunning();
-+ NS_IMETHODIMP kdeFileDialog(PRInt16 *aReturn);
-+ NS_IMETHODIMP kdeAppsDialog(PRInt16 *aReturn);
-+ nsCString kdeMakeFilter( int index );
++ NS_IMETHODIMP kdeFileDialog(nsIFilePicker::ResultCode* aReturn);
++ NS_IMETHODIMP kdeAppsDialog(nsIFilePicker::ResultCode* aReturn);
++ nsCString kdeMakeFilter(int index);
+
void* GtkFileChooserNew(const gchar* title, GtkWindow* parent,
GtkFileChooserAction action,
const gchar* accept_label);
diff --git a/xpcom/components/ManifestParser.cpp b/xpcom/components/ManifestParser.cpp
-index f3d0055f2cc6..d13543ab5221 100644
+index 88ee06d78db6..834d6a2d353c 100644
--- a/xpcom/components/ManifestParser.cpp
+++ b/xpcom/components/ManifestParser.cpp
@@ -43,6 +43,7 @@
@@ -1616,7 +1529,7 @@ index f3d0055f2cc6..d13543ab5221 100644
using namespace mozilla;
-@@ -402,6 +403,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
+@@ -394,6 +395,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
constexpr auto kOs = u"os"_ns;
constexpr auto kOsVersion = u"osversion"_ns;
constexpr auto kABI = u"abi"_ns;
@@ -1624,7 +1537,7 @@ index f3d0055f2cc6..d13543ab5221 100644
constexpr auto kProcess = u"process"_ns;
#if defined(MOZ_WIDGET_ANDROID)
constexpr auto kTablet = u"tablet"_ns;
-@@ -461,6 +463,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
+@@ -453,6 +455,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
}
nsAutoString osVersion;
@@ -1632,7 +1545,7 @@ index f3d0055f2cc6..d13543ab5221 100644
#if defined(XP_WIN)
# pragma warning(push)
# pragma warning(disable : 4996) // VC12+ deprecates GetVersionEx
-@@ -469,14 +472,17 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
+@@ -461,14 +464,17 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
nsTextFormatter::ssprintf(osVersion, u"%ld.%ld", info.dwMajorVersion,
info.dwMinorVersion);
}
@@ -1649,16 +1562,16 @@ index f3d0055f2cc6..d13543ab5221 100644
+ desktop = nsKDEUtils::kdeSession() ? u"kde"_ns : u"gnome"_ns;
#elif defined(MOZ_WIDGET_ANDROID)
bool isTablet = false;
- if (mozilla::AndroidBridge::Bridge()) {
-@@ -484,6 +490,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
- "android/os/Build$VERSION", "RELEASE", osVersion);
+ if (jni::IsAvailable()) {
+@@ -476,6 +482,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
+ osVersion.Assign(release->ToString());
isTablet = java::GeckoAppShell::IsTablet();
}
+ desktop = u"android"_ns;
#endif
if (XRE_IsContentProcess()) {
-@@ -588,6 +595,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
+@@ -576,6 +583,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
: eUnspecified;
#endif
int flags = 0;
@@ -1666,7 +1579,7 @@ index f3d0055f2cc6..d13543ab5221 100644
while ((token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) &&
ok) {
-@@ -597,6 +605,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
+@@ -585,6 +593,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
if (CheckStringFlag(kApplication, wtoken, appID, stApp) ||
CheckOsFlag(kOs, wtoken, osTarget, stOs) ||
CheckStringFlag(kABI, wtoken, abi, stABI) ||
@@ -1674,17 +1587,16 @@ index f3d0055f2cc6..d13543ab5221 100644
CheckStringFlag(kProcess, wtoken, process, stProcess) ||
CheckVersionFlag(kOsVersion, wtoken, osVersion, stOsVersion) ||
CheckVersionFlag(kAppVersion, wtoken, appVersion, stAppVersion) ||
-@@ -655,7 +664,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
- }
+@@ -644,6 +653,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
if (!ok || stApp == eBad || stAppVersion == eBad ||
-- stGeckoVersion == eBad || stOs == eBad || stOsVersion == eBad ||
-+ stGeckoVersion == eBad || stOs == eBad || stOsVersion == eBad || stDesktop == eBad ||
+ stGeckoVersion == eBad || stOs == eBad || stOsVersion == eBad ||
++ stDesktop == eBad ||
#ifdef MOZ_WIDGET_ANDROID
stTablet == eBad ||
#endif
diff --git a/xpcom/components/moz.build b/xpcom/components/moz.build
-index 6cf78aa9bec5..dfcf9c7697af 100644
+index 95ee64e985ac..9af8f80497b7 100644
--- a/xpcom/components/moz.build
+++ b/xpcom/components/moz.build
@@ -71,6 +71,7 @@ LOCAL_INCLUDES += [
@@ -1696,10 +1608,10 @@ index 6cf78aa9bec5..dfcf9c7697af 100644
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
diff --git a/xpcom/io/nsLocalFileUnix.cpp b/xpcom/io/nsLocalFileUnix.cpp
-index 410fcc19e435..d7c976e0e4b2 100644
+index 08c77360de6f..eedd5bcf86bd 100644
--- a/xpcom/io/nsLocalFileUnix.cpp
+++ b/xpcom/io/nsLocalFileUnix.cpp
-@@ -59,6 +59,7 @@
+@@ -51,6 +51,7 @@
#ifdef MOZ_WIDGET_GTK
# include "nsIGIOService.h"
@@ -1707,7 +1619,7 @@ index 410fcc19e435..d7c976e0e4b2 100644
#endif
#ifdef MOZ_WIDGET_COCOA
-@@ -2102,10 +2103,19 @@ nsLocalFile::Reveal() {
+@@ -2172,10 +2173,18 @@ nsLocalFile::Reveal() {
}
#ifdef MOZ_WIDGET_GTK
@@ -1716,33 +1628,29 @@ index 410fcc19e435..d7c976e0e4b2 100644
- if (!giovfs) {
- return NS_ERROR_FAILURE;
+ url = mPath;
-+ if(nsKDEUtils::kdeSupport()) {
++ if (nsKDEUtils::kdeSupport()) {
+ nsTArray<nsCString> command;
-+ command.AppendElement( "REVEAL"_ns );
-+ command.AppendElement( mPath );
-+ return nsKDEUtils::command( command ) ? NS_OK : NS_ERROR_FAILURE;
++ command.AppendElement("REVEAL"_ns);
++ command.AppendElement(mPath);
++ return nsKDEUtils::command(command) ? NS_OK : NS_ERROR_FAILURE;
}
+
-+ if (!giovfs)
-+ return NS_ERROR_FAILURE;
++ if (!giovfs) return NS_ERROR_FAILURE;
+
return giovfs->RevealFile(this);
#elif defined(MOZ_WIDGET_COCOA)
CFURLRef url;
-@@ -2127,6 +2137,13 @@ nsLocalFile::Launch() {
+@@ -2197,6 +2206,13 @@ nsLocalFile::Launch() {
}
#ifdef MOZ_WIDGET_GTK
-+ if( nsKDEUtils::kdeSupport()) {
++ if (nsKDEUtils::kdeSupport()) {
+ nsTArray<nsCString> command;
-+ command.AppendElement( "OPEN"_ns );
-+ command.AppendElement( mPath );
-+ return nsKDEUtils::command( command ) ? NS_OK : NS_ERROR_FAILURE;
++ command.AppendElement("OPEN"_ns);
++ command.AppendElement(mPath);
++ return nsKDEUtils::command(command) ? NS_OK : NS_ERROR_FAILURE;
+ }
+
nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
if (!giovfs) {
return NS_ERROR_FAILURE;
---
-2.37.3
-
diff --git a/waterfox-g/debian/patches/global_menu.patch b/waterfox-g/debian/patches/global_menu.patch
deleted file mode 100644
index 9f9d487..0000000
--- a/waterfox-g/debian/patches/global_menu.patch
+++ /dev/null
@@ -1,5382 +0,0 @@
-Source: https://bazaar.launchpad.net/~mozillateam/firefox/firefox.bionic/view/1484/debian/patches/unity-menubar.patch
-Author: Olivier Tilloy <olivier.tilloy@canonical.com>
-
-diff --git a/browser/base/content/browser-menubar.inc b/browser/base/content/browser-menubar.inc
-index 1adea92456c7..9cb91a4befdf 100644
---- a/browser/base/content/browser-menubar.inc
-+++ b/browser/base/content/browser-menubar.inc
-@@ -7,7 +7,12 @@
- # On macOS, we don't track whether activation of the native menubar happened
- # with the keyboard.
- #ifndef XP_MACOSX
-- onpopupshowing="if (event.target.parentNode.parentNode == this)
-+ onpopupshowing="if (event.target.parentNode.parentNode == this &amp;&amp;
-+#ifdef MOZ_WIDGET_GTK
-+ document.documentElement.getAttribute('shellshowingmenubar') != 'true')
-+#else
-+ true)
-+#endif
- this.setAttribute('openedwithkey',
- event.target.parentNode.openedWithKey);"
- #endif
-diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
-index d72ed6495612..eb4af8d23195 100644
---- a/browser/base/content/browser.js
-+++ b/browser/base/content/browser.js
-@@ -6481,11 +6481,18 @@ function onViewToolbarsPopupShowing(aEvent, aInsertPoint) {
- MozXULElement.insertFTLIfNeeded("browser/toolbarContextMenu.ftl");
- let firstMenuItem = aInsertPoint || popup.firstElementChild;
- let toolbarNodes = gNavToolbox.querySelectorAll("toolbar");
-+
-+ let shellShowingMenubar = document.documentElement.getAttribute("shellshowingmenubar") == "true";
-+
- for (let toolbar of toolbarNodes) {
- if (!toolbar.hasAttribute("toolbarname")) {
- continue;
- }
-
-+ if (shellShowingMenubar && toolbar.id == "toolbar-menubar") {
-+ continue;
-+ }
-+
- if (toolbar.id == "PersonalToolbar") {
- let menu = BookmarkingUI.buildBookmarksToolbarSubmenu(toolbar);
- popup.insertBefore(menu, firstMenuItem);
-diff --git a/browser/components/places/content/places.xhtml b/browser/components/places/content/places.xhtml
-index e19a58029828..4271915765c6 100644
---- a/browser/components/places/content/places.xhtml
-+++ b/browser/components/places/content/places.xhtml
-@@ -166,6 +166,7 @@
- #else
- <menubar id="placesMenu">
- <menu class="menu-iconic" data-l10n-id="places-organize-button"
-+ _moz-menubarkeeplocal="true"
- #endif
- id="organizeButton">
- <menupopup id="organizeButtonPopup">
-diff --git a/dom/xul/XULPopupElement.cpp b/dom/xul/XULPopupElement.cpp
-index b6a50a3e2408..cda8315b0ef8 100644
---- a/dom/xul/XULPopupElement.cpp
-+++ b/dom/xul/XULPopupElement.cpp
-@@ -207,6 +207,10 @@ void XULPopupElement::GetState(nsString& aState) {
- // set this here in case there's no frame for the popup
- aState.AssignLiteral("closed");
-
-+#ifdef MOZ_WIDGET_GTK
-+ nsAutoString nativeState;
-+#endif
-+
- if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
- switch (pm->GetPopupState(this)) {
- case ePopupShown:
-@@ -229,6 +233,11 @@ void XULPopupElement::GetState(nsString& aState) {
- break;
- }
- }
-+#ifdef MOZ_WIDGET_GTK
-+ else if (GetAttr(kNameSpaceID_None, nsGkAtoms::_moz_nativemenupopupstate, nativeState)) {
-+ aState = nativeState;
-+ }
-+#endif
- }
-
- nsINode* XULPopupElement::GetTriggerNode() const {
-diff --git a/dom/xul/moz.build b/dom/xul/moz.build
-index bb7e4b08d924..42d4bd9b654f 100644
---- a/dom/xul/moz.build
-+++ b/dom/xul/moz.build
-@@ -82,4 +82,9 @@ LOCAL_INCLUDES += [
-
- include("/ipc/chromium/chromium-config.mozbuild")
-
-+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
-+ LOCAL_INCLUDES += [
-+ "/widget/gtk",
-+ ]
-+
- FINAL_LIBRARY = "xul"
-diff --git a/layout/build/moz.build b/layout/build/moz.build
-index c7869a01a373..2cbdb021b3b7 100644
---- a/layout/build/moz.build
-+++ b/layout/build/moz.build
-@@ -70,6 +70,10 @@ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
- "/dom/system",
- "/dom/system/android",
- ]
-+elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
-+ LOCAL_INCLUDES += [
-+ "/widget/gtk",
-+ ]
-
- XPCOM_MANIFESTS += [
- "components.conf",
-diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
-index 30641d9727cf..c8382f50ab28 100644
---- a/modules/libpref/init/all.js
-+++ b/modules/libpref/init/all.js
-@@ -188,6 +188,9 @@ pref("dom.mouseevent.click.hack.use_legacy_non-primary_dispatch", "");
- // Fastback caching - if this pref is negative, then we calculate the number
- // of content viewers to cache based on the amount of available memory.
- pref("browser.sessionhistory.max_total_viewers", -1);
-+#ifdef MOZ_WIDGET_GTK
-+pref("ui.use_unity_menubar", true);
-+#endif
-
- pref("browser.display.force_inline_alttext", false); // true = force ALT text for missing images to be layed out inline
- // 0 = no external leading,
-diff --git a/toolkit/content/xul.css b/toolkit/content/xul.css
-index 70bfc2047c05..6b7938a6a7a5 100644
---- a/toolkit/content/xul.css
-+++ b/toolkit/content/xul.css
-@@ -226,6 +226,13 @@ toolbox {
- }
- }
-
-+@media (-moz-platform: linux) {
-+*|*:root[shellshowingmenubar="true"]
-+toolbar[type="menubar"]:not([customizing="true"]) {
-+ display: none !important;
-+}
-+}
-+
- toolbarspring {
- -moz-box-flex: 1000;
- }
-diff --git a/widget/gtk/NativeMenuSupport.cpp b/widget/gtk/NativeMenuSupport.cpp
-index 4360867fff3f..c3a69f31b1d3 100644
---- a/widget/gtk/NativeMenuSupport.cpp
-+++ b/widget/gtk/NativeMenuSupport.cpp
-@@ -7,6 +7,8 @@
-
- #include "MainThreadUtils.h"
- #include "NativeMenuGtk.h"
-+#include "nsINativeMenuService.h"
-+#include "nsServiceManagerUtils.h"
-
- namespace mozilla::widget {
-
-@@ -14,7 +16,14 @@ void NativeMenuSupport::CreateNativeMenuBar(nsIWidget* aParent,
- dom::Element* aMenuBarElement) {
- MOZ_RELEASE_ASSERT(NS_IsMainThread(),
- "Attempting to create native menu bar on wrong thread!");
-- // TODO
-+
-+ nsCOMPtr<nsINativeMenuService> nms =
-+ do_GetService("@mozilla.org/widget/nativemenuservice;1");
-+ if (!nms) {
-+ return;
-+ }
-+
-+ nms->CreateNativeMenuBar(aParent, aMenuBarElement);
- }
-
- already_AddRefed<NativeMenu> NativeMenuSupport::CreateNativeContextMenu(
-diff --git a/widget/gtk/NativeMenuSupport.h b/widget/gtk/NativeMenuSupport.h
-new file mode 100644
-index 000000000000..0843d45185e5
---- /dev/null
-+++ b/widget/gtk/NativeMenuSupport.h
-@@ -0,0 +1,31 @@
-+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-+/* 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 mozilla_widget_NativeMenuSupport_h
-+#define mozilla_widget_NativeMenuSupport_h
-+
-+class nsIWidget;
-+
-+namespace mozilla {
-+
-+namespace dom {
-+class Element;
-+}
-+
-+namespace widget {
-+
-+class NativeMenuSupport final {
-+public:
-+ // Given a top-level window widget and a menu bar DOM node, sets up native
-+ // menus. Once created, native menus are controlled via the DOM, including
-+ // destruction.
-+ static void CreateNativeMenuBar(nsIWidget* aParent,
-+ dom::Element* aMenuBarElement);
-+};
-+
-+} // namespace widget
-+} // namespace mozilla
-+
-+#endif // mozilla_widget_NativeMenuSupport_h
-diff --git a/widget/gtk/components.conf b/widget/gtk/components.conf
-index 61714d8b8190..1738093137f2 100644
---- a/widget/gtk/components.conf
-+++ b/widget/gtk/components.conf
-@@ -117,6 +117,14 @@ Classes = [
- 'headers': ['/widget/gtk/nsUserIdleServiceGTK.h'],
- 'constructor': 'nsUserIdleServiceGTK::GetInstance',
- },
-+ {
-+ 'cid': '{0b3fe5aa-bc72-4303-85ae-76365df1251d}',
-+ 'contract_ids': ['@mozilla.org/widget/nativemenuservice;1'],
-+ 'singleton': True,
-+ 'type': 'nsNativeMenuService',
-+ 'constructor': 'nsNativeMenuService::GetInstanceForServiceManager',
-+ 'headers': ['/widget/gtk/nsNativeMenuService.h'],
-+ },
- ]
-
- if defined('NS_PRINTING'):
-diff --git a/widget/gtk/moz.build b/widget/gtk/moz.build
-index 5218c7df06aa..bf64f7ebdc65 100644
---- a/widget/gtk/moz.build
-+++ b/widget/gtk/moz.build
-@@ -77,6 +77,15 @@ UNIFIED_SOURCES += [
-
- SOURCES += [
- "MediaKeysEventSourceFactory.cpp",
-+ "nsDbusmenu.cpp",
-+ "nsMenu.cpp", # conflicts with X11 headers
-+ "nsMenuBar.cpp",
-+ "nsMenuContainer.cpp",
-+ "nsMenuItem.cpp",
-+ "nsMenuObject.cpp",
-+ "nsMenuSeparator.cpp",
-+ "nsNativeMenuDocListener.cpp",
-+ "nsNativeMenuService.cpp",
- "nsNativeThemeGTK.cpp", # conflicts with X11 headers
- "nsWindow.cpp", # conflicts with X11 headers
- "WaylandVsyncSource.cpp", # conflicts with X11 headers
-@@ -156,6 +165,7 @@ LOCAL_INCLUDES += [
- "/layout/base",
- "/layout/forms",
- "/layout/generic",
-+ "/layout/style",
- "/layout/xul",
- "/other-licenses/atk-1.0",
- "/third_party/cups/include",
-diff --git a/widget/gtk/nsDbusmenu.cpp b/widget/gtk/nsDbusmenu.cpp
-new file mode 100644
-index 000000000000..f3a1c4400bf2
---- /dev/null
-+++ b/widget/gtk/nsDbusmenu.cpp
-@@ -0,0 +1,61 @@
-+/* -*- 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/. */
-+
-+#include "nsDbusmenu.h"
-+#include "prlink.h"
-+#include "mozilla/ArrayUtils.h"
-+
-+#define FUNC(name, type, params) \
-+nsDbusmenuFunctions::_##name##_fn nsDbusmenuFunctions::s_##name;
-+DBUSMENU_GLIB_FUNCTIONS
-+DBUSMENU_GTK_FUNCTIONS
-+#undef FUNC
-+
-+static PRLibrary *gDbusmenuGlib = nullptr;
-+static PRLibrary *gDbusmenuGtk = nullptr;
-+
-+typedef void (*nsDbusmenuFunc)();
-+struct nsDbusmenuDynamicFunction {
-+ const char *functionName;
-+ nsDbusmenuFunc *function;
-+};
-+
-+/* static */ nsresult
-+nsDbusmenuFunctions::Init()
-+{
-+#define FUNC(name, type, params) \
-+ { #name, (nsDbusmenuFunc *)&nsDbusmenuFunctions::s_##name },
-+ static const nsDbusmenuDynamicFunction kDbusmenuGlibSymbols[] = {
-+ DBUSMENU_GLIB_FUNCTIONS
-+ };
-+ static const nsDbusmenuDynamicFunction kDbusmenuGtkSymbols[] = {
-+ DBUSMENU_GTK_FUNCTIONS
-+ };
-+
-+#define LOAD_LIBRARY(symbol, name) \
-+ if (!g##symbol) { \
-+ g##symbol = PR_LoadLibrary(name); \
-+ if (!g##symbol) { \
-+ return NS_ERROR_FAILURE; \
-+ } \
-+ } \
-+ for (uint32_t i = 0; i < mozilla::ArrayLength(k##symbol##Symbols); ++i) { \
-+ *k##symbol##Symbols[i].function = \
-+ PR_FindFunctionSymbol(g##symbol, k##symbol##Symbols[i].functionName); \
-+ if (!*k##symbol##Symbols[i].function) { \
-+ return NS_ERROR_FAILURE; \
-+ } \
-+ }
-+
-+ LOAD_LIBRARY(DbusmenuGlib, "libdbusmenu-glib.so.4")
-+#ifdef MOZ_WIDGET_GTK
-+ LOAD_LIBRARY(DbusmenuGtk, "libdbusmenu-gtk3.so.4")
-+#endif
-+#undef LOAD_LIBRARY
-+
-+ return NS_OK;
-+}
-diff --git a/widget/gtk/nsDbusmenu.h b/widget/gtk/nsDbusmenu.h
-new file mode 100644
-index 000000000000..8d46a0d27bdb
---- /dev/null
-+++ 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:
-+ */
-+/* 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 __nsDbusmenu_h__
-+#define __nsDbusmenu_h__
-+
-+#include "nsError.h"
-+
-+#include <glib.h>
-+#include <gdk/gdk.h>
-+
-+#define DBUSMENU_GLIB_FUNCTIONS \
-+ FUNC(dbusmenu_menuitem_child_add_position, gboolean, (DbusmenuMenuitem *mi, DbusmenuMenuitem *child, guint position)) \
-+ FUNC(dbusmenu_menuitem_child_append, gboolean, (DbusmenuMenuitem *mi, DbusmenuMenuitem *child)) \
-+ FUNC(dbusmenu_menuitem_child_delete, gboolean, (DbusmenuMenuitem *mi, DbusmenuMenuitem *child)) \
-+ FUNC(dbusmenu_menuitem_get_children, GList*, (DbusmenuMenuitem *mi)) \
-+ FUNC(dbusmenu_menuitem_new, DbusmenuMenuitem*, (void)) \
-+ FUNC(dbusmenu_menuitem_property_get, const gchar*, (DbusmenuMenuitem *mi, const gchar *property)) \
-+ FUNC(dbusmenu_menuitem_property_get_bool, gboolean, (DbusmenuMenuitem *mi, const gchar *property)) \
-+ FUNC(dbusmenu_menuitem_property_remove, void, (DbusmenuMenuitem *mi, const gchar *property)) \
-+ FUNC(dbusmenu_menuitem_property_set, gboolean, (DbusmenuMenuitem *mi, const gchar *property, const gchar *value)) \
-+ FUNC(dbusmenu_menuitem_property_set_bool, gboolean, (DbusmenuMenuitem *mi, const gchar *property, const gboolean value)) \
-+ FUNC(dbusmenu_menuitem_property_set_int, gboolean, (DbusmenuMenuitem *mi, const gchar *property, const gint value)) \
-+ FUNC(dbusmenu_menuitem_show_to_user, void, (DbusmenuMenuitem *mi, guint timestamp)) \
-+ FUNC(dbusmenu_menuitem_take_children, GList*, (DbusmenuMenuitem *mi)) \
-+ FUNC(dbusmenu_server_new, DbusmenuServer*, (const gchar *object)) \
-+ FUNC(dbusmenu_server_set_root, void, (DbusmenuServer *server, DbusmenuMenuitem *root)) \
-+ FUNC(dbusmenu_server_set_status, void, (DbusmenuServer *server, DbusmenuStatus status))
-+
-+#define DBUSMENU_GTK_FUNCTIONS \
-+ FUNC(dbusmenu_menuitem_property_set_image, gboolean, (DbusmenuMenuitem *menuitem, const gchar *property, const GdkPixbuf *data)) \
-+ FUNC(dbusmenu_menuitem_property_set_shortcut, gboolean, (DbusmenuMenuitem *menuitem, guint key, GdkModifierType modifier))
-+
-+typedef struct _DbusmenuMenuitem DbusmenuMenuitem;
-+typedef struct _DbusmenuServer DbusmenuServer;
-+
-+enum DbusmenuStatus {
-+ DBUSMENU_STATUS_NORMAL,
-+ DBUSMENU_STATUS_NOTICE
-+};
-+
-+#define DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU "submenu"
-+#define DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY "children-display"
-+#define DBUSMENU_MENUITEM_PROP_ENABLED "enabled"
-+#define DBUSMENU_MENUITEM_PROP_ICON_DATA "icon-data"
-+#define DBUSMENU_MENUITEM_PROP_LABEL "label"
-+#define DBUSMENU_MENUITEM_PROP_SHORTCUT "shortcut"
-+#define DBUSMENU_MENUITEM_PROP_TYPE "type"
-+#define DBUSMENU_MENUITEM_PROP_TOGGLE_STATE "toggle-state"
-+#define DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE "toggle-type"
-+#define DBUSMENU_MENUITEM_PROP_VISIBLE "visible"
-+#define DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW "about-to-show"
-+#define DBUSMENU_MENUITEM_SIGNAL_EVENT "event"
-+#define DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED "item-activated"
-+#define DBUSMENU_MENUITEM_TOGGLE_CHECK "checkmark"
-+#define DBUSMENU_MENUITEM_TOGGLE_RADIO "radio"
-+#define DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED 1
-+#define DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED 0
-+#define DBUSMENU_SERVER_PROP_DBUS_OBJECT "dbus-object"
-+
-+class nsDbusmenuFunctions
-+{
-+public:
-+ nsDbusmenuFunctions() = delete;
-+
-+ static nsresult Init();
-+
-+#define FUNC(name, type, params) \
-+ typedef type (*_##name##_fn) params; \
-+ static _##name##_fn s_##name;
-+ DBUSMENU_GLIB_FUNCTIONS
-+ DBUSMENU_GTK_FUNCTIONS
-+#undef FUNC
-+
-+};
-+
-+#define dbusmenu_menuitem_child_add_position nsDbusmenuFunctions::s_dbusmenu_menuitem_child_add_position
-+#define dbusmenu_menuitem_child_append nsDbusmenuFunctions::s_dbusmenu_menuitem_child_append
-+#define dbusmenu_menuitem_child_delete nsDbusmenuFunctions::s_dbusmenu_menuitem_child_delete
-+#define dbusmenu_menuitem_get_children nsDbusmenuFunctions::s_dbusmenu_menuitem_get_children
-+#define dbusmenu_menuitem_new nsDbusmenuFunctions::s_dbusmenu_menuitem_new
-+#define dbusmenu_menuitem_property_get nsDbusmenuFunctions::s_dbusmenu_menuitem_property_get
-+#define dbusmenu_menuitem_property_get_bool nsDbusmenuFunctions::s_dbusmenu_menuitem_property_get_bool
-+#define dbusmenu_menuitem_property_remove nsDbusmenuFunctions::s_dbusmenu_menuitem_property_remove
-+#define dbusmenu_menuitem_property_set nsDbusmenuFunctions::s_dbusmenu_menuitem_property_set
-+#define dbusmenu_menuitem_property_set_bool nsDbusmenuFunctions::s_dbusmenu_menuitem_property_set_bool
-+#define dbusmenu_menuitem_property_set_int nsDbusmenuFunctions::s_dbusmenu_menuitem_property_set_int
-+#define dbusmenu_menuitem_show_to_user nsDbusmenuFunctions::s_dbusmenu_menuitem_show_to_user
-+#define dbusmenu_menuitem_take_children nsDbusmenuFunctions::s_dbusmenu_menuitem_take_children
-+#define dbusmenu_server_new nsDbusmenuFunctions::s_dbusmenu_server_new
-+#define dbusmenu_server_set_root nsDbusmenuFunctions::s_dbusmenu_server_set_root
-+#define dbusmenu_server_set_status nsDbusmenuFunctions::s_dbusmenu_server_set_status
-+
-+#define dbusmenu_menuitem_property_set_image nsDbusmenuFunctions::s_dbusmenu_menuitem_property_set_image
-+#define dbusmenu_menuitem_property_set_shortcut nsDbusmenuFunctions::s_dbusmenu_menuitem_property_set_shortcut
-+
-+#endif /* __nsDbusmenu_h__ */
-diff --git a/widget/gtk/nsMenu.cpp b/widget/gtk/nsMenu.cpp
-new file mode 100644
-index 000000000000..255eb390e577
---- /dev/null
-+++ b/widget/gtk/nsMenu.cpp
-@@ -0,0 +1,795 @@
-+/* -*- 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/. */
-+
-+#define _IMPL_NS_LAYOUT
-+
-+#include "mozilla/dom/Document.h"
-+#include "mozilla/dom/Element.h"
-+#include "mozilla/Assertions.h"
-+#include "mozilla/ComputedStyleInlines.h"
-+#include "mozilla/EventDispatcher.h"
-+#include "mozilla/MouseEvents.h"
-+#include "mozilla/PresShell.h"
-+#include "mozilla/PresShellInlines.h"
-+#include "nsComponentManagerUtils.h"
-+#include "nsContentUtils.h"
-+#include "nsCSSValue.h"
-+#include "nsGkAtoms.h"
-+#include "nsGtkUtils.h"
-+#include "nsAtom.h"
-+#include "nsIContent.h"
-+#include "nsIRunnable.h"
-+#include "nsITimer.h"
-+#include "nsString.h"
-+#include "nsStyleStruct.h"
-+#include "nsThreadUtils.h"
-+
-+#include "nsNativeMenuDocListener.h"
-+
-+#include <glib-object.h>
-+
-+#include "nsMenu.h"
-+
-+using namespace mozilla;
-+
-+class nsMenuContentInsertedEvent : public Runnable
-+{
-+public:
-+ nsMenuContentInsertedEvent(nsMenu *aMenu,
-+ nsIContent *aContainer,
-+ nsIContent *aChild,
-+ nsIContent *aPrevSibling) :
-+ Runnable("nsMenuContentInsertedEvent"),
-+ mWeakMenu(aMenu),
-+ mContainer(aContainer),
-+ mChild(aChild),
-+ mPrevSibling(aPrevSibling) { }
-+
-+ NS_IMETHODIMP Run()
-+ {
-+ if (!mWeakMenu) {
-+ return NS_OK;
-+ }
-+
-+ static_cast<nsMenu *>(mWeakMenu.get())->HandleContentInserted(mContainer,
-+ mChild,
-+ mPrevSibling);
-+ return NS_OK;
-+ }
-+
-+private:
-+ nsWeakMenuObject mWeakMenu;
-+
-+ nsCOMPtr<nsIContent> mContainer;
-+ nsCOMPtr<nsIContent> mChild;
-+ nsCOMPtr<nsIContent> mPrevSibling;
-+};
-+
-+class nsMenuContentRemovedEvent : public Runnable
-+{
-+public:
-+ nsMenuContentRemovedEvent(nsMenu *aMenu,
-+ nsIContent *aContainer,
-+ nsIContent *aChild) :
-+ Runnable("nsMenuContentRemovedEvent"),
-+ mWeakMenu(aMenu),
-+ mContainer(aContainer),
-+ mChild(aChild) { }
-+
-+ NS_IMETHODIMP Run()
-+ {
-+ if (!mWeakMenu) {
-+ return NS_OK;
-+ }
-+
-+ static_cast<nsMenu *>(mWeakMenu.get())->HandleContentRemoved(mContainer,
-+ mChild);
-+ return NS_OK;
-+ }
-+
-+private:
-+ nsWeakMenuObject mWeakMenu;
-+
-+ nsCOMPtr<nsIContent> mContainer;
-+ nsCOMPtr<nsIContent> mChild;
-+};
-+
-+static void
-+DispatchMouseEvent(nsIContent *aTarget, mozilla::EventMessage aMsg)
-+{
-+ if (!aTarget) {
-+ return;
-+ }
-+
-+ WidgetMouseEvent event(true, aMsg, nullptr, WidgetMouseEvent::eReal);
-+ EventDispatcher::Dispatch(aTarget, nullptr, &event);
-+}
-+
-+void
-+nsMenu::SetPopupState(EPopupState aState)
-+{
-+ mPopupState = aState;
-+
-+ if (!mPopupContent) {
-+ return;
-+ }
-+
-+ nsAutoString state;
-+ switch (aState) {
-+ case ePopupState_Showing:
-+ state.Assign(u"showing"_ns);
-+ break;
-+ case ePopupState_Open:
-+ state.Assign(u"open"_ns);
-+ break;
-+ case ePopupState_Hiding:
-+ state.Assign(u"hiding"_ns);
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ if (state.IsEmpty()) {
-+ mPopupContent->AsElement()->UnsetAttr(
-+ kNameSpaceID_None, nsGkAtoms::_moz_nativemenupopupstate,
-+ false);
-+ } else {
-+ mPopupContent->AsElement()->SetAttr(
-+ kNameSpaceID_None, nsGkAtoms::_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,
-+ guint timestamp,
-+ gpointer user_data)
-+{
-+ nsMenu *self = static_cast<nsMenu *>(user_data);
-+
-+ nsAutoCString event(name);
-+
-+ if (event.Equals("closed"_ns)) {
-+ self->OnClose();
-+ return;
-+ }
-+
-+ if (event.Equals("opened"_ns)) {
-+ self->OnOpen();
-+ return;
-+ }
-+}
-+
-+void
-+nsMenu::MaybeAddPlaceholderItem()
-+{
-+ MOZ_ASSERT(!IsInBatchedUpdate(),
-+ "Shouldn't be modifying the native menu structure now");
-+
-+ GList *children = dbusmenu_menuitem_get_children(GetNativeData());
-+ if (!children) {
-+ MOZ_ASSERT(!mPlaceholderItem);
-+
-+ mPlaceholderItem = dbusmenu_menuitem_new();
-+ if (!mPlaceholderItem) {
-+ return;
-+ }
-+
-+ dbusmenu_menuitem_property_set_bool(mPlaceholderItem,
-+ DBUSMENU_MENUITEM_PROP_VISIBLE,
-+ false);
-+
-+ MOZ_ALWAYS_TRUE(
-+ dbusmenu_menuitem_child_append(GetNativeData(), mPlaceholderItem));
-+ }
-+}
-+
-+void
-+nsMenu::EnsureNoPlaceholderItem()
-+{
-+ MOZ_ASSERT(!IsInBatchedUpdate(),
-+ "Shouldn't be modifying the native menu structure now");
-+
-+ if (!mPlaceholderItem) {
-+ return;
-+ }
-+
-+ 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 (mNeedsRebuild) {
-+ Build();
-+ }
-+
-+ nsWeakMenuObject self(this);
-+ nsCOMPtr<nsIContent> origPopupContent(mPopupContent);
-+ {
-+ nsNativeMenuDocListener::BlockUpdatesScope updatesBlocker;
-+
-+ SetPopupState(ePopupState_Showing);
-+ DispatchMouseEvent(mPopupContent, eXULPopupShowing);
-+
-+ ContentNode()->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::open,
-+ u"true"_ns, true);
-+ }
-+
-+ if (!self) {
-+ // We were deleted!
-+ return;
-+ }
-+
-+ // I guess that the popup could have changed
-+ if (origPopupContent != mPopupContent) {
-+ return;
-+ }
-+
-+ nsNativeMenuDocListener::BlockUpdatesScope updatesBlocker;
-+
-+ size_t count = ChildCount();
-+ for (size_t i = 0; i < count; ++i) {
-+ ChildAt(i)->ContainerIsOpening();
-+ }
-+
-+ SetPopupState(ePopupState_Open);
-+ DispatchMouseEvent(mPopupContent, eXULPopupShown);
-+}
-+
-+void
-+nsMenu::Build()
-+{
-+ mNeedsRebuild = false;
-+
-+ while (ChildCount() > 0) {
-+ RemoveChildAt(0);
-+ }
-+
-+ InitializePopup();
-+
-+ if (!mPopupContent) {
-+ return;
-+ }
-+
-+ uint32_t count = mPopupContent->GetChildCount();
-+ for (uint32_t i = 0; i < count; ++i) {
-+ nsIContent *childContent = mPopupContent->GetChildAt_Deprecated(i);
-+
-+ UniquePtr<nsMenuObject> child = CreateChild(childContent);
-+
-+ if (!child) {
-+ continue;
-+ }
-+
-+ AppendChild(std::move(child));
-+ }
-+}
-+
-+void
-+nsMenu::InitializePopup()
-+{
-+ nsCOMPtr<nsIContent> oldPopupContent;
-+ oldPopupContent.swap(mPopupContent);
-+
-+ for (uint32_t i = 0; i < ContentNode()->GetChildCount(); ++i) {
-+ nsIContent *child = ContentNode()->GetChildAt_Deprecated(i);
-+
-+ if (child->NodeInfo()->NameAtom() == nsGkAtoms::menupopup) {
-+ mPopupContent = child;
-+ break;
-+ }
-+ }
-+
-+ if (oldPopupContent == mPopupContent) {
-+ return;
-+ }
-+
-+ // The popup has changed
-+
-+ if (oldPopupContent) {
-+ DocListener()->UnregisterForContentChanges(oldPopupContent);
-+ }
-+
-+ SetPopupState(ePopupState_Closed);
-+
-+ if (!mPopupContent) {
-+ return;
-+ }
-+
-+ DocListener()->RegisterForContentChanges(mPopupContent, this);
-+}
-+
-+void
-+nsMenu::RemoveChildAt(size_t aIndex)
-+{
-+ MOZ_ASSERT(IsInBatchedUpdate() || !mPlaceholderItem,
-+ "Shouldn't have a placeholder menuitem");
-+
-+ nsMenuContainer::RemoveChildAt(aIndex, !IsInBatchedUpdate());
-+ StructureMutated();
-+
-+ if (!IsInBatchedUpdate()) {
-+ MaybeAddPlaceholderItem();
-+ }
-+}
-+
-+void
-+nsMenu::RemoveChild(nsIContent *aChild)
-+{
-+ size_t index = IndexOf(aChild);
-+ if (index == NoIndex) {
-+ return;
-+ }
-+
-+ RemoveChildAt(index);
-+}
-+
-+void
-+nsMenu::InsertChildAfter(UniquePtr<nsMenuObject> aChild,
-+ nsIContent *aPrevSibling)
-+{
-+ if (!IsInBatchedUpdate()) {
-+ EnsureNoPlaceholderItem();
-+ }
-+
-+ nsMenuContainer::InsertChildAfter(std::move(aChild), aPrevSibling,
-+ !IsInBatchedUpdate());
-+ StructureMutated();
-+}
-+
-+void
-+nsMenu::AppendChild(UniquePtr<nsMenuObject> aChild)
-+{
-+ if (!IsInBatchedUpdate()) {
-+ EnsureNoPlaceholderItem();
-+ }
-+
-+ nsMenuContainer::AppendChild(std::move(aChild), !IsInBatchedUpdate());
-+ StructureMutated();
-+}
-+
-+bool
-+nsMenu::IsInBatchedUpdate() const
-+{
-+ return mBatchedUpdateState != eBatchedUpdateState_Inactive;
-+}
-+
-+void
-+nsMenu::StructureMutated()
-+{
-+ if (!IsInBatchedUpdate()) {
-+ return;
-+ }
-+
-+ mBatchedUpdateState = eBatchedUpdateState_DidMutate;
-+}
-+
-+bool
-+nsMenu::CanOpen() const
-+{
-+ 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 (isVisible && !isDisabled);
-+}
-+
-+void
-+nsMenu::HandleContentInserted(nsIContent *aContainer,
-+ nsIContent *aChild,
-+ nsIContent *aPrevSibling)
-+{
-+ if (aContainer == mPopupContent) {
-+ UniquePtr<nsMenuObject> child = CreateChild(aChild);
-+
-+ if (child) {
-+ InsertChildAfter(std::move(child), aPrevSibling);
-+ }
-+ } else {
-+ Build();
-+ }
-+}
-+
-+void
-+nsMenu::HandleContentRemoved(nsIContent *aContainer, nsIContent *aChild)
-+{
-+ if (aContainer == mPopupContent) {
-+ RemoveChild(aChild);
-+ } else {
-+ Build();
-+ }
-+}
-+
-+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);
-+
-+ mNeedsRebuild = true;
-+ mNeedsUpdate = true;
-+
-+ MaybeAddPlaceholderItem();
-+}
-+
-+void
-+nsMenu::Update(const ComputedStyle *aComputedStyle)
-+{
-+ if (mNeedsUpdate) {
-+ mNeedsUpdate = false;
-+
-+ UpdateLabel();
-+ UpdateSensitivity();
-+ }
-+
-+ UpdateVisibility(aComputedStyle);
-+ UpdateIcon(aComputedStyle);
-+}
-+
-+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, nsAtom *aAttribute)
-+{
-+ MOZ_ASSERT(aContent == ContentNode() || aContent == mPopupContent,
-+ "Received an event that wasn't meant for us!");
-+
-+ if (mNeedsUpdate) {
-+ return;
-+ }
-+
-+ if (aContent != ContentNode()) {
-+ return;
-+ }
-+
-+ if (!Parent()->IsBeingDisplayed()) {
-+ mNeedsUpdate = true;
-+ return;
-+ }
-+
-+ 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<const ComputedStyle> style = GetComputedStyle();
-+ UpdateVisibility(style);
-+ } else if (aAttribute == nsGkAtoms::image) {
-+ RefPtr<const ComputedStyle> style = GetComputedStyle();
-+ UpdateIcon(style);
-+ }
-+}
-+
-+void
-+nsMenu::OnContentInserted(nsIContent *aContainer, nsIContent *aChild,
-+ nsIContent *aPrevSibling)
-+{
-+ MOZ_ASSERT(aContainer == ContentNode() || aContainer == mPopupContent,
-+ "Received an event that wasn't meant for us!");
-+
-+ if (mNeedsRebuild) {
-+ return;
-+ }
-+
-+ if (mPopupState == ePopupState_Closed) {
-+ mNeedsRebuild = true;
-+ return;
-+ }
-+
-+ nsContentUtils::AddScriptRunner(
-+ new nsMenuContentInsertedEvent(this, aContainer, aChild,
-+ aPrevSibling));
-+}
-+
-+void
-+nsMenu::OnContentRemoved(nsIContent *aContainer, nsIContent *aChild)
-+{
-+ MOZ_ASSERT(aContainer == ContentNode() || aContainer == mPopupContent,
-+ "Received an event that wasn't meant for us!");
-+
-+ if (mNeedsRebuild) {
-+ return;
-+ }
-+
-+ if (mPopupState == ePopupState_Closed) {
-+ mNeedsRebuild = true;
-+ return;
-+ }
-+
-+ nsContentUtils::AddScriptRunner(
-+ new nsMenuContentRemovedEvent(this, aContainer, aChild));
-+}
-+
-+/*
-+ * Some menus (eg, the History menu in Firefox) refresh themselves on
-+ * opening by removing all children and then re-adding new ones. As this
-+ * happens whilst the menu is opening in Unity, it causes some flickering
-+ * as the menu popup is resized multiple times. To avoid this, we try to
-+ * reuse native menu items when the menu structure changes during a
-+ * batched update. If we can handle menu structure changes from Gecko
-+ * just by updating properties of native menu items (rather than destroying
-+ * and creating new ones), then we eliminate any flickering that occurs as
-+ * the menu is opened. To do this, we don't modify any native menu items
-+ * until the end of the update batch.
-+ */
-+
-+void
-+nsMenu::OnBeginUpdates(nsIContent *aContent)
-+{
-+ 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) {
-+ return;
-+ }
-+
-+ mBatchedUpdateState = eBatchedUpdateState_Active;
-+}
-+
-+void
-+nsMenu::OnEndUpdates()
-+{
-+ if (!IsInBatchedUpdate()) {
-+ return;
-+ }
-+
-+ bool didMutate = mBatchedUpdateState == eBatchedUpdateState_DidMutate;
-+ mBatchedUpdateState = eBatchedUpdateState_Inactive;
-+
-+ /* Optimize for the case where we only had attribute changes */
-+ if (!didMutate) {
-+ return;
-+ }
-+
-+ EnsureNoPlaceholderItem();
-+
-+ GList *nextNativeChild = dbusmenu_menuitem_get_children(GetNativeData());
-+ DbusmenuMenuitem *nextOwnedNativeChild = nullptr;
-+
-+ size_t count = ChildCount();
-+
-+ // Find the first native menu item that is `owned` by a corresponding
-+ // Gecko menuitem
-+ for (size_t i = 0; i < count; ++i) {
-+ if (ChildAt(i)->GetNativeData()) {
-+ nextOwnedNativeChild = ChildAt(i)->GetNativeData();
-+ break;
-+ }
-+ }
-+
-+ // Now iterate over all Gecko menuitems
-+ for (size_t i = 0; i < count; ++i) {
-+ nsMenuObject *child = ChildAt(i);
-+
-+ if (child->GetNativeData()) {
-+ // This child already has a corresponding native menuitem.
-+ // Remove all preceding orphaned native items. At this point, we
-+ // modify the native menu structure.
-+ while (nextNativeChild &&
-+ nextNativeChild->data != nextOwnedNativeChild) {
-+
-+ DbusmenuMenuitem *data =
-+ static_cast<DbusmenuMenuitem *>(nextNativeChild->data);
-+ nextNativeChild = nextNativeChild->next;
-+
-+ MOZ_ALWAYS_TRUE(dbusmenu_menuitem_child_delete(GetNativeData(),
-+ data));
-+ }
-+
-+ if (nextNativeChild) {
-+ nextNativeChild = nextNativeChild->next;
-+ }
-+
-+ // Now find the next native menu item that is `owned`
-+ nextOwnedNativeChild = nullptr;
-+ for (size_t j = i + 1; j < count; ++j) {
-+ if (ChildAt(j)->GetNativeData()) {
-+ nextOwnedNativeChild = ChildAt(j)->GetNativeData();
-+ break;
-+ }
-+ }
-+ } else {
-+ // This child is new, and doesn't have a native menu item. Find one!
-+ if (nextNativeChild &&
-+ nextNativeChild->data != nextOwnedNativeChild) {
-+
-+ DbusmenuMenuitem *data =
-+ static_cast<DbusmenuMenuitem *>(nextNativeChild->data);
-+
-+ if (NS_SUCCEEDED(child->AdoptNativeData(data))) {
-+ nextNativeChild = nextNativeChild->next;
-+ }
-+ }
-+
-+ // There wasn't a suitable one available, so create a new one.
-+ // At this point, we modify the native menu structure.
-+ if (!child->GetNativeData()) {
-+ child->CreateNativeData();
-+ MOZ_ALWAYS_TRUE(
-+ dbusmenu_menuitem_child_add_position(GetNativeData(),
-+ child->GetNativeData(),
-+ i));
-+ }
-+ }
-+ }
-+
-+ while (nextNativeChild) {
-+ DbusmenuMenuitem *data =
-+ static_cast<DbusmenuMenuitem *>(nextNativeChild->data);
-+ nextNativeChild = nextNativeChild->next;
-+
-+ MOZ_ALWAYS_TRUE(dbusmenu_menuitem_child_delete(GetNativeData(), data));
-+ }
-+
-+ MaybeAddPlaceholderItem();
-+}
-+
-+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 = NS_NewTimer();
-+ 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);
-+}
-+
-diff --git a/widget/gtk/nsMenu.h b/widget/gtk/nsMenu.h
-new file mode 100644
-index 000000000000..40244d012290
---- /dev/null
-+++ b/widget/gtk/nsMenu.h
-@@ -0,0 +1,123 @@
-+/* -*- 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 __nsMenu_h__
-+#define __nsMenu_h__
-+
-+#include "mozilla/Attributes.h"
-+#include "mozilla/UniquePtr.h"
-+#include "nsCOMPtr.h"
-+
-+#include "nsDbusmenu.h"
-+#include "nsMenuContainer.h"
-+#include "nsMenuObject.h"
-+
-+#include <glib.h>
-+
-+class nsAtom;
-+class nsIContent;
-+class nsITimer;
-+
-+#define NSMENU_NUMBER_OF_POPUPSTATE_BITS 2U
-+#define NSMENU_NUMBER_OF_FLAGS 4U
-+
-+// This class represents a menu
-+class nsMenu final : public nsMenuContainer
-+{
-+public:
-+ nsMenu(nsMenuContainer *aParent, nsIContent *aContent);
-+ ~nsMenu();
-+
-+ nsMenuObject::EType Type() const override;
-+
-+ bool IsBeingDisplayed() const override;
-+ bool NeedsRebuild() const override;
-+
-+ // Tell the desktop shell to display this menu
-+ void OpenMenu();
-+
-+ // Normally called via the shell, but it's public so that child
-+ // menuitems can do the shells work. Sigh....
-+ void OnClose();
-+
-+private:
-+ friend class nsMenuContentInsertedEvent;
-+ friend class nsMenuContentRemovedEvent;
-+
-+ enum EPopupState {
-+ ePopupState_Closed,
-+ ePopupState_Showing,
-+ ePopupState_Open,
-+ ePopupState_Hiding
-+ };
-+
-+ void SetPopupState(EPopupState aState);
-+
-+ static void DoOpenCallback(nsITimer *aTimer, void *aClosure);
-+ static void menu_event_cb(DbusmenuMenuitem *menu,
-+ const gchar *name,
-+ GVariant *value,
-+ guint timestamp,
-+ gpointer user_data);
-+
-+ // 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();
-+
-+ // Removes a placeholder item if it exists and asserts that this succeeds
-+ void EnsureNoPlaceholderItem();
-+
-+ void OnOpen();
-+ void Build();
-+ void InitializePopup();
-+ 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;
-+
-+ void HandleContentInserted(nsIContent *aContainer,
-+ nsIContent *aChild,
-+ nsIContent *aPrevSibling);
-+ void HandleContentRemoved(nsIContent *aContainer,
-+ nsIContent *aChild);
-+
-+ void InitializeNativeData() override;
-+ void Update(const mozilla::ComputedStyle *aComputedStyle) 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__ */
-diff --git a/widget/gtk/nsMenuBar.cpp b/widget/gtk/nsMenuBar.cpp
-new file mode 100644
-index 000000000000..0dcae6794329
---- /dev/null
-+++ b/widget/gtk/nsMenuBar.cpp
-@@ -0,0 +1,548 @@
-+/* -*- 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/. */
-+
-+#include "mozilla/Assertions.h"
-+#include "mozilla/DebugOnly.h"
-+#include "mozilla/dom/Document.h"
-+#include "mozilla/dom/Element.h"
-+#include "mozilla/dom/Event.h"
-+#include "mozilla/dom/KeyboardEvent.h"
-+#include "mozilla/dom/KeyboardEventBinding.h"
-+#include "mozilla/Preferences.h"
-+#include "nsContentUtils.h"
-+#include "nsIDOMEventListener.h"
-+#include "nsIRunnable.h"
-+#include "nsIWidget.h"
-+#include "nsTArray.h"
-+#include "nsUnicharUtils.h"
-+
-+#include "nsMenu.h"
-+#include "nsNativeMenuService.h"
-+
-+#include <gdk/gdk.h>
-+#include <gdk/gdkx.h>
-+#include <glib.h>
-+#include <glib-object.h>
-+
-+#include "nsMenuBar.h"
-+
-+using namespace mozilla;
-+
-+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
-+
-+ DocEventListener(nsMenuBar *aOwner) : mOwner(aOwner) { };
-+
-+private:
-+ ~DocEventListener() { };
-+
-+ nsMenuBar *mOwner;
-+};
-+
-+NS_IMPL_ISUPPORTS(nsMenuBar::DocEventListener, nsIDOMEventListener)
-+
-+NS_IMETHODIMP
-+nsMenuBar::DocEventListener::HandleEvent(dom::Event *aEvent)
-+{
-+ nsAutoString type;
-+ aEvent->GetType(type);
-+
-+ if (type.Equals(u"focus"_ns)) {
-+ mOwner->Focus();
-+ } else if (type.Equals(u"blur"_ns)) {
-+ mOwner->Blur();
-+ }
-+
-+ RefPtr<dom::KeyboardEvent> keyEvent = aEvent->AsKeyboardEvent();
-+ if (!keyEvent) {
-+ return NS_OK;
-+ }
-+
-+ if (type.Equals(u"keypress"_ns)) {
-+ return mOwner->Keypress(keyEvent);
-+ } else if (type.Equals(u"keydown"_ns)) {
-+ return mOwner->KeyDown(keyEvent);
-+ } else if (type.Equals(u"keyup"_ns)) {
-+ 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("/com/canonical/menu/"_ns);
-+ 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;
-+ }
-+
-+ 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::KeyboardEvent_Binding::DOM_VK_SHIFT) {
-+ mAccessKeyMask = eModifierShift;
-+ } else if (mAccessKey == dom::KeyboardEvent_Binding::DOM_VK_CONTROL) {
-+ mAccessKeyMask = eModifierCtrl;
-+ } else if (mAccessKey == dom::KeyboardEvent_Binding::DOM_VK_ALT) {
-+ mAccessKeyMask = eModifierAlt;
-+ } else if (mAccessKey == dom::KeyboardEvent_Binding::DOM_VK_META) {
-+ mAccessKeyMask = eModifierMeta;
-+ } else {
-+ mAccessKeyMask = eModifierAlt;
-+ }
-+
-+ return NS_OK;
-+}
-+
-+void
-+nsMenuBar::Build()
-+{
-+ uint32_t count = ContentNode()->GetChildCount();
-+ for (uint32_t i = 0; i < count; ++i) {
-+ nsIContent *childContent = ContentNode()->GetChildAt_Deprecated(i);
-+
-+ UniquePtr<nsMenuObject> child = CreateChild(childContent);
-+
-+ if (!child) {
-+ continue;
-+ }
-+
-+ AppendChild(std::move(child));
-+ }
-+}
-+
-+void
-+nsMenuBar::DisconnectDocumentEventListeners()
-+{
-+ mDocument->RemoveEventListener(u"focus"_ns,
-+ mEventListener,
-+ true);
-+ mDocument->RemoveEventListener(u"blur"_ns,
-+ mEventListener,
-+ true);
-+ mDocument->RemoveEventListener(u"keypress"_ns,
-+ mEventListener,
-+ false);
-+ mDocument->RemoveEventListener(u"keydown"_ns,
-+ mEventListener,
-+ false);
-+ mDocument->RemoveEventListener(u"keyup"_ns,
-+ mEventListener,
-+ false);
-+}
-+
-+void
-+nsMenuBar::SetShellShowingMenuBar(bool aShowing)
-+{
-+ ContentNode()->OwnerDoc()->GetRootElement()->SetAttr(
-+ kNameSpaceID_None, nsGkAtoms::shellshowingmenubar,
-+ aShowing ? u"true"_ns : u"false"_ns,
-+ true);
-+}
-+
-+void
-+nsMenuBar::Focus()
-+{
-+ ContentNode()->AsElement()->SetAttr(kNameSpaceID_None,
-+ nsGkAtoms::openedwithkey,
-+ u"false"_ns, true);
-+}
-+
-+void
-+nsMenuBar::Blur()
-+{
-+ // We do this here in case we lose focus before getting the
-+ // keyup event, which leaves the menubar state looking like
-+ // the alt key is stuck down
-+ dbusmenu_server_set_status(mServer, DBUSMENU_STATUS_NORMAL);
-+}
-+
-+nsMenuBar::ModifierFlags
-+nsMenuBar::GetModifiersFromEvent(dom::KeyboardEvent *aEvent)
-+{
-+ ModifierFlags modifiers = static_cast<ModifierFlags>(0);
-+
-+ if (aEvent->AltKey()) {
-+ modifiers = static_cast<ModifierFlags>(modifiers | eModifierAlt);
-+ }
-+
-+ if (aEvent->ShiftKey()) {
-+ modifiers = static_cast<ModifierFlags>(modifiers | eModifierShift);
-+ }
-+
-+ if (aEvent->CtrlKey()) {
-+ modifiers = static_cast<ModifierFlags>(modifiers | eModifierCtrl);
-+ }
-+
-+ if (aEvent->MetaKey()) {
-+ modifiers = static_cast<ModifierFlags>(modifiers | eModifierMeta);
-+ }
-+
-+ return modifiers;
-+}
-+
-+nsresult
-+nsMenuBar::Keypress(dom::KeyboardEvent *aEvent)
-+{
-+ if (!ShouldHandleKeyEvent(aEvent)) {
-+ return NS_OK;
-+ }
-+
-+ ModifierFlags modifiers = GetModifiersFromEvent(aEvent);
-+ if (((modifiers & mAccessKeyMask) == 0) ||
-+ ((modifiers & ~mAccessKeyMask) != 0)) {
-+ return NS_OK;
-+ }
-+
-+ uint32_t charCode = aEvent->CharCode();
-+ if (charCode == 0) {
-+ return NS_OK;
-+ }
-+
-+ char16_t ch = char16_t(charCode);
-+ char16_t chl = ToLowerCase(ch);
-+ char16_t chu = ToUpperCase(ch);
-+
-+ nsMenuObject *found = nullptr;
-+ uint32_t count = ChildCount();
-+ for (uint32_t i = 0; i < count; ++i) {
-+ nsAutoString 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);
-+ break;
-+ }
-+ }
-+
-+ if (!found || found->Type() != nsMenuObject::eType_Menu) {
-+ return NS_OK;
-+ }
-+
-+ ContentNode()->AsElement()->SetAttr(kNameSpaceID_None,
-+ nsGkAtoms::openedwithkey,
-+ u"true"_ns, true);
-+ static_cast<nsMenu *>(found)->OpenMenu();
-+
-+ aEvent->StopPropagation();
-+ aEvent->PreventDefault();
-+
-+ return NS_OK;
-+}
-+
-+nsresult
-+nsMenuBar::KeyDown(dom::KeyboardEvent *aEvent)
-+{
-+ if (!ShouldHandleKeyEvent(aEvent)) {
-+ return NS_OK;
-+ }
-+
-+ uint32_t keyCode = aEvent->KeyCode();
-+ ModifierFlags modifiers = GetModifiersFromEvent(aEvent);
-+ if ((keyCode != mAccessKey) || ((modifiers & ~mAccessKeyMask) != 0)) {
-+ return NS_OK;
-+ }
-+
-+ dbusmenu_server_set_status(mServer, DBUSMENU_STATUS_NOTICE);
-+
-+ return NS_OK;
-+}
-+
-+nsresult
-+nsMenuBar::KeyUp(dom::KeyboardEvent *aEvent)
-+{
-+ if (!ShouldHandleKeyEvent(aEvent)) {
-+ return NS_OK;
-+ }
-+
-+ uint32_t keyCode = aEvent->KeyCode();
-+ if (keyCode == mAccessKey) {
-+ dbusmenu_server_set_status(mServer, DBUSMENU_STATUS_NORMAL);
-+ }
-+
-+ return NS_OK;
-+}
-+
-+void
-+nsMenuBar::HandleContentInserted(nsIContent *aChild, nsIContent *aPrevSibling)
-+{
-+ UniquePtr<nsMenuObject> child = CreateChild(aChild);
-+
-+ if (!child) {
-+ return;
-+ }
-+
-+ InsertChildAfter(std::move(child), aPrevSibling);
-+}
-+
-+void
-+nsMenuBar::HandleContentRemoved(nsIContent *aChild)
-+{
-+ RemoveChild(aChild);
-+}
-+
-+void
-+nsMenuBar::OnContentInserted(nsIContent *aContainer, nsIContent *aChild,
-+ nsIContent *aPrevSibling)
-+{
-+ MOZ_ASSERT(aContainer == ContentNode(),
-+ "Received an event that wasn't meant for us");
-+
-+ nsContentUtils::AddScriptRunner(
-+ new nsMenuBarContentInsertedEvent(this, aChild, aPrevSibling));
-+}
-+
-+void
-+nsMenuBar::OnContentRemoved(nsIContent *aContainer, nsIContent *aChild)
-+{
-+ MOZ_ASSERT(aContainer == ContentNode(),
-+ "Received an event that wasn't meant for us");
-+
-+ nsContentUtils::AddScriptRunner(
-+ new nsMenuBarContentRemovedEvent(this, aChild));
-+}
-+
-+nsMenuBar::~nsMenuBar()
-+{
-+ nsNativeMenuService *service = nsNativeMenuService::GetSingleton();
-+ if (service) {
-+ service->NotifyNativeMenuBarDestroyed(this);
-+ }
-+
-+ if (ContentNode()) {
-+ SetShellShowingMenuBar(false);
-+ }
-+
-+ // We want to destroy all children before dropping our reference
-+ // to the doc listener
-+ while (ChildCount() > 0) {
-+ RemoveChildAt(0);
-+ }
-+
-+ if (mTopLevel) {
-+ g_object_unref(mTopLevel);
-+ }
-+
-+ if (DocListener()) {
-+ DocListener()->Stop();
-+ }
-+
-+ if (mDocument) {
-+ DisconnectDocumentEventListeners();
-+ }
-+
-+ if (mServer) {
-+ g_object_unref(mServer);
-+ }
-+
-+ MOZ_COUNT_DTOR(nsMenuBar);
-+}
-+
-+/* static */ UniquePtr<nsMenuBar>
-+nsMenuBar::Create(nsIWidget *aParent, nsIContent *aMenuBarNode)
-+{
-+ UniquePtr<nsMenuBar> menubar(new nsMenuBar(aMenuBarNode));
-+ if (NS_FAILED(menubar->Init(aParent))) {
-+ return nullptr;
-+ }
-+
-+ return menubar;
-+}
-+
-+nsMenuObject::EType
-+nsMenuBar::Type() const
-+{
-+ return eType_MenuBar;
-+}
-+
-+bool
-+nsMenuBar::IsBeingDisplayed() const
-+{
-+ return true;
-+}
-+
-+uint32_t
-+nsMenuBar::WindowId() const
-+{
-+ return static_cast<uint32_t>(GDK_WINDOW_XID(gtk_widget_get_window(mTopLevel)));
-+}
-+
-+nsCString
-+nsMenuBar::ObjectPath() const
-+{
-+ gchar *tmp;
-+ g_object_get(mServer, DBUSMENU_SERVER_PROP_DBUS_OBJECT, &tmp, NULL);
-+
-+ nsCString result;
-+ result.Adopt(tmp);
-+
-+ return result;
-+}
-+
-+void
-+nsMenuBar::Activate()
-+{
-+ if (mIsActive) {
-+ return;
-+ }
-+
-+ mIsActive = true;
-+
-+ mDocument->AddEventListener(u"focus"_ns,
-+ mEventListener,
-+ true);
-+ mDocument->AddEventListener(u"blur"_ns,
-+ mEventListener,
-+ true);
-+ mDocument->AddEventListener(u"keypress"_ns,
-+ mEventListener,
-+ false);
-+ mDocument->AddEventListener(u"keydown"_ns,
-+ mEventListener,
-+ false);
-+ mDocument->AddEventListener(u"keyup"_ns,
-+ mEventListener,
-+ false);
-+
-+ // Clear this. Not sure if we really need to though
-+ ContentNode()->AsElement()->SetAttr(kNameSpaceID_None,
-+ nsGkAtoms::openedwithkey,
-+ u"false"_ns, true);
-+
-+ DocListener()->Start();
-+ Build();
-+ SetShellShowingMenuBar(true);
-+}
-+
-+void
-+nsMenuBar::Deactivate()
-+{
-+ if (!mIsActive) {
-+ return;
-+ }
-+
-+ mIsActive = false;
-+
-+ SetShellShowingMenuBar(false);
-+ while (ChildCount() > 0) {
-+ RemoveChildAt(0);
-+ }
-+ DocListener()->Stop();
-+ DisconnectDocumentEventListeners();
-+}
-diff --git a/widget/gtk/nsMenuBar.h b/widget/gtk/nsMenuBar.h
-new file mode 100644
-index 000000000000..7a04316330c1
---- /dev/null
-+++ 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:
-+ */
-+/* 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 __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 <gtk/gtk.h>
-+
-+class nsIContent;
-+class nsIWidget;
-+class nsMenuBarDocEventListener;
-+
-+namespace mozilla {
-+namespace dom {
-+class Document;
-+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
-+ * responsible for telling the desktop shell which object path corresponds
-+ * to a particular window. A menubar and its hierarchy also own a
-+ * nsNativeMenuDocListener.
-+ */
-+class nsMenuBar final : public nsMenuContainer
-+{
-+public:
-+ ~nsMenuBar() override;
-+
-+ static mozilla::UniquePtr<nsMenuBar> Create(nsIWidget *aParent,
-+ nsIContent *aMenuBarNode);
-+
-+ nsMenuObject::EType Type() const override;
-+
-+ bool IsBeingDisplayed() const override;
-+
-+ // Get the native window ID for this menubar
-+ uint32_t WindowId() const;
-+
-+ // Get the object path for this menubar
-+ nsCString ObjectPath() const;
-+
-+ // Get the top-level GtkWindow handle
-+ GtkWidget* TopLevelWindow() { return mTopLevel; }
-+
-+ // Called from the menuservice when the menubar is about to be registered.
-+ // Causes the native menubar to be created, and the XUL menubar to be hidden
-+ void Activate();
-+
-+ // Called from the menuservice when the menubar is no longer registered
-+ // with the desktop shell. Will cause the XUL menubar to be shown again
-+ void Deactivate();
-+
-+private:
-+ class DocEventListener;
-+ friend class nsMenuBarContentInsertedEvent;
-+ friend class nsMenuBarContentRemovedEvent;
-+
-+ enum ModifierFlags {
-+ eModifierShift = (1 << 0),
-+ eModifierCtrl = (1 << 1),
-+ eModifierAlt = (1 << 2),
-+ eModifierMeta = (1 << 3)
-+ };
-+
-+ nsMenuBar(nsIContent *aMenuBarNode);
-+ nsresult Init(nsIWidget *aParent);
-+ void Build();
-+ void DisconnectDocumentEventListeners();
-+ void SetShellShowingMenuBar(bool aShowing);
-+ void Focus();
-+ void Blur();
-+ 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<mozilla::dom::Document> mDocument;
-+ RefPtr<DocEventListener> mEventListener;
-+
-+ uint32_t mAccessKey;
-+ ModifierFlags mAccessKeyMask;
-+ bool mIsActive;
-+};
-+
-+#endif /* __nsMenuBar_h__ */
-diff --git a/widget/gtk/nsMenuContainer.cpp b/widget/gtk/nsMenuContainer.cpp
-new file mode 100644
-index 000000000000..f419201f6338
---- /dev/null
-+++ b/widget/gtk/nsMenuContainer.cpp
-@@ -0,0 +1,170 @@
-+/* -*- 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/. */
-+
-+#include "mozilla/DebugOnly.h"
-+#include "nsGkAtoms.h"
-+#include "nsIContent.h"
-+
-+#include "nsDbusmenu.h"
-+#include "nsMenu.h"
-+#include "nsMenuItem.h"
-+#include "nsMenuSeparator.h"
-+
-+#include "nsMenuContainer.h"
-+
-+using namespace mozilla;
-+
-+const nsMenuContainer::ChildTArray::index_type nsMenuContainer::NoIndex = nsMenuContainer::ChildTArray::NoIndex;
-+
-+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 CreateMenuObject<nsMenuItem>;
-+ } else if (aContent->IsXULElement(nsGkAtoms::menu)) {
-+ return CreateMenuObject<nsMenu>;
-+ } else if (aContent->IsXULElement(nsGkAtoms::menuseparator)) {
-+ return CreateMenuObject<nsMenuSeparator>;
-+ }
-+
-+ return nullptr;
-+}
-+
-+static bool
-+ContentIsSupported(nsIContent *aContent)
-+{
-+ return GetMenuObjectConstructor(aContent) ? true : false;
-+}
-+
-+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
-+ return nullptr;
-+ }
-+
-+ UniquePtr<nsMenuObject> res = ctor(this, aContent);
-+ return res;
-+}
-+
-+size_t
-+nsMenuContainer::IndexOf(nsIContent *aChild) const
-+{
-+ if (!aChild) {
-+ return NoIndex;
-+ }
-+
-+ size_t count = ChildCount();
-+ for (size_t i = 0; i < count; ++i) {
-+ if (ChildAt(i)->ContentNode() == aChild) {
-+ return i;
-+ }
-+ }
-+
-+ return NoIndex;
-+}
-+
-+void
-+nsMenuContainer::RemoveChildAt(size_t aIndex, bool aUpdateNative)
-+{
-+ MOZ_ASSERT(aIndex < ChildCount());
-+
-+ if (aUpdateNative) {
-+ MOZ_ALWAYS_TRUE(
-+ dbusmenu_menuitem_child_delete(GetNativeData(),
-+ ChildAt(aIndex)->GetNativeData()));
-+ }
-+
-+ mChildren.RemoveElementAt(aIndex);
-+}
-+
-+void
-+nsMenuContainer::RemoveChild(nsIContent *aChild, bool aUpdateNative)
-+{
-+ size_t index = IndexOf(aChild);
-+ if (index == NoIndex) {
-+ return;
-+ }
-+
-+ RemoveChildAt(index, aUpdateNative);
-+}
-+
-+void
-+nsMenuContainer::InsertChildAfter(UniquePtr<nsMenuObject> aChild,
-+ nsIContent *aPrevSibling,
-+ bool aUpdateNative)
-+{
-+ size_t index = IndexOf(aPrevSibling);
-+ MOZ_ASSERT(!aPrevSibling || index != NoIndex);
-+
-+ ++index;
-+
-+ if (aUpdateNative) {
-+ aChild->CreateNativeData();
-+ MOZ_ALWAYS_TRUE(
-+ dbusmenu_menuitem_child_add_position(GetNativeData(),
-+ aChild->GetNativeData(),
-+ index));
-+ }
-+
-+ mChildren.InsertElementAt(index, std::move(aChild));
-+}
-+
-+void
-+nsMenuContainer::AppendChild(UniquePtr<nsMenuObject> aChild,
-+ bool aUpdateNative)
-+{
-+ if (aUpdateNative) {
-+ aChild->CreateNativeData();
-+ MOZ_ALWAYS_TRUE(
-+ dbusmenu_menuitem_child_append(GetNativeData(),
-+ aChild->GetNativeData()));
-+ }
-+
-+ mChildren.AppendElement(std::move(aChild));
-+}
-+
-+bool
-+nsMenuContainer::NeedsRebuild() const
-+{
-+ return false;
-+}
-+
-+/* static */ nsIContent*
-+nsMenuContainer::GetPreviousSupportedSibling(nsIContent *aContent)
-+{
-+ do {
-+ aContent = aContent->GetPreviousSibling();
-+ } while (aContent && !ContentIsSupported(aContent));
-+
-+ return aContent;
-+}
-diff --git a/widget/gtk/nsMenuContainer.h b/widget/gtk/nsMenuContainer.h
-new file mode 100644
-index 000000000000..b7e8fa8db46f
---- /dev/null
-+++ 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:
-+ */
-+/* 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 __nsMenuContainer_h__
-+#define __nsMenuContainer_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<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
-+ // in the fully open state, or false otherwise
-+ virtual bool IsBeingDisplayed() const = 0;
-+
-+ // Determine if this container will be rebuilt the next time it opens.
-+ // Returns false by default but can be overridden by subclasses
-+ virtual bool NeedsRebuild() const;
-+
-+ // Return the first previous sibling that is of a type supported by the
-+ // menu system
-+ static nsIContent* GetPreviousSupportedSibling(nsIContent *aContent);
-+
-+ static const ChildTArray::index_type NoIndex;
-+
-+protected:
-+ nsMenuContainer(nsMenuContainer *aParent, nsIContent *aContent);
-+ nsMenuContainer(nsNativeMenuDocListener *aListener, nsIContent *aContent);
-+
-+ // Create a new child element for the specified content node
-+ 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].get(); }
-+
-+ void RemoveChildAt(size_t aIndex, bool aUpdateNative = true);
-+
-+ // Remove the child that owns the specified content node
-+ void RemoveChild(nsIContent *aChild, bool aUpdateNative = true);
-+
-+ // Insert a new child after the child that owns the specified content node
-+ void InsertChildAfter(mozilla::UniquePtr<nsMenuObject> aChild,
-+ nsIContent *aPrevSibling,
-+ bool aUpdateNative = true);
-+
-+ void AppendChild(mozilla::UniquePtr<nsMenuObject> aChild,
-+ bool aUpdateNative = true);
-+
-+private:
-+ ChildTArray mChildren;
-+};
-+
-+#endif /* __nsMenuContainer_h__ */
-diff --git a/widget/gtk/nsMenuItem.cpp b/widget/gtk/nsMenuItem.cpp
-new file mode 100644
-index 000000000000..3a9915e609f5
---- /dev/null
-+++ b/widget/gtk/nsMenuItem.cpp
-@@ -0,0 +1,766 @@
-+/* -*- 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/. */
-+
-+#include "mozilla/ArrayUtils.h"
-+#include "mozilla/Assertions.h"
-+#include "mozilla/dom/Document.h"
-+#include "mozilla/dom/Element.h"
-+#include "mozilla/dom/KeyboardEventBinding.h"
-+#include "mozilla/dom/XULCommandEvent.h"
-+#include "mozilla/Preferences.h"
-+#include "mozilla/TextEvents.h"
-+#include "nsContentUtils.h"
-+#include "nsCRT.h"
-+#include "nsGkAtoms.h"
-+#include "nsGlobalWindowInner.h"
-+#include "nsGtkUtils.h"
-+#include "nsIContent.h"
-+#include "nsIRunnable.h"
-+#include "nsQueryObject.h"
-+#include "nsReadableUtils.h"
-+#include "nsString.h"
-+#include "nsThreadUtils.h"
-+
-+#include "nsMenu.h"
-+#include "nsMenuBar.h"
-+#include "nsMenuContainer.h"
-+#include "nsNativeMenuDocListener.h"
-+
-+#include <gdk/gdk.h>
-+#include <gdk/gdkkeysyms.h>
-+#include <gdk/gdkkeysyms-compat.h>
-+#include <gdk/gdkx.h>
-+#include <gtk/gtk.h>
-+
-+#include "nsMenuItem.h"
-+
-+using namespace mozilla;
-+
-+struct KeyCodeData {
-+ const char* str;
-+ size_t strlength;
-+ uint32_t keycode;
-+};
-+
-+static struct KeyCodeData gKeyCodes[] = {
-+#define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \
-+ { #aDOMKeyName, sizeof(#aDOMKeyName) - 1, aDOMKeyCode },
-+#include "mozilla/VirtualKeyCodeList.h"
-+#undef NS_DEFINE_VK
-+ { nullptr, 0, 0 }
-+};
-+
-+struct KeyPair {
-+ uint32_t DOMKeyCode;
-+ guint GDKKeyval;
-+};
-+
-+//
-+// Netscape keycodes are defined in widget/public/nsGUIEvent.h
-+// GTK keycodes are defined in <gdk/gdkkeysyms.h>
-+//
-+static const KeyPair gKeyPairs[] = {
-+ { NS_VK_CANCEL, GDK_Cancel },
-+ { NS_VK_BACK, GDK_BackSpace },
-+ { NS_VK_TAB, GDK_Tab },
-+ { NS_VK_TAB, GDK_ISO_Left_Tab },
-+ { NS_VK_CLEAR, GDK_Clear },
-+ { NS_VK_RETURN, GDK_Return },
-+ { NS_VK_SHIFT, GDK_Shift_L },
-+ { NS_VK_SHIFT, GDK_Shift_R },
-+ { NS_VK_SHIFT, GDK_Shift_Lock },
-+ { NS_VK_CONTROL, GDK_Control_L },
-+ { NS_VK_CONTROL, GDK_Control_R },
-+ { NS_VK_ALT, GDK_Alt_L },
-+ { NS_VK_ALT, GDK_Alt_R },
-+ { NS_VK_META, GDK_Meta_L },
-+ { NS_VK_META, GDK_Meta_R },
-+
-+ // Assume that Super or Hyper is always mapped to physical Win key.
-+ { NS_VK_WIN, GDK_Super_L },
-+ { NS_VK_WIN, GDK_Super_R },
-+ { NS_VK_WIN, GDK_Hyper_L },
-+ { NS_VK_WIN, GDK_Hyper_R },
-+
-+ // GTK's AltGraph key is similar to Mac's Option (Alt) key. However,
-+ // unfortunately, browsers on Mac are using NS_VK_ALT for it even though
-+ // it's really different from Alt key on Windows.
-+ // On the other hand, GTK's AltGrapsh keys are really different from
-+ // Alt key. However, there is no AltGrapsh key on Windows. On Windows,
-+ // both Ctrl and Alt keys are pressed internally when AltGr key is pressed.
-+ // For some languages' users, AltGraph key is important, so, web
-+ // applications on such locale may want to know AltGraph key press.
-+ // Therefore, we should map AltGr keycode for them only on GTK.
-+ { NS_VK_ALTGR, GDK_ISO_Level3_Shift },
-+ { NS_VK_ALTGR, GDK_ISO_Level5_Shift },
-+ // We assume that Mode_switch is always used for level3 shift.
-+ { NS_VK_ALTGR, GDK_Mode_switch },
-+
-+ { NS_VK_PAUSE, GDK_Pause },
-+ { NS_VK_CAPS_LOCK, GDK_Caps_Lock },
-+ { NS_VK_KANA, GDK_Kana_Lock },
-+ { NS_VK_KANA, GDK_Kana_Shift },
-+ { NS_VK_HANGUL, GDK_Hangul },
-+ // { NS_VK_JUNJA, GDK_XXX },
-+ // { NS_VK_FINAL, GDK_XXX },
-+ { NS_VK_HANJA, GDK_Hangul_Hanja },
-+ { NS_VK_KANJI, GDK_Kanji },
-+ { NS_VK_ESCAPE, GDK_Escape },
-+ { NS_VK_CONVERT, GDK_Henkan },
-+ { NS_VK_NONCONVERT, GDK_Muhenkan },
-+ // { NS_VK_ACCEPT, GDK_XXX },
-+ // { NS_VK_MODECHANGE, GDK_XXX },
-+ { NS_VK_SPACE, GDK_space },
-+ { NS_VK_PAGE_UP, GDK_Page_Up },
-+ { NS_VK_PAGE_DOWN, GDK_Page_Down },
-+ { NS_VK_END, GDK_End },
-+ { NS_VK_HOME, GDK_Home },
-+ { NS_VK_LEFT, GDK_Left },
-+ { NS_VK_UP, GDK_Up },
-+ { NS_VK_RIGHT, GDK_Right },
-+ { NS_VK_DOWN, GDK_Down },
-+ { NS_VK_SELECT, GDK_Select },
-+ { NS_VK_PRINT, GDK_Print },
-+ { NS_VK_EXECUTE, GDK_Execute },
-+ { NS_VK_PRINTSCREEN, GDK_Print },
-+ { NS_VK_INSERT, GDK_Insert },
-+ { NS_VK_DELETE, GDK_Delete },
-+ { NS_VK_HELP, GDK_Help },
-+
-+ // keypad keys
-+ { NS_VK_LEFT, GDK_KP_Left },
-+ { NS_VK_RIGHT, GDK_KP_Right },
-+ { NS_VK_UP, GDK_KP_Up },
-+ { NS_VK_DOWN, GDK_KP_Down },
-+ { NS_VK_PAGE_UP, GDK_KP_Page_Up },
-+ // Not sure what these are
-+ //{ NS_VK_, GDK_KP_Prior },
-+ //{ NS_VK_, GDK_KP_Next },
-+ { NS_VK_CLEAR, GDK_KP_Begin }, // Num-unlocked 5
-+ { NS_VK_PAGE_DOWN, GDK_KP_Page_Down },
-+ { NS_VK_HOME, GDK_KP_Home },
-+ { NS_VK_END, GDK_KP_End },
-+ { NS_VK_INSERT, GDK_KP_Insert },
-+ { NS_VK_DELETE, GDK_KP_Delete },
-+ { NS_VK_RETURN, GDK_KP_Enter },
-+
-+ { NS_VK_NUM_LOCK, GDK_Num_Lock },
-+ { NS_VK_SCROLL_LOCK,GDK_Scroll_Lock },
-+
-+ // Function keys
-+ { NS_VK_F1, GDK_F1 },
-+ { NS_VK_F2, GDK_F2 },
-+ { NS_VK_F3, GDK_F3 },
-+ { NS_VK_F4, GDK_F4 },
-+ { NS_VK_F5, GDK_F5 },
-+ { NS_VK_F6, GDK_F6 },
-+ { NS_VK_F7, GDK_F7 },
-+ { NS_VK_F8, GDK_F8 },
-+ { NS_VK_F9, GDK_F9 },
-+ { NS_VK_F10, GDK_F10 },
-+ { NS_VK_F11, GDK_F11 },
-+ { NS_VK_F12, GDK_F12 },
-+ { NS_VK_F13, GDK_F13 },
-+ { NS_VK_F14, GDK_F14 },
-+ { NS_VK_F15, GDK_F15 },
-+ { NS_VK_F16, GDK_F16 },
-+ { NS_VK_F17, GDK_F17 },
-+ { NS_VK_F18, GDK_F18 },
-+ { NS_VK_F19, GDK_F19 },
-+ { NS_VK_F20, GDK_F20 },
-+ { NS_VK_F21, GDK_F21 },
-+ { NS_VK_F22, GDK_F22 },
-+ { NS_VK_F23, GDK_F23 },
-+ { NS_VK_F24, GDK_F24 },
-+
-+ // context menu key, keysym 0xff67, typically keycode 117 on 105-key (Microsoft)
-+ // x86 keyboards, located between right 'Windows' key and right Ctrl key
-+ { NS_VK_CONTEXT_MENU, GDK_Menu },
-+ { NS_VK_SLEEP, GDK_Sleep },
-+
-+ { NS_VK_ATTN, GDK_3270_Attn },
-+ { NS_VK_CRSEL, GDK_3270_CursorSelect },
-+ { NS_VK_EXSEL, GDK_3270_ExSelect },
-+ { NS_VK_EREOF, GDK_3270_EraseEOF },
-+ { NS_VK_PLAY, GDK_3270_Play },
-+ //{ NS_VK_ZOOM, GDK_XXX },
-+ { NS_VK_PA1, GDK_3270_PA1 },
-+};
-+
-+static guint
-+ConvertGeckoKeyNameToGDKKeyval(nsAString& aKeyName)
-+{
-+ NS_ConvertUTF16toUTF8 keyName(aKeyName);
-+ ToUpperCase(keyName); // We want case-insensitive comparison with data
-+ // stored as uppercase.
-+
-+ uint32_t keyCode = 0;
-+
-+ uint32_t keyNameLength = keyName.Length();
-+ const char* keyNameStr = keyName.get();
-+ for (uint16_t i = 0; i < ArrayLength(gKeyCodes); ++i) {
-+ if (keyNameLength == gKeyCodes[i].strlength &&
-+ !nsCRT::strcmp(gKeyCodes[i].str, keyNameStr)) {
-+ keyCode = gKeyCodes[i].keycode;
-+ break;
-+ }
-+ }
-+
-+ // First, try to handle alphanumeric input, not listed in nsKeycodes:
-+ // most likely, more letters will be getting typed in than things in
-+ // the key list, so we will look through these first.
-+
-+ if (keyCode >= NS_VK_A && keyCode <= NS_VK_Z) {
-+ // gdk and DOM both use the ASCII codes for these keys.
-+ return keyCode;
-+ }
-+
-+ // numbers
-+ if (keyCode >= NS_VK_0 && keyCode <= NS_VK_9) {
-+ // gdk and DOM both use the ASCII codes for these keys.
-+ return keyCode - NS_VK_0 + GDK_0;
-+ }
-+
-+ switch (keyCode) {
-+ // keys in numpad
-+ case NS_VK_MULTIPLY: return GDK_KP_Multiply;
-+ case NS_VK_ADD: return GDK_KP_Add;
-+ case NS_VK_SEPARATOR: return GDK_KP_Separator;
-+ case NS_VK_SUBTRACT: return GDK_KP_Subtract;
-+ case NS_VK_DECIMAL: return GDK_KP_Decimal;
-+ case NS_VK_DIVIDE: return GDK_KP_Divide;
-+ case NS_VK_NUMPAD0: return GDK_KP_0;
-+ case NS_VK_NUMPAD1: return GDK_KP_1;
-+ case NS_VK_NUMPAD2: return GDK_KP_2;
-+ case NS_VK_NUMPAD3: return GDK_KP_3;
-+ case NS_VK_NUMPAD4: return GDK_KP_4;
-+ case NS_VK_NUMPAD5: return GDK_KP_5;
-+ case NS_VK_NUMPAD6: return GDK_KP_6;
-+ case NS_VK_NUMPAD7: return GDK_KP_7;
-+ case NS_VK_NUMPAD8: return GDK_KP_8;
-+ case NS_VK_NUMPAD9: return GDK_KP_9;
-+ // other prinable keys
-+ case NS_VK_SPACE: return GDK_space;
-+ case NS_VK_COLON: return GDK_colon;
-+ case NS_VK_SEMICOLON: return GDK_semicolon;
-+ case NS_VK_LESS_THAN: return GDK_less;
-+ case NS_VK_EQUALS: return GDK_equal;
-+ case NS_VK_GREATER_THAN: return GDK_greater;
-+ case NS_VK_QUESTION_MARK: return GDK_question;
-+ case NS_VK_AT: return GDK_at;
-+ case NS_VK_CIRCUMFLEX: return GDK_asciicircum;
-+ case NS_VK_EXCLAMATION: return GDK_exclam;
-+ case NS_VK_DOUBLE_QUOTE: return GDK_quotedbl;
-+ case NS_VK_HASH: return GDK_numbersign;
-+ case NS_VK_DOLLAR: return GDK_dollar;
-+ case NS_VK_PERCENT: return GDK_percent;
-+ case NS_VK_AMPERSAND: return GDK_ampersand;
-+ case NS_VK_UNDERSCORE: return GDK_underscore;
-+ case NS_VK_OPEN_PAREN: return GDK_parenleft;
-+ case NS_VK_CLOSE_PAREN: return GDK_parenright;
-+ case NS_VK_ASTERISK: return GDK_asterisk;
-+ case NS_VK_PLUS: return GDK_plus;
-+ case NS_VK_PIPE: return GDK_bar;
-+ case NS_VK_HYPHEN_MINUS: return GDK_minus;
-+ case NS_VK_OPEN_CURLY_BRACKET: return GDK_braceleft;
-+ case NS_VK_CLOSE_CURLY_BRACKET: return GDK_braceright;
-+ case NS_VK_TILDE: return GDK_asciitilde;
-+ case NS_VK_COMMA: return GDK_comma;
-+ case NS_VK_PERIOD: return GDK_period;
-+ case NS_VK_SLASH: return GDK_slash;
-+ case NS_VK_BACK_QUOTE: return GDK_grave;
-+ case NS_VK_OPEN_BRACKET: return GDK_bracketleft;
-+ case NS_VK_BACK_SLASH: return GDK_backslash;
-+ case NS_VK_CLOSE_BRACKET: return GDK_bracketright;
-+ case NS_VK_QUOTE: return GDK_apostrophe;
-+ }
-+
-+ // misc other things
-+ for (uint32_t i = 0; i < ArrayLength(gKeyPairs); ++i) {
-+ if (gKeyPairs[i].DOMKeyCode == keyCode) {
-+ return gKeyPairs[i].GDKKeyval;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+class nsMenuItemUncheckSiblingsRunnable final : public Runnable
-+{
-+public:
-+ NS_IMETHODIMP Run()
-+ {
-+ if (mMenuItem) {
-+ static_cast<nsMenuItem *>(mMenuItem.get())->UncheckSiblings();
-+ }
-+ return NS_OK;
-+ }
-+
-+ nsMenuItemUncheckSiblingsRunnable(nsMenuItem *aMenuItem) :
-+ Runnable("nsMenuItemUncheckSiblingsRunnable"),
-+ mMenuItem(aMenuItem) { };
-+
-+private:
-+ nsWeakMenuObject mMenuItem;
-+};
-+
-+bool
-+nsMenuItem::IsCheckboxOrRadioItem() const
-+{
-+ return mType == eMenuItemType_Radio ||
-+ mType == eMenuItemType_CheckBox;
-+}
-+
-+/* static */ void
-+nsMenuItem::item_activated_cb(DbusmenuMenuitem *menuitem,
-+ guint timestamp,
-+ gpointer user_data)
-+{
-+ nsMenuItem *item = static_cast<nsMenuItem *>(user_data);
-+ item->Activate(timestamp);
-+}
-+
-+void
-+nsMenuItem::Activate(uint32_t aTimestamp)
-+{
-+ GdkWindow *window = gtk_widget_get_window(MenuBar()->TopLevelWindow());
-+ gdk_x11_window_set_user_time(
-+ window, std::min(aTimestamp, gdk_x11_get_server_time(window)));
-+
-+ // We do this to avoid mutating our view of the menu until
-+ // after we have finished
-+ 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 ?
-+ u"false"_ns
-+ : u"true"_ns,
-+ true);
-+ }
-+
-+ dom::Document *doc = ContentNode()->OwnerDoc();
-+ ErrorResult rv;
-+ RefPtr<dom::Event> event =
-+ doc->CreateEvent(u"xulcommandevent"_ns,
-+ dom::CallerType::System, rv);
-+ if (!rv.Failed()) {
-+ RefPtr<dom::XULCommandEvent> command = event->AsXULCommandEvent();
-+ if (command) {
-+ command->InitCommandEvent(u"command"_ns, true, true,
-+ nsGlobalWindowInner::Cast(doc->GetInnerWindow()),
-+ 0, false, false, false, false, 0, nullptr, 0, rv);
-+ if (!rv.Failed()) {
-+ event->SetTrusted(true);
-+ ContentNode()->DispatchEvent(*event, rv);
-+ if (rv.Failed()) {
-+ NS_WARNING("Failed to dispatch event");
-+ rv.SuppressException();
-+ }
-+ } else {
-+ NS_WARNING("Failed to initialize command event");
-+ rv.SuppressException();
-+ }
-+ }
-+ } else {
-+ NS_WARNING("CreateEvent failed");
-+ rv.SuppressException();
-+ }
-+
-+ // This kinda sucks, but Unity doesn't send a closed event
-+ // after activating a menuitem
-+ nsMenuObject *ancestor = Parent();
-+ while (ancestor && ancestor->Type() == eType_Menu) {
-+ static_cast<nsMenu *>(ancestor)->OnClose();
-+ ancestor = ancestor->Parent();
-+ }
-+}
-+
-+void
-+nsMenuItem::CopyAttrFromNodeIfExists(nsIContent *aContent, nsAtom *aAttribute)
-+{
-+ nsAutoString value;
-+ if (aContent->AsElement()->GetAttr(kNameSpaceID_None, aAttribute, value)) {
-+ ContentNode()->AsElement()->SetAttr(kNameSpaceID_None, aAttribute,
-+ value, true);
-+ }
-+}
-+
-+void
-+nsMenuItem::UpdateState()
-+{
-+ if (!IsCheckboxOrRadioItem()) {
-+ return;
-+ }
-+
-+ mIsChecked = ContentNode()->AsElement()->AttrValueIs(kNameSpaceID_None,
-+ nsGkAtoms::checked,
-+ nsGkAtoms::_true,
-+ eCaseMatters);
-+ dbusmenu_menuitem_property_set_int(GetNativeData(),
-+ DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
-+ mIsChecked ?
-+ DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED :
-+ DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED);
-+}
-+
-+void
-+nsMenuItem::UpdateTypeAndState()
-+{
-+ static mozilla::dom::Element::AttrValuesArray attrs[] =
-+ { nsGkAtoms::checkbox, nsGkAtoms::radio, nullptr };
-+ 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);
-+ mType = eMenuItemType_CheckBox;
-+ } else if (type == 1) {
-+ dbusmenu_menuitem_property_set(GetNativeData(),
-+ DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE,
-+ DBUSMENU_MENUITEM_TOGGLE_RADIO);
-+ mType = eMenuItemType_Radio;
-+ }
-+
-+ UpdateState();
-+ } else {
-+ dbusmenu_menuitem_property_remove(GetNativeData(),
-+ DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE);
-+ dbusmenu_menuitem_property_remove(GetNativeData(),
-+ DBUSMENU_MENUITEM_PROP_TOGGLE_STATE);
-+ mType = eMenuItemType_Normal;
-+ }
-+}
-+
-+void
-+nsMenuItem::UpdateAccel()
-+{
-+ dom::Document *doc = ContentNode()->GetUncomposedDoc();
-+ if (doc) {
-+ nsCOMPtr<nsIContent> oldKeyContent;
-+ oldKeyContent.swap(mKeyContent);
-+
-+ nsAutoString key;
-+ ContentNode()->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::key,
-+ key);
-+ if (!key.IsEmpty()) {
-+ mKeyContent = doc->GetElementById(key);
-+ }
-+
-+ if (mKeyContent != oldKeyContent) {
-+ if (oldKeyContent) {
-+ DocListener()->UnregisterForContentChanges(oldKeyContent);
-+ }
-+ if (mKeyContent) {
-+ DocListener()->RegisterForContentChanges(mKeyContent, this);
-+ }
-+ }
-+ }
-+
-+ if (!mKeyContent) {
-+ dbusmenu_menuitem_property_remove(GetNativeData(),
-+ DBUSMENU_MENUITEM_PROP_SHORTCUT);
-+ return;
-+ }
-+
-+ nsAutoString modifiers;
-+ mKeyContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers,
-+ modifiers);
-+
-+ uint32_t modifier = 0;
-+
-+ if (!modifiers.IsEmpty()) {
-+ char* str = ToNewUTF8String(modifiers);
-+ char *token = strtok(str, ", \t");
-+ while(token) {
-+ if (nsCRT::strcmp(token, "shift") == 0) {
-+ modifier |= GDK_SHIFT_MASK;
-+ } else if (nsCRT::strcmp(token, "alt") == 0) {
-+ modifier |= GDK_MOD1_MASK;
-+ } else if (nsCRT::strcmp(token, "meta") == 0) {
-+ modifier |= GDK_META_MASK;
-+ } else if (nsCRT::strcmp(token, "control") == 0) {
-+ modifier |= GDK_CONTROL_MASK;
-+ } else if (nsCRT::strcmp(token, "accel") == 0) {
-+ int32_t accel = Preferences::GetInt("ui.key.accelKey");
-+ if (accel == dom::KeyboardEvent_Binding::DOM_VK_META) {
-+ modifier |= GDK_META_MASK;
-+ } else if (accel == dom::KeyboardEvent_Binding::DOM_VK_ALT) {
-+ modifier |= GDK_MOD1_MASK;
-+ } else {
-+ modifier |= GDK_CONTROL_MASK;
-+ }
-+ }
-+
-+ token = strtok(nullptr, ", \t");
-+ }
-+
-+ free(str);
-+ }
-+
-+ nsAutoString keyStr;
-+ mKeyContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::key,
-+ keyStr);
-+
-+ guint key = 0;
-+ if (!keyStr.IsEmpty()) {
-+ key = gdk_unicode_to_keyval(*keyStr.BeginReading());
-+ }
-+
-+ if (key == 0) {
-+ mKeyContent->AsElement()->GetAttr(kNameSpaceID_None,
-+ nsGkAtoms::keycode, keyStr);
-+ if (!keyStr.IsEmpty()) {
-+ key = ConvertGeckoKeyNameToGDKKeyval(keyStr);
-+ }
-+ }
-+
-+ if (key == 0) {
-+ key = GDK_VoidSymbol;
-+ }
-+
-+ if (key != GDK_VoidSymbol) {
-+ dbusmenu_menuitem_property_set_shortcut(GetNativeData(), key,
-+ static_cast<GdkModifierType>(modifier));
-+ } else {
-+ dbusmenu_menuitem_property_remove(GetNativeData(),
-+ DBUSMENU_MENUITEM_PROP_SHORTCUT);
-+ }
-+}
-+
-+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->IsComment()) {
-+ 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);
-+ mNeedsUpdate = true;
-+}
-+
-+void
-+nsMenuItem::UpdateContentAttributes()
-+{
-+ dom::Document *doc = ContentNode()->GetUncomposedDoc();
-+ if (!doc) {
-+ return;
-+ }
-+
-+ nsAutoString command;
-+ ContentNode()->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::command,
-+ command);
-+ if (command.IsEmpty()) {
-+ return;
-+ }
-+
-+ nsCOMPtr<nsIContent> commandContent = doc->GetElementById(command);
-+ if (!commandContent) {
-+ return;
-+ }
-+
-+ if (commandContent->AsElement()->AttrValueIs(kNameSpaceID_None,
-+ nsGkAtoms::disabled,
-+ nsGkAtoms::_true,
-+ eCaseMatters)) {
-+ ContentNode()->AsElement()->SetAttr(kNameSpaceID_None,
-+ nsGkAtoms::disabled,
-+ u"true"_ns, true);
-+ } else {
-+ ContentNode()->AsElement()->UnsetAttr(kNameSpaceID_None,
-+ nsGkAtoms::disabled, true);
-+ }
-+
-+ CopyAttrFromNodeIfExists(commandContent, nsGkAtoms::checked);
-+ CopyAttrFromNodeIfExists(commandContent, nsGkAtoms::accesskey);
-+ CopyAttrFromNodeIfExists(commandContent, nsGkAtoms::label);
-+ CopyAttrFromNodeIfExists(commandContent, nsGkAtoms::hidden);
-+}
-+
-+void
-+nsMenuItem::Update(const ComputedStyle *aComputedStyle)
-+{
-+ if (mNeedsUpdate) {
-+ mNeedsUpdate = false;
-+
-+ UpdateTypeAndState();
-+ UpdateAccel();
-+ UpdateLabel();
-+ UpdateSensitivity();
-+ }
-+
-+ UpdateVisibility(aComputedStyle);
-+ UpdateIcon(aComputedStyle);
-+}
-+
-+bool
-+nsMenuItem::IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const
-+{
-+ return nsCRT::strcmp(dbusmenu_menuitem_property_get(aNativeData,
-+ DBUSMENU_MENUITEM_PROP_TYPE),
-+ "separator") != 0;
-+}
-+
-+nsMenuObject::PropertyFlags
-+nsMenuItem::SupportedProperties() const
-+{
-+ return static_cast<nsMenuObject::PropertyFlags>(
-+ nsMenuObject::ePropLabel |
-+ nsMenuObject::ePropEnabled |
-+ nsMenuObject::ePropVisible |
-+ nsMenuObject::ePropIconData |
-+ nsMenuObject::ePropShortcut |
-+ nsMenuObject::ePropToggleType |
-+ nsMenuObject::ePropToggleState
-+ );
-+}
-+
-+void
-+nsMenuItem::OnAttributeChanged(nsIContent *aContent, nsAtom *aAttribute)
-+{
-+ MOZ_ASSERT(aContent == ContentNode() || aContent == mKeyContent,
-+ "Received an event that wasn't meant for us!");
-+
-+ if (aContent == ContentNode() && aAttribute == nsGkAtoms::checked &&
-+ aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
-+ nsGkAtoms::checked,
-+ nsGkAtoms::_true, eCaseMatters)) {
-+ nsContentUtils::AddScriptRunner(
-+ new nsMenuItemUncheckSiblingsRunnable(this));
-+ }
-+
-+ if (mNeedsUpdate) {
-+ return;
-+ }
-+
-+ if (!Parent()->IsBeingDisplayed()) {
-+ mNeedsUpdate = true;
-+ return;
-+ }
-+
-+ if (aContent == ContentNode()) {
-+ if (aAttribute == nsGkAtoms::key) {
-+ UpdateAccel();
-+ } else if (aAttribute == nsGkAtoms::label ||
-+ aAttribute == nsGkAtoms::accesskey ||
-+ aAttribute == nsGkAtoms::crop) {
-+ UpdateLabel();
-+ } else if (aAttribute == nsGkAtoms::disabled) {
-+ UpdateSensitivity();
-+ } else if (aAttribute == nsGkAtoms::type) {
-+ UpdateTypeAndState();
-+ } else if (aAttribute == nsGkAtoms::checked) {
-+ UpdateState();
-+ } else if (aAttribute == nsGkAtoms::hidden ||
-+ aAttribute == nsGkAtoms::collapsed) {
-+ RefPtr<const ComputedStyle> style = GetComputedStyle();
-+ UpdateVisibility(style);
-+ } else if (aAttribute == nsGkAtoms::image) {
-+ RefPtr<const ComputedStyle> style = GetComputedStyle();
-+ UpdateIcon(style);
-+ }
-+ } else if (aContent == mKeyContent &&
-+ (aAttribute == nsGkAtoms::key ||
-+ aAttribute == nsGkAtoms::keycode ||
-+ aAttribute == nsGkAtoms::modifiers)) {
-+ UpdateAccel();
-+ }
-+}
-+
-+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 (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;
-+}
-diff --git a/widget/gtk/nsMenuItem.h b/widget/gtk/nsMenuItem.h
-new file mode 100644
-index 000000000000..c621b4e223f1
---- /dev/null
-+++ b/widget/gtk/nsMenuItem.h
-@@ -0,0 +1,80 @@
-+/* -*- 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 __nsMenuItem_h__
-+#define __nsMenuItem_h__
-+
-+#include "mozilla/Attributes.h"
-+#include "nsCOMPtr.h"
-+
-+#include "nsDbusmenu.h"
-+#include "nsMenuObject.h"
-+
-+#include <glib.h>
-+
-+class nsAtom;
-+class nsIContent;
-+class nsMenuBar;
-+class nsMenuContainer;
-+
-+/*
-+ * This class represents 3 main classes of menuitems: labels, checkboxes and
-+ * radio buttons (with/without an icon)
-+ */
-+class nsMenuItem final : public nsMenuObject
-+{
-+public:
-+ nsMenuItem(nsMenuContainer *aParent, nsIContent *aContent);
-+ ~nsMenuItem() override;
-+
-+ nsMenuObject::EType Type() const override;
-+
-+private:
-+ friend class nsMenuItemUncheckSiblingsRunnable;
-+
-+ enum {
-+ eMenuItemFlag_ToggleState = (1 << 0)
-+ };
-+
-+ enum EMenuItemType {
-+ eMenuItemType_Normal,
-+ eMenuItemType_Radio,
-+ eMenuItemType_CheckBox
-+ };
-+
-+ bool IsCheckboxOrRadioItem() const;
-+
-+ static void item_activated_cb(DbusmenuMenuitem *menuitem,
-+ guint timestamp,
-+ gpointer user_data);
-+ void Activate(uint32_t aTimestamp);
-+
-+ void CopyAttrFromNodeIfExists(nsIContent *aContent, nsAtom *aAtom);
-+ void UpdateState();
-+ void UpdateTypeAndState();
-+ void UpdateAccel();
-+ nsMenuBar* MenuBar();
-+ void UncheckSiblings();
-+
-+ void InitializeNativeData() override;
-+ void UpdateContentAttributes() override;
-+ void Update(const mozilla::ComputedStyle *aComputedStyle) 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__ */
-diff --git a/widget/gtk/nsMenuObject.cpp b/widget/gtk/nsMenuObject.cpp
-new file mode 100644
-index 000000000000..fbf0fbef1368
---- /dev/null
-+++ 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:
-+ */
-+/* 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/. */
-+
-+#include "ImageOps.h"
-+#include "imgIContainer.h"
-+#include "imgINotificationObserver.h"
-+#include "imgLoader.h"
-+#include "imgRequestProxy.h"
-+#include "mozilla/ArrayUtils.h"
-+#include "mozilla/Assertions.h"
-+#include "mozilla/dom/Document.h"
-+#include "mozilla/dom/Element.h"
-+#include "mozilla/Preferences.h"
-+#include "mozilla/PresShell.h"
-+#include "mozilla/PresShellInlines.h"
-+#include "mozilla/GRefPtr.h"
-+#include "nsAttrValue.h"
-+#include "nsComputedDOMStyle.h"
-+#include "nsContentUtils.h"
-+#include "nsGkAtoms.h"
-+#include "nsIContent.h"
-+#include "nsIContentPolicy.h"
-+#include "nsILoadGroup.h"
-+#include "nsImageToPixbuf.h"
-+#include "nsIURI.h"
-+#include "nsNetUtil.h"
-+#include "nsPresContext.h"
-+#include "nsRect.h"
-+#include "nsServiceManagerUtils.h"
-+#include "nsString.h"
-+#include "nsStyleConsts.h"
-+#include "nsStyleStruct.h"
-+#include "nsUnicharUtils.h"
-+
-+#include "nsMenuContainer.h"
-+#include "nsNativeMenuDocListener.h"
-+
-+#include <gdk/gdk.h>
-+#include <glib-object.h>
-+#include <pango/pango.h>
-+
-+#include "nsMenuObject.h"
-+
-+// X11's None clashes with StyleDisplay::None
-+#include "X11UndefineNone.h"
-+
-+#undef None
-+
-+using namespace mozilla;
-+using mozilla::image::ImageOps;
-+
-+#define MAX_WIDTH 350000
-+
-+const char *gPropertyStrings[] = {
-+#define DBUSMENU_PROPERTY(e, s, b) s,
-+ DBUSMENU_PROPERTIES
-+#undef DBUSMENU_PROPERTY
-+ nullptr
-+};
-+
-+nsWeakMenuObject* nsWeakMenuObject::sHead;
-+PangoLayout* gPangoLayout = nullptr;
-+
-+class nsMenuObjectIconLoader final : public imgINotificationObserver
-+{
-+public:
-+ NS_DECL_ISUPPORTS
-+ NS_DECL_IMGINOTIFICATIONOBSERVER
-+
-+ nsMenuObjectIconLoader(nsMenuObject *aOwner) : mOwner(aOwner) { };
-+
-+ void LoadIcon(const ComputedStyle *aComputedStyle);
-+ void Destroy();
-+
-+private:
-+ ~nsMenuObjectIconLoader() { };
-+
-+ nsMenuObject *mOwner;
-+ RefPtr<imgRequestProxy> mImageRequest;
-+ nsCOMPtr<nsIURI> mURI;
-+ nsIntRect mImageRect;
-+};
-+
-+NS_IMPL_ISUPPORTS(nsMenuObjectIconLoader, imgINotificationObserver)
-+
-+void
-+nsMenuObjectIconLoader::Notify(imgIRequest *aProxy,
-+ int32_t aType, const nsIntRect *aRect)
-+{
-+ if (!mOwner) {
-+ return;
-+ }
-+
-+ if (aProxy != mImageRequest) {
-+ return;
-+ }
-+
-+ if (aType == imgINotificationObserver::LOAD_COMPLETE) {
-+ uint32_t status = imgIRequest::STATUS_ERROR;
-+ if (NS_FAILED(mImageRequest->GetImageStatus(&status)) ||
-+ (status & imgIRequest::STATUS_ERROR)) {
-+ mImageRequest->Cancel(NS_BINDING_ABORTED);
-+ mImageRequest = nullptr;
-+ return;
-+ }
-+
-+ nsCOMPtr<imgIContainer> image;
-+ mImageRequest->GetImage(getter_AddRefs(image));
-+ MOZ_ASSERT(image);
-+
-+ // Ask the image to decode at its intrinsic size.
-+ int32_t width = 0, height = 0;
-+ image->GetWidth(&width);
-+ image->GetHeight(&height);
-+ image->RequestDecodeForSize(nsIntSize(width, height), imgIContainer::FLAG_NONE);
-+ return;
-+ }
-+
-+ if (aType == imgINotificationObserver::DECODE_COMPLETE) {
-+ mImageRequest->Cancel(NS_BINDING_ABORTED);
-+ mImageRequest = nullptr;
-+ return;
-+ }
-+
-+ if (aType != imgINotificationObserver::FRAME_COMPLETE) {
-+ return;
-+ }
-+
-+ nsCOMPtr<imgIContainer> img;
-+ mImageRequest->GetImage(getter_AddRefs(img));
-+ if (!img) {
-+ return;
-+ }
-+
-+ if (!mImageRect.IsEmpty()) {
-+ img = ImageOps::Clip(img, mImageRect);
-+ }
-+
-+ int32_t width, height;
-+ img->GetWidth(&width);
-+ img->GetHeight(&height);
-+
-+ if (width <= 0 || height <= 0) {
-+ mOwner->ClearIcon();
-+ return;
-+ }
-+
-+ if (width > 100 || height > 100) {
-+ // The icon data needs to go across DBus. Make sure the icon
-+ // data isn't too large, else our connection gets terminated and
-+ // GDbus helpfully aborts the application. Thank you :)
-+ NS_WARNING("Icon data too large");
-+ mOwner->ClearIcon();
-+ return;
-+ }
-+
-+ RefPtr<GdkPixbuf> pixbuf = nsImageToPixbuf::ImageToPixbuf(img);
-+ if (pixbuf) {
-+ dbusmenu_menuitem_property_set_image(mOwner->GetNativeData(),
-+ DBUSMENU_MENUITEM_PROP_ICON_DATA,
-+ pixbuf);
-+ }
-+
-+ return;
-+}
-+
-+void
-+nsMenuObjectIconLoader::LoadIcon(const ComputedStyle *aComputedStyle)
-+{
-+ dom::Document *doc = mOwner->ContentNode()->OwnerDoc();
-+
-+ nsCOMPtr<nsIURI> uri;
-+ nsIntRect imageRect;
-+ imgRequestProxy *imageRequest = nullptr;
-+
-+ nsAutoString uriString;
-+ if (mOwner->ContentNode()->AsElement()->GetAttr(kNameSpaceID_None,
-+ nsGkAtoms::image,
-+ uriString)) {
-+ NS_NewURI(getter_AddRefs(uri), uriString);
-+ } else {
-+ PresShell *shell = doc->GetPresShell();
-+ if (!shell) {
-+ return;
-+ }
-+
-+ nsPresContext *pc = shell->GetPresContext();
-+ if (!pc || !aComputedStyle) {
-+ return;
-+ }
-+
-+ const nsStyleList *list = aComputedStyle->StyleList();
-+ imageRequest = list->mListStyleImage.GetImageRequest();
-+ if (imageRequest) {
-+ imageRequest->GetURI(getter_AddRefs(uri));
-+ auto& rect = list->mImageRegion.AsRect();
-+ imageRect = rect.ToLayoutRect().ToNearestPixels(
-+ pc->AppUnitsPerDevPixel());
-+ }
-+ }
-+
-+ if (!uri) {
-+ mOwner->ClearIcon();
-+ mURI = nullptr;
-+
-+ if (mImageRequest) {
-+ mImageRequest->Cancel(NS_BINDING_ABORTED);
-+ mImageRequest = nullptr;
-+ }
-+
-+ return;
-+ }
-+
-+ bool same;
-+ if (mURI && NS_SUCCEEDED(mURI->Equals(uri, &same)) && same &&
-+ (!imageRequest || imageRect == mImageRect)) {
-+ return;
-+ }
-+
-+ if (mImageRequest) {
-+ mImageRequest->Cancel(NS_BINDING_ABORTED);
-+ mImageRequest = nullptr;
-+ }
-+
-+ mURI = uri;
-+
-+ if (imageRequest) {
-+ mImageRect = imageRect;
-+ imageRequest->Clone(this, nullptr, getter_AddRefs(mImageRequest));
-+ } else {
-+ mImageRect.SetEmpty();
-+ nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
-+ RefPtr<imgLoader> loader =
-+ nsContentUtils::GetImgLoaderForDocument(doc);
-+ if (!loader || !loadGroup) {
-+ NS_WARNING("Failed to get loader or load group for image load");
-+ return;
-+ }
-+
-+ loader->LoadImage(uri, nullptr, nullptr,
-+ nullptr, 0, loadGroup, this, nullptr, nullptr,
-+ nsIRequest::LOAD_NORMAL, nullptr,
-+ nsIContentPolicy::TYPE_IMAGE, EmptyString(),
-+ false, false, getter_AddRefs(mImageRequest));
-+ }
-+}
-+
-+void
-+nsMenuObjectIconLoader::Destroy()
-+{
-+ if (mImageRequest) {
-+ mImageRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
-+ mImageRequest = nullptr;
-+ }
-+
-+ mOwner = nullptr;
-+}
-+
-+static int
-+CalculateTextWidth(const nsAString& aText)
-+{
-+ if (!gPangoLayout) {
-+ PangoFontMap *fontmap = pango_cairo_font_map_get_default();
-+ PangoContext *ctx = pango_font_map_create_context(fontmap);
-+ gPangoLayout = pango_layout_new(ctx);
-+ g_object_unref(ctx);
-+ }
-+
-+ pango_layout_set_text(gPangoLayout, NS_ConvertUTF16toUTF8(aText).get(), -1);
-+
-+ int width, dummy;
-+ pango_layout_get_size(gPangoLayout, &width, &dummy);
-+
-+ return width;
-+}
-+
-+static const nsDependentString
-+GetEllipsis()
-+{
-+ static char16_t sBuf[4] = { 0, 0, 0, 0 };
-+ if (!sBuf[0]) {
-+ nsString ellipsis;
-+ Preferences::GetLocalizedString("intl.ellipsis", ellipsis);
-+ if (!ellipsis.IsEmpty()) {
-+ uint32_t l = ellipsis.Length();
-+ const nsString::char_type *c = ellipsis.BeginReading();
-+ uint32_t i = 0;
-+ while (i < 3 && i < l) {
-+ sBuf[i++] = *(c++);
-+ }
-+ } else {
-+ sBuf[0] = '.';
-+ sBuf[1] = '.';
-+ sBuf[2] = '.';
-+ }
-+ }
-+
-+ return nsDependentString(sBuf);
-+}
-+
-+static int
-+GetEllipsisWidth()
-+{
-+ static int sEllipsisWidth = -1;
-+
-+ if (sEllipsisWidth == -1) {
-+ sEllipsisWidth = CalculateTextWidth(GetEllipsis());
-+ }
-+
-+ return sEllipsisWidth;
-+}
-+
-+nsMenuObject::nsMenuObject(nsMenuContainer *aParent, nsIContent *aContent) :
-+ mContent(aContent),
-+ mListener(aParent->DocListener()),
-+ mParent(aParent),
-+ mNativeData(nullptr)
-+{
-+ MOZ_ASSERT(mContent);
-+ MOZ_ASSERT(mListener);
-+ MOZ_ASSERT(mParent);
-+}
-+
-+nsMenuObject::nsMenuObject(nsNativeMenuDocListener *aListener,
-+ nsIContent *aContent) :
-+ mContent(aContent),
-+ mListener(aListener),
-+ mParent(nullptr),
-+ mNativeData(nullptr)
-+{
-+ MOZ_ASSERT(mContent);
-+ MOZ_ASSERT(mListener);
-+}
-+
-+void
-+nsMenuObject::UpdateLabel()
-+{
-+ // Gecko stores the label and access key in separate attributes
-+ // so we need to convert label="Foo_Bar"/accesskey="F" in to
-+ // label="_Foo__Bar" for dbusmenu
-+
-+ nsAutoString label;
-+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::label, label);
-+
-+ nsAutoString accesskey;
-+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey,
-+ accesskey);
-+
-+ const nsAutoString::char_type *akey = accesskey.BeginReading();
-+ char16_t keyLower = ToLowerCase(*akey);
-+ char16_t keyUpper = ToUpperCase(*akey);
-+
-+ const nsAutoString::char_type *iter = label.BeginReading();
-+ const nsAutoString::char_type *end = label.EndReading();
-+ uint32_t length = label.Length();
-+ uint32_t pos = 0;
-+ bool foundAccessKey = false;
-+
-+ while (iter != end) {
-+ if (*iter != char16_t('_')) {
-+ if ((*iter != keyLower && *iter != keyUpper) || foundAccessKey) {
-+ ++iter;
-+ ++pos;
-+ continue;
-+ }
-+ foundAccessKey = true;
-+ }
-+
-+ label.SetLength(++length);
-+
-+ iter = label.BeginReading() + pos;
-+ end = label.EndReading();
-+ nsAutoString::char_type *cur = label.BeginWriting() + pos;
-+
-+ memmove(cur + 1, cur, (length - 1 - pos) * sizeof(nsAutoString::char_type));
-+ *cur = nsAutoString::char_type('_');
-+
-+ iter += 2;
-+ pos += 2;
-+ }
-+
-+ if (CalculateTextWidth(label) <= MAX_WIDTH) {
-+ dbusmenu_menuitem_property_set(mNativeData,
-+ DBUSMENU_MENUITEM_PROP_LABEL,
-+ NS_ConvertUTF16toUTF8(label).get());
-+ return;
-+ }
-+
-+ // 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
-+ nsAutoString truncated;
-+ int target = MAX_WIDTH - GetEllipsisWidth();
-+ length = label.Length();
-+
-+ static mozilla::dom::Element::AttrValuesArray strings[] = {
-+ nsGkAtoms::left, nsGkAtoms::start,
-+ nsGkAtoms::center, nsGkAtoms::right,
-+ nsGkAtoms::end, nullptr
-+ };
-+
-+ int32_t type = mContent->AsElement()->FindAttrValueIn(kNameSpaceID_None,
-+ nsGkAtoms::crop,
-+ strings, eCaseMatters);
-+
-+ switch (type) {
-+ case 0:
-+ case 1:
-+ // FIXME: Implement left cropping
-+ case 2:
-+ // FIXME: Implement center cropping
-+ case 3:
-+ case 4:
-+ default:
-+ for (uint32_t i = 0; i < length; i++) {
-+ truncated.Append(label.CharAt(i));
-+ if (CalculateTextWidth(truncated) > target) {
-+ break;
-+ }
-+ }
-+
-+ truncated.Append(GetEllipsis());
-+ }
-+
-+ dbusmenu_menuitem_property_set(mNativeData,
-+ DBUSMENU_MENUITEM_PROP_LABEL,
-+ NS_ConvertUTF16toUTF8(truncated).get());
-+}
-+
-+void
-+nsMenuObject::UpdateVisibility(const ComputedStyle *aComputedStyle)
-+{
-+ bool vis = true;
-+
-+ if (aComputedStyle &&
-+ (aComputedStyle->StyleDisplay()->mDisplay == StyleDisplay::None ||
-+ aComputedStyle->StyleVisibility()->mVisible ==
-+ StyleVisibility::Collapse)) {
-+ vis = false;
-+ }
-+
-+ dbusmenu_menuitem_property_set_bool(mNativeData,
-+ DBUSMENU_MENUITEM_PROP_VISIBLE,
-+ vis);
-+}
-+
-+void
-+nsMenuObject::UpdateSensitivity()
-+{
-+ bool disabled = mContent->AsElement()->AttrValueIs(kNameSpaceID_None,
-+ nsGkAtoms::disabled,
-+ nsGkAtoms::_true,
-+ eCaseMatters);
-+
-+ dbusmenu_menuitem_property_set_bool(mNativeData,
-+ DBUSMENU_MENUITEM_PROP_ENABLED,
-+ !disabled);
-+
-+}
-+
-+void
-+nsMenuObject::UpdateIcon(const ComputedStyle *aComputedStyle)
-+{
-+ if (ShouldShowIcon()) {
-+ if (!mIconLoader) {
-+ mIconLoader = new nsMenuObjectIconLoader(this);
-+ }
-+
-+ mIconLoader->LoadIcon(aComputedStyle);
-+ } else {
-+ if (mIconLoader) {
-+ mIconLoader->Destroy();
-+ mIconLoader = nullptr;
-+ }
-+
-+ ClearIcon();
-+ }
-+}
-+
-+already_AddRefed<const ComputedStyle>
-+nsMenuObject::GetComputedStyle()
-+{
-+ RefPtr<const ComputedStyle> style =
-+ nsComputedDOMStyle::GetComputedStyleNoFlush(
-+ mContent->AsElement());
-+
-+ return style.forget();
-+}
-+
-+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(const ComputedStyle *aComputedStyle)
-+{
-+}
-+
-+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) == nsGkAtoms::menuitem_with_favicon) {
-+ return true;
-+ }
-+ }
-+
-+ return false;
-+}
-+
-+void
-+nsMenuObject::ClearIcon()
-+{
-+ dbusmenu_menuitem_property_remove(mNativeData,
-+ DBUSMENU_MENUITEM_PROP_ICON_DATA);
-+}
-+
-+nsMenuObject::~nsMenuObject()
-+{
-+ nsWeakMenuObject::NotifyDestroyed(this);
-+
-+ if (mIconLoader) {
-+ mIconLoader->Destroy();
-+ }
-+
-+ if (mListener) {
-+ mListener->UnregisterForContentChanges(mContent);
-+ }
-+
-+ if (mNativeData) {
-+ g_object_unref(mNativeData);
-+ mNativeData = nullptr;
-+ }
-+}
-+
-+void
-+nsMenuObject::CreateNativeData()
-+{
-+ MOZ_ASSERT(mNativeData == nullptr, "This node already has a DbusmenuMenuitem. The old one will be leaked");
-+
-+ mNativeData = dbusmenu_menuitem_new();
-+ InitializeNativeData();
-+ if (mParent && mParent->IsBeingDisplayed()) {
-+ ContainerIsOpening();
-+ }
-+
-+ mListener->RegisterForContentChanges(mContent, this);
-+}
-+
-+nsresult
-+nsMenuObject::AdoptNativeData(DbusmenuMenuitem *aNativeData)
-+{
-+ MOZ_ASSERT(mNativeData == nullptr, "This node already has a DbusmenuMenuitem. The old one will be leaked");
-+
-+ if (!IsCompatibleWithNativeData(aNativeData)) {
-+ return NS_ERROR_FAILURE;
-+ }
-+
-+ mNativeData = aNativeData;
-+ g_object_ref(mNativeData);
-+
-+ PropertyFlags supported = SupportedProperties();
-+ PropertyFlags mask = static_cast<PropertyFlags>(1);
-+
-+ for (uint32_t i = 0; gPropertyStrings[i]; ++i) {
-+ if (!(mask & supported)) {
-+ dbusmenu_menuitem_property_remove(mNativeData, gPropertyStrings[i]);
-+ }
-+ mask = static_cast<PropertyFlags>(mask << 1);
-+ }
-+
-+ InitializeNativeData();
-+ if (mParent && mParent->IsBeingDisplayed()) {
-+ ContainerIsOpening();
-+ }
-+
-+ mListener->RegisterForContentChanges(mContent, this);
-+
-+ return NS_OK;
-+}
-+
-+void
-+nsMenuObject::ContainerIsOpening()
-+{
-+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
-+
-+ UpdateContentAttributes();
-+
-+ RefPtr<const ComputedStyle> style = GetComputedStyle();
-+ Update(style);
-+}
-+
-+/* static */ void
-+nsWeakMenuObject::AddWeakReference(nsWeakMenuObject *aWeak)
-+{
-+ aWeak->mPrev = sHead;
-+ sHead = aWeak;
-+}
-+
-+/* static */ void
-+nsWeakMenuObject::RemoveWeakReference(nsWeakMenuObject *aWeak)
-+{
-+ if (aWeak == sHead) {
-+ sHead = aWeak->mPrev;
-+ return;
-+ }
-+
-+ nsWeakMenuObject *weak = sHead;
-+ while (weak && weak->mPrev != aWeak) {
-+ weak = weak->mPrev;
-+ }
-+
-+ if (weak) {
-+ weak->mPrev = aWeak->mPrev;
-+ }
-+}
-+
-+/* static */ void
-+nsWeakMenuObject::NotifyDestroyed(nsMenuObject *aMenuObject)
-+{
-+ nsWeakMenuObject *weak = sHead;
-+ while (weak) {
-+ if (weak->mMenuObject == aMenuObject) {
-+ weak->mMenuObject = nullptr;
-+ }
-+
-+ weak = weak->mPrev;
-+ }
-+}
-diff --git a/widget/gtk/nsMenuObject.h b/widget/gtk/nsMenuObject.h
-new file mode 100644
-index 000000000000..92b1ffd8f2fa
---- /dev/null
-+++ 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:
-+ */
-+/* 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 __nsMenuObject_h__
-+#define __nsMenuObject_h__
-+
-+#include "mozilla/Attributes.h"
-+#include "mozilla/ComputedStyleInlines.h"
-+#include "nsCOMPtr.h"
-+
-+#include "nsDbusmenu.h"
-+#include "nsNativeMenuDocListener.h"
-+
-+class nsIContent;
-+class nsMenuContainer;
-+class nsMenuObjectIconLoader;
-+
-+#define DBUSMENU_PROPERTIES \
-+ DBUSMENU_PROPERTY(Label, DBUSMENU_MENUITEM_PROP_LABEL, 0) \
-+ DBUSMENU_PROPERTY(Enabled, DBUSMENU_MENUITEM_PROP_ENABLED, 1) \
-+ DBUSMENU_PROPERTY(Visible, DBUSMENU_MENUITEM_PROP_VISIBLE, 2) \
-+ DBUSMENU_PROPERTY(IconData, DBUSMENU_MENUITEM_PROP_ICON_DATA, 3) \
-+ DBUSMENU_PROPERTY(Type, DBUSMENU_MENUITEM_PROP_TYPE, 4) \
-+ DBUSMENU_PROPERTY(Shortcut, DBUSMENU_MENUITEM_PROP_SHORTCUT, 5) \
-+ DBUSMENU_PROPERTY(ToggleType, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, 6) \
-+ DBUSMENU_PROPERTY(ToggleState, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, 7) \
-+ DBUSMENU_PROPERTY(ChildDisplay, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY, 8)
-+
-+/*
-+ * This is the base class for all menu nodes. Each instance represents
-+ * a single node in the menu hierarchy. It wraps the corresponding DOM node and
-+ * native menu node, keeps them in sync and transfers events between the two.
-+ * It is not reference counted - each node is owned by its parent (the top
-+ * level menubar is owned by the window) and keeps a weak pointer to its
-+ * parent (which is guaranteed to always be valid because a node will never
-+ * outlive its parent). It is not safe to keep a reference to nsMenuObject
-+ * externally.
-+ */
-+class nsMenuObject : public nsNativeMenuChangeObserver
-+{
-+public:
-+ enum EType {
-+ eType_MenuBar,
-+ eType_Menu,
-+ eType_MenuItem
-+ };
-+
-+ virtual ~nsMenuObject();
-+
-+ // Get the native menu item node
-+ DbusmenuMenuitem* GetNativeData() const { return mNativeData; }
-+
-+ // Get the parent menu object
-+ nsMenuContainer* Parent() const { return mParent; }
-+
-+ // Get the content node
-+ nsIContent* ContentNode() const { return mContent; }
-+
-+ // Get the type of this node. Must be provided by subclasses
-+ virtual EType Type() const = 0;
-+
-+ // Get the document listener
-+ nsNativeMenuDocListener* DocListener() const { return mListener; }
-+
-+ // Create the native menu item node (called by containers)
-+ void CreateNativeData();
-+
-+ // Adopt the specified native menu item node (called by containers)
-+ nsresult AdoptNativeData(DbusmenuMenuitem *aNativeData);
-+
-+ // Called by the container to tell us that it's opening
-+ void ContainerIsOpening();
-+
-+protected:
-+ 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(const mozilla::ComputedStyle *aComputedStyle);
-+ void UpdateSensitivity();
-+ void UpdateIcon(const mozilla::ComputedStyle *aComputedStyle);
-+
-+ already_AddRefed<const mozilla::ComputedStyle> GetComputedStyle();
-+
-+private:
-+ friend class nsMenuObjectIconLoader;
-+
-+ // Set up initial properties on the native data, connect to signals etc.
-+ // This should be implemented by subclasses
-+ virtual void InitializeNativeData();
-+
-+ // Return the properties that this menu object type supports
-+ // This should be implemented by subclasses
-+ virtual PropertyFlags SupportedProperties() const;
-+
-+ // Determine whether this menu object could use the specified
-+ // native item. Returns true by default but can be overridden by subclasses
-+ virtual bool
-+ IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const;
-+
-+ // Update attributes on this objects content node when the container opens.
-+ // This is called before style resolution, and should be implemented by
-+ // subclasses who want to modify attributes that might affect style.
-+ // This will not be called when there are script blockers
-+ virtual void UpdateContentAttributes();
-+
-+ // Update properties that should be refreshed when the container opens.
-+ // This should be implemented by subclasses that have properties which
-+ // need refreshing
-+ virtual void Update(const mozilla::ComputedStyle *aComputedStyle);
-+
-+ bool ShouldShowIcon() const;
-+ void ClearIcon();
-+
-+ nsCOMPtr<nsIContent> mContent;
-+ // mListener is a strong ref for simplicity - someone in the tree needs to
-+ // own it, and this only really needs to be the top-level object (as no
-+ // children outlives their parent). However, we need to keep it alive until
-+ // after running the nsMenuObject destructor for the top-level menu object,
-+ // hence the strong ref
-+ RefPtr<nsNativeMenuDocListener> mListener;
-+ nsMenuContainer *mParent; // [weak]
-+ DbusmenuMenuitem *mNativeData; // [strong]
-+ RefPtr<nsMenuObjectIconLoader> mIconLoader;
-+};
-+
-+// Keep a weak pointer to a menu object
-+class nsWeakMenuObject
-+{
-+public:
-+ nsWeakMenuObject() : mPrev(nullptr), mMenuObject(nullptr) {}
-+
-+ nsWeakMenuObject(nsMenuObject *aMenuObject) :
-+ mPrev(nullptr), mMenuObject(aMenuObject)
-+ {
-+ AddWeakReference(this);
-+ }
-+
-+ ~nsWeakMenuObject() { RemoveWeakReference(this); }
-+
-+ nsMenuObject* get() const { return mMenuObject; }
-+
-+ nsMenuObject* operator->() const { return mMenuObject; }
-+
-+ explicit operator bool() const { return !!mMenuObject; }
-+
-+ static void NotifyDestroyed(nsMenuObject *aMenuObject);
-+
-+private:
-+ static void AddWeakReference(nsWeakMenuObject *aWeak);
-+ static void RemoveWeakReference(nsWeakMenuObject *aWeak);
-+
-+ nsWeakMenuObject *mPrev;
-+ static nsWeakMenuObject *sHead;
-+
-+ nsMenuObject *mMenuObject;
-+};
-+
-+#endif /* __nsMenuObject_h__ */
-diff --git a/widget/gtk/nsMenuSeparator.cpp b/widget/gtk/nsMenuSeparator.cpp
-new file mode 100644
-index 000000000000..3ab135bd8a20
---- /dev/null
-+++ b/widget/gtk/nsMenuSeparator.cpp
-@@ -0,0 +1,82 @@
-+/* -*- 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/. */
-+
-+#include "mozilla/Assertions.h"
-+#include "nsCRT.h"
-+#include "nsGkAtoms.h"
-+
-+#include "nsDbusmenu.h"
-+
-+#include "nsMenuContainer.h"
-+#include "nsMenuSeparator.h"
-+
-+using namespace mozilla;
-+
-+void
-+nsMenuSeparator::InitializeNativeData()
-+{
-+ dbusmenu_menuitem_property_set(GetNativeData(),
-+ DBUSMENU_MENUITEM_PROP_TYPE,
-+ "separator");
-+}
-+
-+void
-+nsMenuSeparator::Update(const ComputedStyle *aComputedStyle)
-+{
-+ UpdateVisibility(aComputedStyle);
-+}
-+
-+bool
-+nsMenuSeparator::IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const
-+{
-+ return nsCRT::strcmp(dbusmenu_menuitem_property_get(aNativeData,
-+ DBUSMENU_MENUITEM_PROP_TYPE),
-+ "separator") == 0;
-+}
-+
-+nsMenuObject::PropertyFlags
-+nsMenuSeparator::SupportedProperties() const
-+{
-+ return static_cast<nsMenuObject::PropertyFlags>(
-+ nsMenuObject::ePropVisible |
-+ nsMenuObject::ePropType
-+ );
-+}
-+
-+void
-+nsMenuSeparator::OnAttributeChanged(nsIContent *aContent, nsAtom *aAttribute)
-+{
-+ MOZ_ASSERT(aContent == ContentNode(), "Received an event that wasn't meant for us!");
-+
-+ if (!Parent()->IsBeingDisplayed()) {
-+ return;
-+ }
-+
-+ if (aAttribute == nsGkAtoms::hidden ||
-+ aAttribute == nsGkAtoms::collapsed) {
-+ RefPtr<const ComputedStyle> style = GetComputedStyle();
-+ UpdateVisibility(style);
-+ }
-+}
-+
-+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;
-+}
-diff --git a/widget/gtk/nsMenuSeparator.h b/widget/gtk/nsMenuSeparator.h
-new file mode 100644
-index 000000000000..dd3f5b974dd3
---- /dev/null
-+++ 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:
-+ */
-+/* 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 __nsMenuSeparator_h__
-+#define __nsMenuSeparator_h__
-+
-+#include "mozilla/Attributes.h"
-+
-+#include "nsMenuObject.h"
-+
-+class nsIContent;
-+class nsAtom;
-+class nsMenuContainer;
-+
-+// Menu separator class
-+class nsMenuSeparator final : public nsMenuObject
-+{
-+public:
-+ nsMenuSeparator(nsMenuContainer *aParent, nsIContent *aContent);
-+ ~nsMenuSeparator();
-+
-+ nsMenuObject::EType Type() const override;
-+
-+private:
-+ void InitializeNativeData() override;
-+ void Update(const mozilla::ComputedStyle *aComputedStyle) override;
-+ bool IsCompatibleWithNativeData(DbusmenuMenuitem *aNativeData) const override;
-+ nsMenuObject::PropertyFlags SupportedProperties() const override;
-+
-+ void OnAttributeChanged(nsIContent *aContent, nsAtom *aAttribute) override;
-+};
-+
-+#endif /* __nsMenuSeparator_h__ */
-diff --git a/widget/gtk/nsNativeMenuDocListener.cpp b/widget/gtk/nsNativeMenuDocListener.cpp
-new file mode 100644
-index 000000000000..2336ed6e3bc5
---- /dev/null
-+++ b/widget/gtk/nsNativeMenuDocListener.cpp
-@@ -0,0 +1,347 @@
-+/* -*- 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/. */
-+
-+#include "mozilla/Assertions.h"
-+#include "mozilla/DebugOnly.h"
-+#include "mozilla/dom/Document.h"
-+#include "mozilla/dom/Element.h"
-+#include "nsContentUtils.h"
-+#include "nsAtom.h"
-+#include "nsIContent.h"
-+
-+#include "nsMenuContainer.h"
-+
-+#include "nsNativeMenuDocListener.h"
-+
-+using namespace mozilla;
-+
-+uint32_t nsNativeMenuDocListener::sUpdateBlockersCount = 0;
-+
-+nsNativeMenuDocListenerTArray *gPendingListeners;
-+
-+/*
-+ * Small helper which caches a single listener, so that consecutive
-+ * events which go to the same node avoid multiple hash table lookups
-+ */
-+class MOZ_STACK_CLASS DispatchHelper
-+{
-+public:
-+ DispatchHelper(nsNativeMenuDocListener *aListener,
-+ nsIContent *aContent) :
-+ mObserver(nullptr)
-+ {
-+ if (aContent == aListener->mLastSource) {
-+ mObserver = aListener->mLastTarget;
-+ } else {
-+ mObserver = aListener->mContentToObserverTable.Get(aContent);
-+ if (mObserver) {
-+ aListener->mLastSource = aContent;
-+ aListener->mLastTarget = mObserver;
-+ }
-+ }
-+ }
-+
-+ ~DispatchHelper() { };
-+
-+ nsNativeMenuChangeObserver* Observer() const { return mObserver; }
-+
-+ bool HasObserver() const { return !!mObserver; }
-+
-+private:
-+ nsNativeMenuChangeObserver *mObserver;
-+};
-+
-+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(MakeUnique<MutationRecord>())->get();
-+ 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(MakeUnique<MutationRecord>())->get();
-+ 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(MakeUnique<MutationRecord>())->get();
-+ 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,
-+ nsAtom *aAttribute)
-+{
-+ DispatchHelper h(this, aContent);
-+ if (h.HasObserver()) {
-+ h.Observer()->OnAttributeChanged(aContent, aAttribute);
-+ }
-+}
-+
-+void
-+nsNativeMenuDocListener::DoContentInserted(nsIContent *aContainer,
-+ nsIContent *aChild,
-+ nsIContent *aPrevSibling)
-+{
-+ DispatchHelper h(this, aContainer);
-+ if (h.HasObserver()) {
-+ h.Observer()->OnContentInserted(aContainer, aChild, aPrevSibling);
-+ }
-+}
-+
-+void
-+nsNativeMenuDocListener::DoContentRemoved(nsIContent *aContainer,
-+ nsIContent *aChild)
-+{
-+ DispatchHelper h(this, aContainer);
-+ if (h.HasObserver()) {
-+ h.Observer()->OnContentRemoved(aContainer, aChild);
-+ }
-+}
-+
-+void
-+nsNativeMenuDocListener::DoBeginUpdates(nsIContent *aTarget)
-+{
-+ DispatchHelper h(this, aTarget);
-+ if (h.HasObserver()) {
-+ h.Observer()->OnBeginUpdates(aTarget);
-+ }
-+}
-+
-+void
-+nsNativeMenuDocListener::DoEndUpdates(nsIContent *aTarget)
-+{
-+ DispatchHelper h(this, aTarget);
-+ if (h.HasObserver()) {
-+ h.Observer()->OnEndUpdates();
-+ }
-+}
-+
-+void
-+nsNativeMenuDocListener::FlushPendingMutations()
-+{
-+ nsIContent *currentTarget = nullptr;
-+ bool inUpdateSequence = false;
-+
-+ while (mPendingMutations.Length() > 0) {
-+ MutationRecord *m = mPendingMutations[0].get();
-+
-+ if (m->mTarget != currentTarget) {
-+ if (inUpdateSequence) {
-+ DoEndUpdates(currentTarget);
-+ inUpdateSequence = false;
-+ }
-+
-+ currentTarget = m->mTarget;
-+
-+ if (mPendingMutations.Length() > 1 &&
-+ mPendingMutations[1]->mTarget == currentTarget) {
-+ DoBeginUpdates(currentTarget);
-+ inUpdateSequence = true;
-+ }
-+ }
-+
-+ switch (m->mType) {
-+ case MutationRecord::eAttributeChanged:
-+ DoAttributeChanged(m->mTarget, m->mAttribute);
-+ break;
-+ case MutationRecord::eContentInserted:
-+ DoContentInserted(m->mTarget, m->mChild, m->mPrevSibling);
-+ break;
-+ case MutationRecord::eContentRemoved:
-+ DoContentRemoved(m->mTarget, m->mChild);
-+ break;
-+ default:
-+ MOZ_ASSERT_UNREACHABLE("Invalid type");
-+ }
-+
-+ mPendingMutations.RemoveElementAt(0);
-+ }
-+
-+ if (inUpdateSequence) {
-+ DoEndUpdates(currentTarget);
-+ }
-+}
-+
-+/* static */ void
-+nsNativeMenuDocListener::ScheduleFlush(nsNativeMenuDocListener *aListener)
-+{
-+ MOZ_ASSERT(sUpdateBlockersCount > 0, "Shouldn't be doing this now");
-+
-+ if (!gPendingListeners) {
-+ gPendingListeners = new nsNativeMenuDocListenerTArray;
-+ }
-+
-+ if (gPendingListeners->IndexOf(aListener) ==
-+ nsNativeMenuDocListenerTArray::NoIndex) {
-+ gPendingListeners->AppendElement(aListener);
-+ }
-+}
-+
-+/* static */ void
-+nsNativeMenuDocListener::CancelFlush(nsNativeMenuDocListener *aListener)
-+{
-+ if (!gPendingListeners) {
-+ return;
-+ }
-+
-+ gPendingListeners->RemoveElement(aListener);
-+}
-+
-+/* static */ void
-+nsNativeMenuDocListener::RemoveUpdateBlocker()
-+{
-+ if (sUpdateBlockersCount == 1 && gPendingListeners) {
-+ while (gPendingListeners->Length() > 0) {
-+ (*gPendingListeners)[0]->FlushPendingMutations();
-+ gPendingListeners->RemoveElementAt(0);
-+ }
-+ }
-+
-+ MOZ_ASSERT(sUpdateBlockersCount > 0, "Negative update blockers count!");
-+ sUpdateBlockersCount--;
-+}
-+
-+nsNativeMenuDocListener::nsNativeMenuDocListener(nsIContent *aRootNode) :
-+ mRootNode(aRootNode),
-+ mDocument(nullptr),
-+ mLastSource(nullptr),
-+ mLastTarget(nullptr)
-+{
-+ MOZ_COUNT_CTOR(nsNativeMenuDocListener);
-+}
-+
-+void
-+nsNativeMenuDocListener::RegisterForContentChanges(nsIContent *aContent,
-+ nsNativeMenuChangeObserver *aObserver)
-+{
-+ MOZ_ASSERT(aContent, "Need content parameter");
-+ MOZ_ASSERT(aObserver, "Need observer parameter");
-+ if (!aContent || !aObserver) {
-+ return;
-+ }
-+
-+ DebugOnly<nsNativeMenuChangeObserver *> old;
-+ MOZ_ASSERT(!mContentToObserverTable.Get(aContent, &old) || old == aObserver,
-+ "Multiple observers for the same content node are not supported");
-+
-+ mContentToObserverTable.InsertOrUpdate(aContent, aObserver);
-+}
-+
-+void
-+nsNativeMenuDocListener::UnregisterForContentChanges(nsIContent *aContent)
-+{
-+ MOZ_ASSERT(aContent, "Need content parameter");
-+ if (!aContent) {
-+ return;
-+ }
-+
-+ mContentToObserverTable.Remove(aContent);
-+ if (aContent == mLastSource) {
-+ mLastSource = nullptr;
-+ mLastTarget = nullptr;
-+ }
-+}
-+
-+void
-+nsNativeMenuDocListener::Start()
-+{
-+ if (mDocument) {
-+ return;
-+ }
-+
-+ mDocument = mRootNode->OwnerDoc();
-+ if (!mDocument) {
-+ return;
-+ }
-+
-+ mDocument->AddMutationObserver(this);
-+}
-+
-+void
-+nsNativeMenuDocListener::Stop()
-+{
-+ if (mDocument) {
-+ mDocument->RemoveMutationObserver(this);
-+ mDocument = nullptr;
-+ }
-+
-+ CancelFlush(this);
-+ mPendingMutations.Clear();
-+}
-diff --git a/widget/gtk/nsNativeMenuDocListener.h b/widget/gtk/nsNativeMenuDocListener.h
-new file mode 100644
-index 000000000000..5ee99cba70fd
---- /dev/null
-+++ 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:
-+ */
-+/* 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 __nsNativeMenuDocListener_h__
-+#define __nsNativeMenuDocListener_h__
-+
-+#include "mozilla/Attributes.h"
-+#include "mozilla/RefPtr.h"
-+#include "mozilla/UniquePtr.h"
-+#include "nsTHashMap.h"
-+#include "nsStubMutationObserver.h"
-+#include "nsTArray.h"
-+
-+class nsAtom;
-+class nsIContent;
-+class nsNativeMenuChangeObserver;
-+
-+namespace mozilla {
-+namespace dom {
-+class Document;
-+}
-+}
-+
-+/*
-+ * This class keeps a mapping of content nodes to observers and forwards DOM
-+ * mutations to these. There is exactly one of these for every menubar.
-+ */
-+class nsNativeMenuDocListener final : nsStubMutationObserver
-+{
-+public:
-+ NS_DECL_ISUPPORTS
-+
-+ nsNativeMenuDocListener(nsIContent *aRootNode);
-+
-+ // Register an observer to receive mutation events for the specified
-+ // content node. The caller must keep the observer alive until
-+ // UnregisterForContentChanges is called.
-+ void RegisterForContentChanges(nsIContent *aContent,
-+ nsNativeMenuChangeObserver *aObserver);
-+
-+ // Unregister the registered observer for the specified content node
-+ void UnregisterForContentChanges(nsIContent *aContent);
-+
-+ // Start listening to the document and forwarding DOM mutations to
-+ // registered observers.
-+ void Start();
-+
-+ // Stop listening to the document. No DOM mutations will be forwarded
-+ // 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()
-+ {
-+ nsNativeMenuDocListener::AddUpdateBlocker();
-+ }
-+
-+ ~BlockUpdatesScope()
-+ {
-+ nsNativeMenuDocListener::RemoveUpdateBlocker();
-+ }
-+ };
-+
-+private:
-+ friend class DispatchHelper;
-+
-+ struct MutationRecord {
-+ enum RecordType {
-+ eAttributeChanged,
-+ eContentInserted,
-+ eContentRemoved
-+ } mType;
-+
-+ nsCOMPtr<nsIContent> mTarget;
-+ nsCOMPtr<nsIContent> mChild;
-+ nsCOMPtr<nsIContent> mPrevSibling;
-+ RefPtr<nsAtom> mAttribute;
-+ };
-+
-+ ~nsNativeMenuDocListener();
-+
-+ 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 DoBeginUpdates(nsIContent *aTarget);
-+ void DoEndUpdates(nsIContent *aTarget);
-+
-+ void FlushPendingMutations();
-+ static void ScheduleFlush(nsNativeMenuDocListener *aListener);
-+ static void CancelFlush(nsNativeMenuDocListener *aListener);
-+
-+ static void AddUpdateBlocker() { ++sUpdateBlockersCount; }
-+ static void RemoveUpdateBlocker();
-+
-+ nsCOMPtr<nsIContent> mRootNode;
-+ mozilla::dom::Document *mDocument;
-+ nsIContent *mLastSource;
-+ nsNativeMenuChangeObserver *mLastTarget;
-+ nsTArray<mozilla::UniquePtr<MutationRecord> > mPendingMutations;
-+ nsTHashMap<nsPtrHashKey<nsIContent>, nsNativeMenuChangeObserver *> mContentToObserverTable;
-+
-+ 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, nsAtom *aAttribute) {}
-+
-+ virtual void OnContentInserted(nsIContent *aContainer,
-+ nsIContent *aChild,
-+ nsIContent *aPrevSibling) {}
-+
-+ virtual void OnContentRemoved(nsIContent *aContainer, nsIContent *aChild) {}
-+
-+ // 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) {};
-+
-+ // Signals the end of a sequence of events
-+ virtual void OnEndUpdates() {};
-+};
-+
-+#endif /* __nsNativeMenuDocListener_h__ */
-diff --git a/widget/gtk/nsNativeMenuService.cpp b/widget/gtk/nsNativeMenuService.cpp
-new file mode 100644
-index 000000000000..5cf36a447036
---- /dev/null
-+++ b/widget/gtk/nsNativeMenuService.cpp
-@@ -0,0 +1,478 @@
-+/* -*- 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/. */
-+
-+#include "mozilla/dom/Element.h"
-+#include "mozilla/Assertions.h"
-+#include "mozilla/Preferences.h"
-+#include "mozilla/UniquePtr.h"
-+#include "nsCOMPtr.h"
-+#include "nsCRT.h"
-+#include "nsGtkUtils.h"
-+#include "nsIContent.h"
-+#include "nsIWidget.h"
-+#include "nsServiceManagerUtils.h"
-+#include "nsWindow.h"
-+#include "prlink.h"
-+
-+#include "nsDbusmenu.h"
-+#include "nsMenuBar.h"
-+#include "nsNativeMenuDocListener.h"
-+
-+#include <glib-object.h>
-+#include <pango/pango.h>
-+#include <stdlib.h>
-+
-+#include "nsNativeMenuService.h"
-+
-+using namespace mozilla;
-+
-+nsNativeMenuService* nsNativeMenuService::sService = nullptr;
-+
-+extern PangoLayout* gPangoLayout;
-+extern nsNativeMenuDocListenerTArray* gPendingListeners;
-+
-+#undef g_dbus_proxy_new_for_bus
-+#undef g_dbus_proxy_new_for_bus_finish
-+#undef g_dbus_proxy_call
-+#undef g_dbus_proxy_call_finish
-+#undef g_dbus_proxy_get_name_owner
-+
-+typedef void (*_g_dbus_proxy_new_for_bus_fn)(GBusType, GDBusProxyFlags,
-+ GDBusInterfaceInfo*,
-+ const gchar*, const gchar*,
-+ const gchar*, GCancellable*,
-+ GAsyncReadyCallback, gpointer);
-+
-+typedef GDBusProxy* (*_g_dbus_proxy_new_for_bus_finish_fn)(GAsyncResult*,
-+ GError**);
-+typedef void (*_g_dbus_proxy_call_fn)(GDBusProxy*, const gchar*, GVariant*,
-+ GDBusCallFlags, gint, GCancellable*,
-+ GAsyncReadyCallback, gpointer);
-+typedef GVariant* (*_g_dbus_proxy_call_finish_fn)(GDBusProxy*, GAsyncResult*,
-+ GError**);
-+typedef gchar* (*_g_dbus_proxy_get_name_owner_fn)(GDBusProxy*);
-+
-+static _g_dbus_proxy_new_for_bus_fn _g_dbus_proxy_new_for_bus;
-+static _g_dbus_proxy_new_for_bus_finish_fn _g_dbus_proxy_new_for_bus_finish;
-+static _g_dbus_proxy_call_fn _g_dbus_proxy_call;
-+static _g_dbus_proxy_call_finish_fn _g_dbus_proxy_call_finish;
-+static _g_dbus_proxy_get_name_owner_fn _g_dbus_proxy_get_name_owner;
-+
-+#define g_dbus_proxy_new_for_bus _g_dbus_proxy_new_for_bus
-+#define g_dbus_proxy_new_for_bus_finish _g_dbus_proxy_new_for_bus_finish
-+#define g_dbus_proxy_call _g_dbus_proxy_call
-+#define g_dbus_proxy_call_finish _g_dbus_proxy_call_finish
-+#define g_dbus_proxy_get_name_owner _g_dbus_proxy_get_name_owner
-+
-+static PRLibrary *gGIOLib = nullptr;
-+
-+static nsresult
-+GDBusInit()
-+{
-+ gGIOLib = PR_LoadLibrary("libgio-2.0.so.0");
-+ if (!gGIOLib) {
-+ return NS_ERROR_FAILURE;
-+ }
-+
-+ g_dbus_proxy_new_for_bus = (_g_dbus_proxy_new_for_bus_fn)PR_FindFunctionSymbol(gGIOLib, "g_dbus_proxy_new_for_bus");
-+ g_dbus_proxy_new_for_bus_finish = (_g_dbus_proxy_new_for_bus_finish_fn)PR_FindFunctionSymbol(gGIOLib, "g_dbus_proxy_new_for_bus_finish");
-+ g_dbus_proxy_call = (_g_dbus_proxy_call_fn)PR_FindFunctionSymbol(gGIOLib, "g_dbus_proxy_call");
-+ g_dbus_proxy_call_finish = (_g_dbus_proxy_call_finish_fn)PR_FindFunctionSymbol(gGIOLib, "g_dbus_proxy_call_finish");
-+ g_dbus_proxy_get_name_owner = (_g_dbus_proxy_get_name_owner_fn)PR_FindFunctionSymbol(gGIOLib, "g_dbus_proxy_get_name_owner");
-+
-+ if (!g_dbus_proxy_new_for_bus ||
-+ !g_dbus_proxy_new_for_bus_finish ||
-+ !g_dbus_proxy_call ||
-+ !g_dbus_proxy_call_finish ||
-+ !g_dbus_proxy_get_name_owner) {
-+ return NS_ERROR_FAILURE;
-+ }
-+
-+ return NS_OK;
-+}
-+
-+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) {
-+ return;
-+ }
-+ nsCOMPtr<nsINativeMenuService> service =
-+ do_GetService("@mozilla.org/widget/nativemenuservice;1");
-+}
-+
-+void
-+nsNativeMenuService::SetOnline(bool aOnline)
-+{
-+ if (!Preferences::GetBool("ui.use_unity_menubar", true)) {
-+ aOnline = false;
-+ }
-+
-+ mOnline = aOnline;
-+ if (aOnline) {
-+ for (uint32_t i = 0; i < mMenuBars.Length(); ++i) {
-+ RegisterNativeMenuBar(mMenuBars[i]);
-+ }
-+ } else {
-+ for (uint32_t i = 0; i < mMenuBars.Length(); ++i) {
-+ mMenuBars[i]->Deactivate();
-+ }
-+ }
-+}
-+
-+void
-+nsNativeMenuService::RegisterNativeMenuBar(nsMenuBar *aMenuBar)
-+{
-+ if (!mOnline) {
-+ return;
-+ }
-+
-+ // This will effectively create the native menubar for
-+ // exporting over the session bus, and hide the XUL menubar
-+ aMenuBar->Activate();
-+
-+ if (!mDbusProxy ||
-+ !gtk_widget_get_mapped(aMenuBar->TopLevelWindow()) ||
-+ 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();
-+ 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.InsertOrUpdate(aMenuBar, cancellable);
-+
-+ // We keep a weak ref because we can't assume that GDBus cancellation
-+ // is reliable (see https://launchpad.net/bugs/953562)
-+
-+ g_dbus_proxy_call(mDbusProxy, "RegisterWindow",
-+ g_variant_new("(uo)", xid, path.get()),
-+ G_DBUS_CALL_FLAGS_NONE, -1,
-+ cancellable,
-+ register_native_menubar_cb, aMenuBar);
-+}
-+
-+/* static */ void
-+nsNativeMenuService::name_owner_changed_cb(GObject *gobject,
-+ GParamSpec *pspec,
-+ gpointer user_data)
-+{
-+ nsNativeMenuService::GetSingleton()->OnNameOwnerChanged();
-+}
-+
-+/* static */ void
-+nsNativeMenuService::proxy_created_cb(GObject *source_object,
-+ GAsyncResult *res,
-+ gpointer user_data)
-+{
-+ GError *error = nullptr;
-+ GDBusProxy *proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
-+ if (error && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-+ g_error_free(error);
-+ return;
-+ }
-+
-+ if (error) {
-+ g_error_free(error);
-+ }
-+
-+ // We need this check because we can't assume that GDBus cancellation
-+ // is reliable (see https://launchpad.net/bugs/953562)
-+ nsNativeMenuService *self = nsNativeMenuService::GetSingleton();
-+ if (!self) {
-+ if (proxy) {
-+ g_object_unref(proxy);
-+ }
-+ return;
-+ }
-+
-+ self->OnProxyCreated(proxy);
-+}
-+
-+/* static */ void
-+nsNativeMenuService::register_native_menubar_cb(GObject *source_object,
-+ GAsyncResult *res,
-+ gpointer user_data)
-+{
-+ nsMenuBar *menuBar = static_cast<nsMenuBar *>(user_data);
-+
-+ GError *error = nullptr;
-+ GVariant *results = g_dbus_proxy_call_finish(G_DBUS_PROXY(source_object),
-+ res, &error);
-+ if (results) {
-+ // There's nothing useful in the response
-+ g_variant_unref(results);
-+ }
-+
-+ bool success = error ? false : true;
-+ if (error && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-+ g_error_free(error);
-+ return;
-+ }
-+
-+ if (error) {
-+ g_error_free(error);
-+ }
-+
-+ nsNativeMenuService *self = nsNativeMenuService::GetSingleton();
-+ if (!self) {
-+ return;
-+ }
-+
-+ self->OnNativeMenuBarRegistered(menuBar, success);
-+}
-+
-+/* static */ gboolean
-+nsNativeMenuService::map_event_cb(GtkWidget *widget,
-+ GdkEvent *event,
-+ gpointer user_data)
-+{
-+ nsMenuBar *menubar = static_cast<nsMenuBar *>(user_data);
-+ nsNativeMenuService::GetSingleton()->RegisterNativeMenuBar(menubar);
-+
-+ return FALSE;
-+}
-+
-+void
-+nsNativeMenuService::OnNameOwnerChanged()
-+{
-+ char *owner = g_dbus_proxy_get_name_owner(mDbusProxy);
-+ SetOnline(owner ? true : false);
-+ g_free(owner);
-+}
-+
-+void
-+nsNativeMenuService::OnProxyCreated(GDBusProxy *aProxy)
-+{
-+ mDbusProxy = aProxy;
-+
-+ g_object_unref(mCreateProxyCancellable);
-+ mCreateProxyCancellable = nullptr;
-+
-+ if (!mDbusProxy) {
-+ SetOnline(false);
-+ return;
-+ }
-+
-+ g_signal_connect(mDbusProxy, "notify::g-name-owner",
-+ G_CALLBACK(name_owner_changed_cb), nullptr);
-+
-+ OnNameOwnerChanged();
-+}
-+
-+void
-+nsNativeMenuService::OnNativeMenuBarRegistered(nsMenuBar *aMenuBar,
-+ bool aSuccess)
-+{
-+ // 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();
-+ }
-+}
-+
-+/* static */ void
-+nsNativeMenuService::PrefChangedCallback(const char *aPref,
-+ void *aClosure)
-+{
-+ nsNativeMenuService::GetSingleton()->PrefChanged();
-+}
-+
-+void
-+nsNativeMenuService::PrefChanged()
-+{
-+ if (!mDbusProxy) {
-+ SetOnline(false);
-+ return;
-+ }
-+
-+ OnNameOwnerChanged();
-+}
-+
-+NS_IMETHODIMP
-+nsNativeMenuService::CreateNativeMenuBar(nsIWidget *aParent,
-+ mozilla::dom::Element *aMenuBarNode)
-+{
-+ NS_ENSURE_ARG(aParent);
-+ NS_ENSURE_ARG(aMenuBarNode);
-+
-+ if (aMenuBarNode->AttrValueIs(kNameSpaceID_None,
-+ nsGkAtoms::_moz_menubarkeeplocal,
-+ nsGkAtoms::_true,
-+ eCaseMatters)) {
-+ return NS_OK;
-+ }
-+
-+ UniquePtr<nsMenuBar> menubar(nsMenuBar::Create(aParent, aMenuBarNode));
-+ if (!menubar) {
-+ NS_WARNING("Failed to create menubar");
-+ return NS_ERROR_FAILURE;
-+ }
-+
-+ // 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());
-+
-+ mMenuBars.AppendElement(menubar.get());
-+ RegisterNativeMenuBar(menubar.get());
-+
-+ static_cast<nsWindow *>(aParent)->SetMenuBar(std::move(menubar));
-+
-+ return NS_OK;
-+}
-+
-+/* static */ already_AddRefed<nsNativeMenuService>
-+nsNativeMenuService::GetInstanceForServiceManager()
-+{
-+ RefPtr<nsNativeMenuService> service(sService);
-+
-+ if (service) {
-+ return service.forget();
-+ }
-+
-+ service = new nsNativeMenuService();
-+
-+ if (NS_FAILED(service->Init())) {
-+ return nullptr;
-+ }
-+
-+ sService = service.get();
-+ return service.forget();
-+}
-+
-+/* static */ nsNativeMenuService*
-+nsNativeMenuService::GetSingleton()
-+{
-+ EnsureInitialized();
-+ return sService;
-+}
-+
-+void
-+nsNativeMenuService::NotifyNativeMenuBarDestroyed(nsMenuBar *aMenuBar)
-+{
-+ g_signal_handlers_disconnect_by_func(aMenuBar->TopLevelWindow(),
-+ FuncToGpointer(map_event_cb),
-+ aMenuBar);
-+
-+ mMenuBars.RemoveElement(aMenuBar);
-+
-+ GCancellable *cancellable = nullptr;
-+ if (mMenuBarRegistrationCancellables.Get(aMenuBar, &cancellable)) {
-+ mMenuBarRegistrationCancellables.Remove(aMenuBar);
-+ g_cancellable_cancel(cancellable);
-+ g_object_unref(cancellable);
-+ }
-+}
-diff --git a/widget/gtk/nsNativeMenuService.h b/widget/gtk/nsNativeMenuService.h
-new file mode 100644
-index 000000000000..2e0d429eddfd
---- /dev/null
-+++ b/widget/gtk/nsNativeMenuService.h
-@@ -0,0 +1,85 @@
-+/* -*- 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 __nsNativeMenuService_h__
-+#define __nsNativeMenuService_h__
-+
-+#include "mozilla/Attributes.h"
-+#include "nsCOMPtr.h"
-+#include "nsTHashMap.h"
-+#include "nsINativeMenuService.h"
-+#include "nsTArray.h"
-+
-+#include <gdk/gdk.h>
-+#include <gio/gio.h>
-+#include <gtk/gtk.h>
-+
-+class nsMenuBar;
-+
-+/*
-+ * The main native menu service singleton.
-+ * NativeMenuSupport::CreateNativeMenuBar calls in to this when a new top level
-+ * window is created.
-+ *
-+ * Menubars are owned by their nsWindow. This service holds a weak reference to
-+ * each menubar for the purpose of re-registering them with the shell if it
-+ * needs to. The menubar is responsible for notifying the service when the last
-+ * reference to it is dropped.
-+ */
-+class nsNativeMenuService final : public nsINativeMenuService
-+{
-+public:
-+ NS_DECL_ISUPPORTS
-+
-+ NS_IMETHOD CreateNativeMenuBar(nsIWidget* aParent, mozilla::dom::Element* aMenuBarNode) override;
-+
-+ // Returns the singleton addref'd for the service manager
-+ static already_AddRefed<nsNativeMenuService> GetInstanceForServiceManager();
-+
-+ // Returns the singleton without increasing the reference count
-+ static nsNativeMenuService* GetSingleton();
-+
-+ // Called by a menubar when it is deleted
-+ void NotifyNativeMenuBarDestroyed(nsMenuBar *aMenuBar);
-+
-+private:
-+ nsNativeMenuService();
-+ ~nsNativeMenuService();
-+ nsresult Init();
-+
-+ static void EnsureInitialized();
-+ void SetOnline(bool aOnline);
-+ void RegisterNativeMenuBar(nsMenuBar *aMenuBar);
-+ static void name_owner_changed_cb(GObject *gobject,
-+ GParamSpec *pspec,
-+ gpointer user_data);
-+ static void proxy_created_cb(GObject *source_object,
-+ GAsyncResult *res,
-+ gpointer user_data);
-+ static void register_native_menubar_cb(GObject *source_object,
-+ GAsyncResult *res,
-+ gpointer user_data);
-+ static gboolean map_event_cb(GtkWidget *widget, GdkEvent *event,
-+ gpointer user_data);
-+ void OnNameOwnerChanged();
-+ void OnProxyCreated(GDBusProxy *aProxy);
-+ void OnNativeMenuBarRegistered(nsMenuBar *aMenuBar,
-+ bool aSuccess);
-+ static void PrefChangedCallback(const char *aPref, void *aClosure);
-+ void PrefChanged();
-+
-+ GCancellable *mCreateProxyCancellable;
-+ GDBusProxy *mDbusProxy;
-+ bool mOnline;
-+ nsTArray<nsMenuBar *> mMenuBars;
-+ nsTHashMap<nsPtrHashKey<nsMenuBar>, GCancellable*> mMenuBarRegistrationCancellables;
-+
-+ static bool sShutdown;
-+ static nsNativeMenuService *sService;
-+};
-+
-+#endif /* __nsNativeMenuService_h__ */
-diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
-index 5f227073d8cd..1ab99c38551f 100644
---- a/widget/gtk/nsWindow.cpp
-+++ b/widget/gtk/nsWindow.cpp
-@@ -7103,6 +7103,10 @@ void nsWindow::HideWindowChrome(bool aShouldHide) {
- SetWindowDecoration(aShouldHide ? eBorderStyle_none : mBorderStyle);
- }
-
-+void nsWindow::SetMenuBar(UniquePtr<nsMenuBar> aMenuBar) {
-+ mMenuBar = std::move(aMenuBar);
-+}
-+
- bool nsWindow::CheckForRollup(gdouble aMouseX, gdouble aMouseY, bool aIsWheel,
- bool aAlwaysRollup) {
- nsIRollupListener* rollupListener = GetActiveRollupListener();
-diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h
-index d4951ec54d1c..90fbd91765b1 100644
---- a/widget/gtk/nsWindow.h
-+++ b/widget/gtk/nsWindow.h
-@@ -27,6 +27,8 @@
- #include "nsRefPtrHashtable.h"
- #include "IMContextWrapper.h"
-
-+#include "nsMenuBar.h"
-+
- #ifdef ACCESSIBILITY
- # include "mozilla/a11y/LocalAccessible.h"
- #endif
-@@ -203,6 +205,8 @@ class nsWindow final : public nsBaseWidget {
- nsresult MakeFullScreen(bool aFullScreen) override;
- void HideWindowChrome(bool aShouldHide) override;
-
-+ 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).
-@@ -874,6 +878,8 @@ class nsWindow final : public nsBaseWidget {
-
- static bool sTransparentMainWindow;
-
-+ mozilla::UniquePtr<nsMenuBar> mMenuBar;
-+
- #ifdef ACCESSIBILITY
- RefPtr<mozilla::a11y::LocalAccessible> mRootAccessible;
-
-diff --git a/widget/moz.build b/widget/moz.build
-index 9b150c592a8c..77bd0cbbe5c5 100644
---- a/widget/moz.build
-+++ b/widget/moz.build
-@@ -161,6 +161,11 @@ EXPORTS += [
- "PuppetWidget.h",
- ]
-
-+if toolkit == "gtk":
-+ EXPORTS += [
-+ "nsINativeMenuService.h",
-+ ]
-+
- EXPORTS.mozilla += [
- "BasicEvents.h",
- "ColorScheme.h",
-diff --git a/widget/nsINativeMenuService.h b/widget/nsINativeMenuService.h
-new file mode 100644
-index 000000000000..e92d7a74a3bc
---- /dev/null
-+++ b/widget/nsINativeMenuService.h
-@@ -0,0 +1,39 @@
-+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-+/* 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 nsINativeMenuService_h_
-+#define nsINativeMenuService_h_
-+
-+#include "nsISupports.h"
-+
-+class nsIWidget;
-+class nsIContent;
-+namespace mozilla {
-+namespace dom {
-+class Element;
-+}
-+} // namespace mozilla
-+
-+// {90DF88F9-F084-4EF3-829A-49496E636DED}
-+#define NS_INATIVEMENUSERVICE_IID \
-+ { \
-+ 0x90DF88F9, 0xF084, 0x4EF3, { \
-+ 0x82, 0x9A, 0x49, 0x49, 0x6E, 0x63, 0x6D, 0xED \
-+ } \
-+ }
-+
-+class nsINativeMenuService : public nsISupports {
-+ public:
-+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_INATIVEMENUSERVICE_IID)
-+ // Given a top-level window widget and a menu bar DOM node, sets up native
-+ // menus. Once created, native menus are controlled via the DOM, including
-+ // destruction.
-+ NS_IMETHOD CreateNativeMenuBar(nsIWidget* aParent,
-+ mozilla::dom::Element* aMenuBarNode) = 0;
-+};
-+
-+NS_DEFINE_STATIC_IID_ACCESSOR(nsINativeMenuService, NS_INATIVEMENUSERVICE_IID)
-+
-+#endif // nsINativeMenuService_h_
-diff --git a/widget/nsWidgetsCID.h b/widget/nsWidgetsCID.h
-index 8e0f67661414..911711e88a0e 100644
---- a/widget/nsWidgetsCID.h
-+++ b/widget/nsWidgetsCID.h
-@@ -66,6 +66,14 @@
- // Menus
- //-----------------------------------------------------------
-
-+// {0B3FE5AA-BC72-4303-85AE-76365DF1251D}
-+#define NS_NATIVEMENUSERVICE_CID \
-+ { \
-+ 0x0B3FE5AA, 0xBC72, 0x4303, { \
-+ 0x85, 0xAE, 0x76, 0x36, 0x5D, 0xF1, 0x25, 0x1D \
-+ } \
-+ }
-+
- // {F6CD4F21-53AF-11d2-8DC4-00609703C14E}
- #define NS_POPUPMENU_CID \
- { \
-diff --git a/xpcom/ds/NativeMenuAtoms.py b/xpcom/ds/NativeMenuAtoms.py
-new file mode 100644
-index 000000000000..488c8f49c021
---- /dev/null
-+++ b/xpcom/ds/NativeMenuAtoms.py
-@@ -0,0 +1,9 @@
-+from Atom import Atom
-+
-+NATIVE_MENU_ATOMS = [
-+ Atom("menuitem_with_favicon", "menuitem-with-favicon"),
-+ Atom("_moz_menubarkeeplocal", "_moz-menubarkeeplocal"),
-+ Atom("_moz_nativemenupopupstate", "_moz-nativemenupopupstate"),
-+ Atom("openedwithkey", "openedwithkey"),
-+ Atom("shellshowingmenubar", "shellshowingmenubar"),
-+]
-diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py
-index 740b3f6e187d..3ca7630abf25 100644
---- a/xpcom/ds/StaticAtoms.py
-+++ b/xpcom/ds/StaticAtoms.py
-@@ -7,6 +7,7 @@
- from Atom import Atom, InheritingAnonBoxAtom, NonInheritingAnonBoxAtom
- from Atom import PseudoElementAtom
- from HTMLAtoms import HTML_PARSER_ATOMS
-+from NativeMenuAtoms import NATIVE_MENU_ATOMS
- import sys
-
- # Static atom definitions, used to generate nsGkAtomList.h.
-@@ -2536,7 +2537,7 @@ STATIC_ATOMS = [
- InheritingAnonBoxAtom("AnonBox_mozSVGForeignContent", ":-moz-svg-foreign-content"),
- InheritingAnonBoxAtom("AnonBox_mozSVGText", ":-moz-svg-text"),
- # END ATOMS
--] + HTML_PARSER_ATOMS
-+] + HTML_PARSER_ATOMS + NATIVE_MENU_ATOMS
- # fmt: on
-
-
-diff --git a/xpfe/appshell/AppWindow.cpp b/xpfe/appshell/AppWindow.cpp
-index fb36837f23da..c6b6aae09ac8 100644
---- a/xpfe/appshell/AppWindow.cpp
-+++ b/xpfe/appshell/AppWindow.cpp
-@@ -80,7 +80,7 @@
-
- #include "mozilla/dom/DocumentL10n.h"
-
--#ifdef XP_MACOSX
-+#if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
- # include "mozilla/widget/NativeMenuSupport.h"
- # define USE_NATIVE_MENUS
- #endif
---
-2.37.3
-
diff --git a/waterfox-g/debian/patches/libavcodec58_91.patch b/waterfox-g/debian/patches/libavcodec58_91.patch
index eff1d60..48aa2af 100644
--- a/waterfox-g/debian/patches/libavcodec58_91.patch
+++ b/waterfox-g/debian/patches/libavcodec58_91.patch
@@ -1,16 +1,13 @@
diff --git a/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp b/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
-index 91bf38df8354..e79bc068668a 100644
+index 7716162e0707..a9bed062905d 100644
--- a/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
-@@ -39,6 +39,8 @@ static const char* sLibs[] = {
- // of ffmpeg and update it regulary on ABI/API changes
+@@ -41,6 +41,8 @@ static const char* sLibs[] = {
#else
+ "libavcodec.so.60",
"libavcodec.so.59",
+ "libavcodec.so.58.134",
+ "libavcodec.so.58.91",
"libavcodec.so.58",
"libavcodec-ffmpeg.so.58",
"libavcodec-ffmpeg.so.57",
---
-2.37.3
-
diff --git a/waterfox-g/debian/patches/mach-depends.patch b/waterfox-g/debian/patches/mach-depends.patch
index de85066..a499d45 100644
--- a/waterfox-g/debian/patches/mach-depends.patch
+++ b/waterfox-g/debian/patches/mach-depends.patch
@@ -1,24 +1,21 @@
diff --git a/python/sites/mach.txt b/python/sites/mach.txt
-index 6547ee58574a..a7d9a8b2ca70 100644
+index 83dec4a14237..effb59fc457f 100644
--- a/python/sites/mach.txt
+++ b/python/sites/mach.txt
-@@ -114,7 +114,7 @@ vendored:third_party/python/taskcluster
- vendored:third_party/python/taskcluster_taskgraph
+@@ -121,7 +121,7 @@ vendored:third_party/python/taskcluster_taskgraph
vendored:third_party/python/taskcluster_urls
+ vendored:third_party/python/toml
vendored:third_party/python/tqdm
-vendored:third_party/python/typing_extensions
+pypi-optional:typing_extensions>=3.10.0:something will break
vendored:third_party/python/urllib3
vendored:third_party/python/voluptuous
- vendored:third_party/python/wheel
-@@ -133,5 +133,5 @@ pypi-optional:glean-sdk==44.1.1:telemetry will not be collected
+ vendored:third_party/python/wcwidth
+@@ -141,5 +141,5 @@ pypi-optional:glean-sdk==52.7.0:telemetry will not be collected
# Mach gracefully handles the case where `psutil` is unavailable.
# We aren't (yet) able to pin packages in automation, so we have to
# support down to the oldest locally-installed version (5.4.2).
--pypi-optional:psutil>=5.4.2,<=5.8.0:telemetry will be missing some data
--pypi-optional:zstandard>=0.11.1,<=0.17.0:zstd archives will not be possible to extract
+-pypi-optional:psutil>=5.4.2,<=5.9.4:telemetry will be missing some data
+-pypi-optional:zstandard>=0.11.1,<=0.19.0:zstd archives will not be possible to extract
+pypi-optional:psutil>=5.4.2,<=6.0.0:telemetry will be missing some data
+pypi-optional:zstandard>=0.11.1,<=1.0.0:zstd archives will not be possible to extract
---
-2.37.3
-
diff --git a/waterfox-g/debian/patches/mozilla-ntlm-full-path.patch b/waterfox-g/debian/patches/mozilla-ntlm-full-path.patch
index ce5b6a2..9c7a8db 100644
--- a/waterfox-g/debian/patches/mozilla-ntlm-full-path.patch
+++ b/waterfox-g/debian/patches/mozilla-ntlm-full-path.patch
@@ -1,18 +1,10 @@
-# HG changeset patch
# User Petr Cerny <pcerny@novell.com>
-# Parent 7308e4a7c1f769f4bbbc90870b849cadd99495a6
-# Parent 2361c5db1e70e358b2158325e07fa15bb4569c2c
-Bug 634334 - call to the ntlm_auth helper fails
diff --git a/extensions/auth/nsAuthSambaNTLM.cpp b/extensions/auth/nsAuthSambaNTLM.cpp
+index 5b701f237978..4004a5cfc79a 100644
--- a/extensions/auth/nsAuthSambaNTLM.cpp
+++ b/extensions/auth/nsAuthSambaNTLM.cpp
-@@ -156,17 +156,17 @@ static uint8_t* ExtractMessage(const nsA
- *aLen = (length / 4) * 3 - numEquals;
- return reinterpret_cast<uint8_t*>(PL_Base64Decode(s, length, nullptr));
- }
-
- nsresult nsAuthSambaNTLM::SpawnNTLMAuthHelper() {
+@@ -160,7 +160,7 @@ nsresult nsAuthSambaNTLM::SpawnNTLMAuthHelper() {
const char* username = PR_GetEnv("USER");
if (!username) return NS_ERROR_FAILURE;
@@ -21,8 +13,3 @@ diff --git a/extensions/auth/nsAuthSambaNTLM.cpp b/extensions/auth/nsAuthSambaNT
"--helper-protocol",
"ntlmssp-client-1",
"--use-cached-creds",
- "--username",
- username,
- nullptr};
-
- bool isOK = SpawnIOChild(const_cast<char* const*>(args), &mChildPID,
diff --git a/waterfox-g/debian/patches/nongnome-proxies.patch b/waterfox-g/debian/patches/nongnome-proxies.patch
index 5eaf79d..ff5e4b9 100644
--- a/waterfox-g/debian/patches/nongnome-proxies.patch
+++ b/waterfox-g/debian/patches/nongnome-proxies.patch
@@ -2,14 +2,10 @@ From: Wolfgang Rosenauer
Subject: Do not use gconf for proxy settings if not running within Gnome
diff --git a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
+index 982faf7d7968..185dc1e22a90 100644
--- a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
+++ b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
-@@ -49,20 +49,24 @@ NS_IMETHODIMP
- nsUnixSystemProxySettings::GetMainThreadOnly(bool* aMainThreadOnly) {
- // dbus prevents us from being threadsafe, but this routine should not block
- // anyhow
- *aMainThreadOnly = true;
- return NS_OK;
+@@ -52,10 +52,14 @@ nsUnixSystemProxySettings::GetMainThreadOnly(bool* aMainThreadOnly) {
}
void nsUnixSystemProxySettings::Init() {
@@ -28,8 +24,3 @@ diff --git a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp b/toolkit/sy
}
}
- nsresult nsUnixSystemProxySettings::GetPACURI(nsACString& aResult) {
- if (mProxySettings) {
- nsCString proxyMode;
- // Check if mode is auto
- nsresult rv = mProxySettings->GetString("mode"_ns, proxyMode);
diff --git a/waterfox-g/debian/patches/series b/waterfox-g/debian/patches/series
index d6ead97..0b9bea1 100644
--- a/waterfox-g/debian/patches/series
+++ b/waterfox-g/debian/patches/series
@@ -1,9 +1,10 @@
-global_menu.patch -p1
fis-csd-global-menu.patch -p1
+# stackrpms, 2
#g-kde.patch -p1
nongnome-proxies.patch -p1
mozilla-ntlm-full-path.patch -p1
libavcodec58_91.patch -p1
fix-langpack-id.patch -p1
-waterfox-branded-icons.patch -p1
fix-wayland-build.patch -p1
+debian-hacks/Relax-nodejs-minimum-version.patch -p1
+debian-hacks/Relax-cargo-version-requirement.patch -p1
diff --git a/waterfox-g/debian/patches/waterfox-branded-icons.patch b/waterfox-g/debian/patches/waterfox-branded-icons.patch
deleted file mode 100644
index e0778a5..0000000
--- a/waterfox-g/debian/patches/waterfox-branded-icons.patch
+++ /dev/null
@@ -1,19 +0,0 @@
-diff --git a/browser/branding/branding-common.mozbuild b/browser/branding/branding-common.mozbuild
---- a/browser/branding/branding-common.mozbuild
-+++ b/browser/branding/branding-common.mozbuild
-@@ -22,12 +22,15 @@ def FirefoxBranding():
- FINAL_TARGET_FILES.VisualElements += [
- 'VisualElements_150.png',
- 'VisualElements_70.png',
- ]
- elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk':
- FINAL_TARGET_FILES.chrome.icons.default += [
- 'default128.png',
- 'default16.png',
-+ 'default22.png',
-+ 'default24.png',
-+ 'default256.png',
- 'default32.png',
- 'default48.png',
- 'default64.png',
- ]
diff --git a/waterfox-g/debian/rules b/waterfox-g/debian/rules
index 462dd96..21db460 100755
--- a/waterfox-g/debian/rules
+++ b/waterfox-g/debian/rules
@@ -5,60 +5,38 @@ export SHELL=/bin/bash
export MOZCONFIG=$(shell pwd)/debian/mozconfig
export MOZ_NOSPAM:=1
export MOZ_SOURCE_REPO=https://github.com/WaterfoxCo/Waterfox
+# stackrpms,3
export MOZ_SOURCE_CHANGESET=$(shell awk -F ': ' '/^commit:/ {print $$2; exit}' ../SOURCES/waterfox-g.obsinfo)
export WF_VERSION=$(shell awk -F ': ' '/^version:/ {print "G"$$2; exit}' ../SOURCES/waterfox-g.obsinfo)
export TODAY_DATE=$(shell date +%Y-%m-%d)
export LC_ALL=C.UTF-8
-
-LDFLAGS += -Wl,--no-keep-memory -Wl,--no-mmap-output-file
-
-distrelease := $(shell lsb_release -sc)
-ifeq ($(distrelease),$(filter $(distrelease),stretch xenial))
-LDFLAGS += -static-libstdc++
-endif
-
-export LDFLAGS
-
export JOBS=$(shell echo $(shell grep -c ^processor /proc/cpuinfo)\/2 | bc)
-
export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
export DEB_BUILD_MAINT_OPTIONS = optimize=-lto
+include /usr/share/dpkg/buildflags.mk
+export CXXFLAGS CFLAGS LDFLAGS
%:
dh $@
+override_dh_auto_configure:
+
override_dh_auto_clean:
dh_auto_clean
find . -name '*.pyc' -delete
override_dh_auto_build:
- # Upstream modifies version_display.txt using script included in GitHub Actions (version=git_tag),
- # without patching. Debhelper won't allow us to do that by same way and
- # patching won't be much convenient, so we need to create new files.
- mkdir -p $$(pwd)/debian/app_version
- cp $$(pwd)/browser/config/version.txt $$(pwd)/debian/app_version/version.txt
- echo $$WF_VERSION > $$(pwd)/debian/app_version/version_display.txt
- # Build browser
- # LTO needs more open files
- ulimit -n 4096; xvfb-run -a -n 97 -s "-screen 0 1920x1080x24" ./mach build
-
- # Build langpacks
- mkdir -p $$(pwd)/extensions
- # langpack-build can not be done in parallel easily (see https://bugzilla.mozilla.org/show_bug.cgi?id=1660943)
- # Therefore, we have to have a separate obj-dir for each language
- # We do this, by creating a mozconfig-template with the necessary switches
- # and a placeholder obj-dir, which gets copied and modified for each language
- sed -r '/^(ja-JP-mac|en-US|)$$/d;s/ .*$$//' debian/locales.shipped | cut -f1 -d":" \
- | xargs -n 1 -P $$JOBS -I {} /bin/sh -c 'locale=$$1; cp debian/mozconfig_LANG mozconfig_$$locale; sed -i "s|obj_LANG|obj_$$locale|" mozconfig_$$locale; export MOZCONFIG=mozconfig_$$locale; ./mach build config/nsinstall langpack-$$locale; cp -L ../obj_$$locale/dist/linux-*/xpi/waterfox-g-$$WF_VERSION.$$locale.langpack.xpi \
- $$(pwd)/extensions/langpack-$$locale@l10n.waterfox.net.xpi' -- {}
+ $$(pwd)/debian/build.sh
override_dh_auto_install:
chmod +x $$(pwd)/debian/waterfox-g-bin.sh
+ # stackrpms,4
DESTDIR=$$(pwd)/debian/waterfox-g ./mach install
rm -rf $$(pwd)/debian/waterfox-g/usr/lib/waterfox-g/dictionaries
rm -rf $$(pwd)/debian/waterfox-g/usr/lib/waterfox-g/waterfox-g-bin
sed -i "s/__DATE__/$$TODAY_DATE/g" $$(pwd)/debian/waterfox-g.appdata.xml.in
+ # stackrpms,2
sed -e "s/__VERSION__/$$WF_VERSION/g" $$(pwd)/debian/waterfox-g.appdata.xml.in > $$(pwd)/debian/waterfox-g/usr/share/metainfo/waterfox-g.appdata.xml
mv $$(pwd)/debian/waterfox-g-wayland-bin.sh $$(pwd)/debian/waterfox-g-wayland/usr/bin/waterfox-g-wayland
@@ -66,4 +44,3 @@ override_dh_shlibdeps:
dh_shlibdeps -l /usr/lib/waterfox-g/waterfox-g
override_dh_strip_nondeterminism:
- dh_strip_nondeterminism -Xdebian/waterfox-g/usr/lib/waterfox-g/browser/features/formautofill@mozilla.org.xpi
bgstack15