summaryrefslogtreecommitdiff
path: root/waterfox-g/debian/patches
diff options
context:
space:
mode:
Diffstat (limited to 'waterfox-g/debian/patches')
-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.patch18
-rw-r--r--waterfox-g/debian/patches/fix-langpack-id.patch64
-rw-r--r--waterfox-g/debian/patches/fix-wayland-build.patch21
-rw-r--r--waterfox-g/debian/patches/g-kde.patch1656
-rw-r--r--waterfox-g/debian/patches/libavcodec58_91.patch13
-rw-r--r--waterfox-g/debian/patches/mach-depends.patch21
-rw-r--r--waterfox-g/debian/patches/mozilla-ntlm-full-path.patch15
-rw-r--r--waterfox-g/debian/patches/nongnome-proxies.patch26
-rw-r--r--waterfox-g/debian/patches/series10
11 files changed, 1886 insertions, 0 deletions
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
new file mode 100644
index 0000000..6f2d2dc
--- /dev/null
+++ b/waterfox-g/debian/patches/fis-csd-global-menu.patch
@@ -0,0 +1,18 @@
+diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css
+index 4279c9ccfc83..8573feb7cdfb 100644
+--- a/browser/base/content/browser.css
++++ b/browser/base/content/browser.css
+@@ -359,6 +359,13 @@ toolbar[customizing] #whats-new-menu-button {
+ visibility: hidden;
+ }
+
++@media (-moz-platform: linux) {
++ *|*:root[shellshowingmenubar="true"]
++ #toolbar-menubar[autohide="true"]:not([inactive]) + #TabsToolbar > .titlebar-buttonbox-container {
++ visibility: visible !important;
++ }
++}
++
+ :root[tabsintitlebar] .titlebar-buttonbox {
+ position: relative;
+ }
diff --git a/waterfox-g/debian/patches/fix-langpack-id.patch b/waterfox-g/debian/patches/fix-langpack-id.patch
new file mode 100644
index 0000000..4159530
--- /dev/null
+++ b/waterfox-g/debian/patches/fix-langpack-id.patch
@@ -0,0 +1,64 @@
+diff --git a/browser/components/preferences/tests/browser_browser_languages_subdialog.js b/browser/components/preferences/tests/browser_browser_languages_subdialog.js
+index 8b57bf08a83b..dd6f22094a93 100644
+--- a/browser/components/preferences/tests/browser_browser_languages_subdialog.js
++++ b/browser/components/preferences/tests/browser_browser_languages_subdialog.js
+@@ -13,7 +13,7 @@ const DICTIONARY_ID_PL = "pl@dictionaries.addons.mozilla.org";
+ const TELEMETRY_CATEGORY = "intl.ui.browserLanguage";
+
+ function langpackId(locale) {
+- return `langpack-${locale}@firefox.mozilla.org`;
++ return `langpack-${locale}@l10n.waterfox.net`;
+ }
+
+ function getManifestData(locale, version = "2.0") {
+@@ -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.
+- langpack = await AddonManager.getAddonByID("langpack-pl@firefox.mozilla.org");
++ langpack = await AddonManager.getAddonByID("langpack-pl@l10n.waterfox.net");
+ await langpack.disable();
+
+ ({ dialogDoc, available, selected } = await openDialog(doc, true));
+diff --git a/browser/locales/Makefile.in b/browser/locales/Makefile.in
+index 1b8f21be1ca6..7ca5d391219a 100644
+--- a/browser/locales/Makefile.in
++++ b/browser/locales/Makefile.in
+@@ -21,9 +21,9 @@ PWD := $(CURDIR)
+ ZIP_IN ?= $(ABS_DIST)/$(PACKAGE)
+
+ ifdef MOZ_DEV_EDITION
+-MOZ_LANGPACK_EID=langpack-$(AB_CD)@devedition.mozilla.org
++MOZ_LANGPACK_EID=langpack-$(AB_CD)@l10n.waterfox.net
+ else
+-MOZ_LANGPACK_EID=langpack-$(AB_CD)@firefox.mozilla.org
++MOZ_LANGPACK_EID=langpack-$(AB_CD)@l10n.waterfox.net
+ endif
+ # For Nightly, we know where to get the builds from to do local repacks
+ ifdef NIGHTLY_BUILD
+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 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,
diff --git a/waterfox-g/debian/patches/fix-wayland-build.patch b/waterfox-g/debian/patches/fix-wayland-build.patch
new file mode 100644
index 0000000..f0309fa
--- /dev/null
+++ b/waterfox-g/debian/patches/fix-wayland-build.patch
@@ -0,0 +1,21 @@
+Description: Fix FTBFS on bionic. Compiler errors:
+ In file included from Unified_cpp_widget_gtk1.cpp:2:
+ /<<BUILDDIR>>/firefox-92.0~b2+build1/widget/gtk/WaylandBuffer.cpp:261:39: error: unknown type name 'GLContext'; did you mean 'EGLContext'?
+ const LayoutDeviceIntSize& aSize, GLContext* aGL) {
+ ^~~~~~~~~
+
+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
+@@ -197,7 +197,7 @@ void WaylandBufferSHM::DumpToFile(const char* aHint) {
+
+ /* static */
+ RefPtr<WaylandBufferDMABUF> WaylandBufferDMABUF::Create(
+- const LayoutDeviceIntSize& aSize, GLContext* aGL) {
++ const LayoutDeviceIntSize& aSize, gl::GLContext* aGL) {
+ RefPtr<WaylandBufferDMABUF> buffer = new WaylandBufferDMABUF(aSize);
+
+ const auto flags =
diff --git a/waterfox-g/debian/patches/g-kde.patch b/waterfox-g/debian/patches/g-kde.patch
new file mode 100644
index 0000000..6fb67eb
--- /dev/null
+++ b/waterfox-g/debian/patches/g-kde.patch
@@ -0,0 +1,1656 @@
+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/firefox115
+
+diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js
+index 95ed194f4bdd..90460d8bab88 100644
+--- a/browser/components/preferences/main.js
++++ b/browser/components/preferences/main.js
+@@ -300,6 +300,13 @@ var gMainPane = {
+ }, backoffTimes[this._backoffIndex]);
+ }
+
++ var env = Components.classes["@mozilla.org/process/environment;1"]
++ .getService(Components.interfaces.nsIEnvironment);
++ var kde_session = 0;
++ if (env.get('KDE_FULL_SESSION') == "true") {
++ kde_session = 1;
++ }
++
+ this.initBrowserContainers();
+ this.buildContentProcessCountMenuList();
+
+@@ -1771,6 +1778,17 @@ var gMainPane = {
+ }
+ try {
+ shellSvc.setDefaultBrowser(true, false);
++ if (kde_session == 1) {
++ var shellObj = Components.classes["@mozilla.org/file/local;1"]
++ .createInstance(Components.interfaces.nsILocalFile);
++ shellObj.initWithPath("/usr/bin/kwriteconfig");
++ var process = Components.classes["@mozilla.org/process/util;1"]
++ .createInstance(Components.interfaces.nsIProcess);
++ process.init(shellObj);
++ var args = ["--file", "kdeglobals", "--group", "General", "--key",
++ "BrowserApplication", "waterfox-g"];
++ process.run(false, args, args.length);
++ }
+ } catch (ex) {
+ console.error(ex);
+ return;
+diff --git a/browser/components/shell/moz.build b/browser/components/shell/moz.build
+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":
+
+ SOURCES += [
+ "nsGNOMEShellService.cpp",
++ "nsKDEShellService.cpp",
++ "nsUnixShellService.cpp",
+ ]
+ if CONFIG["MOZ_ENABLE_DBUS"]:
+ SOURCES += [
+diff --git a/browser/components/shell/nsKDEShellService.cpp b/browser/components/shell/nsKDEShellService.cpp
+new file mode 100644
+index 000000000000..152a3aca87ea
+--- /dev/null
++++ b/browser/components/shell/nsKDEShellService.cpp
+@@ -0,0 +1,109 @@
++/* -*- 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/. */
++
++#include "mozilla/ArrayUtils.h"
++
++#include "nsCOMPtr.h"
++#include "nsKDEShellService.h"
++#include "nsShellService.h"
++#include "nsKDEUtils.h"
++#include "nsIPrefService.h"
++#include "nsIProcess.h"
++#include "nsIFile.h"
++#include "nsServiceManagerUtils.h"
++#include "nsComponentManagerUtils.h"
++#include "nsIMutableArray.h"
++#include "nsISupportsPrimitives.h"
++#include "nsArrayUtils.h"
++
++using namespace mozilla;
++
++nsresult
++nsKDEShellService::Init()
++{
++ if( !nsKDEUtils::kdeSupport())
++ return NS_ERROR_NOT_AVAILABLE;
++ return NS_OK;
++}
++
++NS_IMPL_ISUPPORTS(nsKDEShellService, nsIGNOMEShellService, nsIShellService)
++
++NS_IMETHODIMP
++nsKDEShellService::IsDefaultBrowser(bool aForAllTypes,
++ bool* aIsDefaultBrowser)
++{
++ *aIsDefaultBrowser = false;
++
++ nsCOMPtr<nsIMutableArray> command = do_CreateInstance( NS_ARRAY_CONTRACTID );
++ if (!command)
++ return NS_ERROR_FAILURE;
++
++ nsCOMPtr<nsISupportsCString> str = do_CreateInstance( NS_SUPPORTS_CSTRING_CONTRACTID );
++ if (!str)
++ return NS_ERROR_FAILURE;
++
++ str->SetData("ISDEFAULTBROWSER"_ns);
++ command->AppendElement( str );
++
++ if( nsKDEUtils::command( command ))
++ *aIsDefaultBrowser = true;
++ return NS_OK;
++}
++
++NS_IMETHODIMP
++nsKDEShellService::SetDefaultBrowser(bool aClaimAllTypes,
++ bool aForAllUsers)
++{
++ nsCOMPtr<nsIMutableArray> command = do_CreateInstance( NS_ARRAY_CONTRACTID );
++ if (!command)
++ return NS_ERROR_FAILURE;
++
++ nsCOMPtr<nsISupportsCString> cmdstr = do_CreateInstance( NS_SUPPORTS_CSTRING_CONTRACTID );
++ nsCOMPtr<nsISupportsCString> paramstr = do_CreateInstance( NS_SUPPORTS_CSTRING_CONTRACTID );
++ if (!cmdstr || !paramstr)
++ return NS_ERROR_FAILURE;
++
++ cmdstr->SetData("SETDEFAULTBROWSER"_ns);
++ command->AppendElement( cmdstr );
++
++ paramstr->SetData( aClaimAllTypes ? "ALLTYPES"_ns : "NORMAL"_ns );
++ command->AppendElement( paramstr );
++
++ return nsKDEUtils::command( command ) ? NS_OK : NS_ERROR_FAILURE;
++}
++
++NS_IMETHODIMP
++nsKDEShellService::GetCanSetDesktopBackground(bool* aResult)
++{
++ *aResult = true;
++ return NS_OK;
++}
++
++NS_IMETHODIMP
++nsKDEShellService::SetDesktopBackground(dom::Element* aElement,
++ int32_t aPosition,
++ const nsACString& aImageName)
++{
++ return NS_ERROR_NOT_IMPLEMENTED;
++}
++
++NS_IMETHODIMP
++nsKDEShellService::GetDesktopBackgroundColor(PRUint32 *aColor)
++{
++ return NS_ERROR_NOT_IMPLEMENTED;
++}
++
++NS_IMETHODIMP
++nsKDEShellService::SetDesktopBackgroundColor(PRUint32 aColor)
++{
++ return NS_ERROR_NOT_IMPLEMENTED;
++}
++
++NS_IMETHODIMP
++nsKDEShellService::IsDefaultForScheme(nsTSubstring<char> const& aScheme, bool* aIsDefaultBrowser)
++{
++ return NS_ERROR_NOT_IMPLEMENTED;
++}
++
+diff --git a/browser/components/shell/nsKDEShellService.h b/browser/components/shell/nsKDEShellService.h
+new file mode 100644
+index 000000000000..8b0bb1916435
+--- /dev/null
++++ b/browser/components/shell/nsKDEShellService.h
+@@ -0,0 +1,32 @@
++/* -*- 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 nskdeshellservice_h____
++#define nskdeshellservice_h____
++
++#include "nsIGNOMEShellService.h"
++#include "nsToolkitShellService.h"
++#include "nsString.h"
++#include "mozilla/Attributes.h"
++
++class nsKDEShellService final : public nsIGNOMEShellService,
++ public nsToolkitShellService
++{
++public:
++ nsKDEShellService() : mCheckedThisSession(false) { }
++
++ NS_DECL_ISUPPORTS
++ NS_DECL_NSISHELLSERVICE
++ NS_DECL_NSIGNOMESHELLSERVICE
++
++ nsresult Init();
++
++private:
++ ~nsKDEShellService() {}
++
++ bool mCheckedThisSession;
++};
++
++#endif // nskdeshellservice_h____
+diff --git a/browser/components/shell/nsUnixShellService.cpp b/browser/components/shell/nsUnixShellService.cpp
+new file mode 100644
+index 000000000000..abf266ebdc52
+--- /dev/null
++++ b/browser/components/shell/nsUnixShellService.cpp
+@@ -0,0 +1,22 @@
++/* -*- 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/. */
++
++
++#include "nsUnixShellService.h"
++#include "nsGNOMEShellService.h"
++#include "nsKDEShellService.h"
++#include "nsKDEUtils.h"
++#include "mozilla/ModuleUtils.h"
++
++NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGNOMEShellService, Init)
++NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsKDEShellService, Init)
++
++NS_IMETHODIMP
++nsUnixShellServiceConstructor(REFNSIID aIID, void **aResult)
++{
++ if( nsKDEUtils::kdeSupport())
++ return nsKDEShellServiceConstructor( aIID, aResult );
++ return nsGNOMEShellServiceConstructor( aIID, aResult );
++}
+diff --git a/browser/components/shell/nsUnixShellService.h b/browser/components/shell/nsUnixShellService.h
+new file mode 100644
+index 000000000000..26b5dbac47dd
+--- /dev/null
++++ b/browser/components/shell/nsUnixShellService.h
+@@ -0,0 +1,15 @@
++/* -*- 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 nsunixshellservice_h____
++#define nsunixshellservice_h____
++
++#include "nsIGNOMEShellService.h"
++
++NS_IMETHODIMP
++nsUnixShellServiceConstructor(nsISupports *aOuter, REFNSIID aIID, void **aResult);
++
++#endif // nsunixshellservice_h____
+diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp
+index c04744a8b936..c2f1cd5a1e1a 100644
+--- a/modules/libpref/Preferences.cpp
++++ b/modules/libpref/Preferences.cpp
+@@ -95,6 +95,7 @@
+ #ifdef MOZ_BACKGROUNDTASKS
+ # include "mozilla/BackgroundTasks.h"
+ #endif
++#include "nsKDEUtils.h"
+
+ #ifdef DEBUG
+ # include <map>
+@@ -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";
++ break;
++ }
++ }
++ }
++
+ rv = pref_LoadPrefsInDir(defaultPrefDir, specialFiles,
+ ArrayLength(specialFiles));
+ if (NS_FAILED(rv)) {
+@@ -4985,7 +4996,7 @@ nsresult Preferences::InitInitialObjects(bool aIsStartup) {
+ }
+
+ // Do we care if a file provided by this process fails to load?
+- pref_LoadPrefsInDir(path, nullptr, 0);
++ pref_LoadPrefsInDir(path, specialFiles, ArrayLength(specialFiles));
+ }
+ }
+
+diff --git a/modules/libpref/moz.build b/modules/libpref/moz.build
+index e8f8b97170d3..831001cee4b1 100644
+--- a/modules/libpref/moz.build
++++ b/modules/libpref/moz.build
+@@ -126,6 +126,10 @@ UNIFIED_SOURCES += [
+ "SharedPrefMap.cpp",
+ ]
+
++LOCAL_INCLUDES += [
++ '/toolkit/xre'
++]
++
+ gen_all_tuple = tuple(gen_h + gen_cpp + gen_rs)
+
+ GeneratedFile(
+diff --git a/python/mozbuild/mozpack/chrome/flags.py b/python/mozbuild/mozpack/chrome/flags.py
+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):
+ "tablet": Flag,
+ "process": StringFlag,
+ "backgroundtask": StringFlag,
++ "desktop": StringFlag,
+ }
+ RE = re.compile(r"([!<>=]+)")
+
+diff --git a/python/mozbuild/mozpack/chrome/manifest.py b/python/mozbuild/mozpack/chrome/manifest.py
+index 14c11d4c1daa..41b9969e7277 100644
+--- a/python/mozbuild/mozpack/chrome/manifest.py
++++ b/python/mozbuild/mozpack/chrome/manifest.py
+@@ -43,6 +43,7 @@ class ManifestEntry(object):
+ "process",
+ "contentaccessible",
+ "backgroundtask",
++ "desktop",
+ ]
+
+ def __init__(self, base, *flags):
+diff --git a/toolkit/components/downloads/moz.build b/toolkit/components/downloads/moz.build
+index 3818e8c0db1e..b70986db8111 100644
+--- a/toolkit/components/downloads/moz.build
++++ b/toolkit/components/downloads/moz.build
+@@ -51,5 +51,9 @@ if CONFIG["MOZ_PLACES"]:
+
+ FINAL_LIBRARY = "xul"
+
++LOCAL_INCLUDES += [
++ '/toolkit/xre'
++]
++
+ with Files("**"):
+ BUG_COMPONENT = ("Toolkit", "Downloads API")
+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) {
+- var nsIApplicationChooser = Ci.nsIApplicationChooser;
+- var appChooser = Cc["@mozilla.org/applicationchooser;1"].createInstance(
+- nsIApplicationChooser
+- );
+- appChooser.init(
+- this.mDialog,
+- this.dialogElement("strings").getString("chooseAppFilePickerTitle")
+- );
+- var contentTypeDialogObj = this;
+- let appChooserCallback = function appChooserCallback_done(aResult) {
+- if (aResult) {
+- contentTypeDialogObj.chosenApp = aResult.QueryInterface(
+- Ci.nsILocalHandlerApp
+- );
+- }
+- contentTypeDialogObj.finishChooseApp();
+- };
+- appChooser.open(this.mLauncher.MIMEInfo.MIMEType, appChooserCallback);
+- // The finishChooseApp is called from appChooserCallback
+- return;
++ // handle the KDE case which is implemented in the filepicker
++ // therefore falling back to Gtk2 like behaviour if KDE is running
++ // FIXME this should be better handled in the nsIApplicationChooser
++ // interface
++ var env = Components.classes["@mozilla.org/process/environment;1"]
++ .getService(Components.interfaces.nsIEnvironment);
++ if (env.get('KDE_FULL_SESSION') == "true")
++ {
++ var nsIFilePicker = Ci.nsIFilePicker;
++ var fp = Cc["@mozilla.org/filepicker;1"]
++ .createInstance(nsIFilePicker);
++ fp.init(this.mDialog,
++ this.dialogElement("strings").getString("chooseAppFilePickerTitle"),
++ nsIFilePicker.modeOpen);
++
++ fp.appendFilters(nsIFilePicker.filterApps);
++
++ fp.open(aResult => {
++ if (aResult == nsIFilePicker.returnOK && fp.file) {
++ // Remember the file they chose to run.
++ var localHandlerApp =
++ Cc["@mozilla.org/uriloader/local-handler-app;1"].
++ createInstance(Ci.nsILocalHandlerApp);
++ localHandlerApp.executable = fp.file;
++ this.chosenApp = localHandlerApp;
++ }
++ this.finishChooseApp();
++ });
++ } else {
++ var nsIApplicationChooser = Ci.nsIApplicationChooser;
++ var appChooser = Cc["@mozilla.org/applicationchooser;1"].createInstance(
++ nsIApplicationChooser
++ );
++ appChooser.init(
++ this.mDialog,
++ this.dialogElement("strings").getString("chooseAppFilePickerTitle")
++ );
++ var contentTypeDialogObj = this;
++ let appChooserCallback = function appChooserCallback_done(aResult) {
++ if (aResult) {
++ contentTypeDialogObj.chosenApp = aResult.QueryInterface(
++ Ci.nsILocalHandlerApp
++ );
++ }
++ contentTypeDialogObj.finishChooseApp();
++ };
++ appChooser.open(this.mLauncher.MIMEInfo.MIMEType, appChooserCallback);
++ // The finishChooseApp is called from appChooserCallback
++ return;
++ }
+ } else {
+ 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 982faf7d7968..d5417c058050 100644
+--- a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
++++ b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
+@@ -16,6 +16,8 @@
+ #include "nsISupportsPrimitives.h"
+ #include "nsIGSettingsService.h"
+ #include "nsReadableUtils.h"
++#include "nsPrintfCString.h"
++#include "nsKDEUtils.h"
+
+ using namespace mozilla;
+
+@@ -39,6 +41,8 @@ class nsUnixSystemProxySettings final : public nsISystemProxySettings {
+ nsACString& aResult);
+ nsresult SetProxyResultFromGSettings(const char* aKeyBase, const char* aType,
+ nsACString& aResult);
++ nsresult GetProxyFromKDE(const nsACString& aScheme, const nsACString& aHost,
++ PRInt32 aPort, nsACString& aResult);
+ };
+
+ NS_IMPL_ISUPPORTS(nsUnixSystemProxySettings, nsISystemProxySettings)
+@@ -393,6 +397,9 @@ nsresult nsUnixSystemProxySettings::GetProxyForURI(const nsACString& aSpec,
+ const nsACString& aHost,
+ const int32_t aPort,
+ nsACString& aResult) {
++ if (nsKDEUtils::kdeSupport())
++ return GetProxyFromKDE(aScheme, aHost, aPort, aResult);
++
+ if (mProxySettings) {
+ nsresult rv = GetProxyFromGSettings(aScheme, aHost, aPort, aResult);
+ if (NS_SUCCEEDED(rv)) return rv;
+@@ -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) {
++ nsAutoCString url;
++ url = aScheme;
++ url += "://";
++ url += aHost;
++ if (aPort >= 0) {
++ url += ":";
++ url += nsPrintfCString("%d", aPort);
++ }
++ nsTArray<nsCString> command;
++ command.AppendElement("GETPROXY"_ns);
++ command.AppendElement(url);
++ nsTArray<nsCString> result;
++ 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 dc7695e7acc5..3d7656515967 100644
+--- a/toolkit/xre/moz.build
++++ b/toolkit/xre/moz.build
+@@ -96,7 +96,9 @@ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "uikit":
+ "UIKitDirProvider.mm",
+ ]
+ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
++ EXPORTS += ['nsKDEUtils.h']
+ UNIFIED_SOURCES += [
++ "nsKDEUtils.cpp",
+ "nsNativeAppSupportUnix.cpp",
+ ]
+ CXXFLAGS += CONFIG["MOZ_X11_SM_CFLAGS"]
+diff --git a/toolkit/xre/nsKDEUtils.cpp b/toolkit/xre/nsKDEUtils.cpp
+new file mode 100644
+index 000000000000..693280deefff
+--- /dev/null
++++ b/toolkit/xre/nsKDEUtils.cpp
+@@ -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
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++#include "nsKDEUtils.h"
++#include "nsIWidget.h"
++#include "nsISupportsPrimitives.h"
++#include "nsIMutableArray.h"
++#include "nsComponentManagerUtils.h"
++#include "nsArrayUtils.h"
++
++#include <gtk/gtk.h>
++
++#include <limits.h>
++#include <stdio.h>
++#include <sys/wait.h>
++#include <sys/resource.h>
++#include <unistd.h>
++#include <X11/Xlib.h>
++// 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 */
++#endif
++
++// #define DEBUG_KDE
++#ifdef DEBUG_KDE
++# define KWATERFOXHELPER "kwaterfoxhelper"
++#else
++// not need for lib64, it's a binary
++# define KWATERFOXHELPER "/usr/lib/waterfox/kwaterfoxhelper"
++#endif
++
++#define KWATERFOXHELPER_VERSION 6
++#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 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);
++#endif
++ return kde;
++}
++
++nsKDEUtils::nsKDEUtils() : commandFile(NULL), replyFile(NULL) {}
++
++nsKDEUtils::~nsKDEUtils() {
++ // closeHelper(); not actually useful, exiting will close the fd too
++}
++
++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::kdeSupport() {
++ static bool support = kdeSession() && getKdeSupport();
++ return support && helperRunning;
++}
++
++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;
++}
++
++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);
++ }
++ }
++
++ nsTArray<nsCString> out;
++ bool ret = self()->internalCommand(in, NULL, false, &out);
++
++ if (!output) return ret;
++
++ 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;
++
++ rstr->SetData(out[i]);
++ result->AppendElement(rstr);
++ }
++
++ 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::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;
++ }
++ 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);
++#ifdef DEBUG_KDE
++ execvp(KWATERFOXHELPER, args);
++#else
++ 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;
++}
++
++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());
++#endif
++ 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..ed607357c1fe
+--- /dev/null
++++ b/toolkit/xre/nsKDEUtils.h
+@@ -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
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++#ifndef nsKDEUtils_h__
++#define nsKDEUtils_h__
++
++#include "nsString.h"
++#include "nsTArray.h"
++#include <stdio.h>
++
++typedef struct _GtkWindow GtkWindow;
++
++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
+diff --git a/uriloader/exthandler/HandlerServiceParent.cpp b/uriloader/exthandler/HandlerServiceParent.cpp
+index ab77657dd5f3..18b4d8556069 100644
+--- a/uriloader/exthandler/HandlerServiceParent.cpp
++++ b/uriloader/exthandler/HandlerServiceParent.cpp
+@@ -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;
+@@ -310,8 +310,8 @@ mozilla::ipc::IPCResult HandlerServiceParent::RecvExistsForProtocolOS(
+ }
+ #ifdef MOZ_WIDGET_GTK
+ // Check the GNOME registry for a protocol handler
+- *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 0fb126a7f3f7..8cc0006f3045 100644
+--- a/uriloader/exthandler/moz.build
++++ b/uriloader/exthandler/moz.build
+@@ -86,7 +86,9 @@ else:
+
+ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ UNIFIED_SOURCES += [
++ "unix/nsCommonRegistry.cpp",
+ "unix/nsGNOMERegistry.cpp",
++ "unix/nsKDERegistry.cpp",
+ "unix/nsMIMEInfoUnix.cpp",
+ ]
+ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
+@@ -134,6 +136,7 @@ LOCAL_INCLUDES += [
+ "/dom/ipc",
+ "/netwerk/base",
+ "/netwerk/protocol/http",
++ "/toolkit/xre",
+ ]
+
+ 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..3371a756e2c2
+--- /dev/null
++++ b/uriloader/exthandler/unix/nsCommonRegistry.cpp
+@@ -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
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++#include "nsCommonRegistry.h"
++
++#include "nsGNOMERegistry.h"
++#include "nsKDERegistry.h"
++#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 */ 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 */ already_AddRefed<nsMIMEInfoBase>
++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);
++}
+diff --git a/uriloader/exthandler/unix/nsCommonRegistry.h b/uriloader/exthandler/unix/nsCommonRegistry.h
+new file mode 100644
+index 000000000000..075413e2fbb1
+--- /dev/null
++++ b/uriloader/exthandler/unix/nsCommonRegistry.h
+@@ -0,0 +1,28 @@
++/* 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 nsCommonRegistry_h__
++#define nsCommonRegistry_h__
++
++#include "nsIURI.h"
++#include "nsCOMPtr.h"
++
++class nsMIMEInfoBase;
++
++class nsCommonRegistry {
++ public:
++ static bool HandlerExists(const char* aProtocolScheme);
++
++ static nsresult LoadURL(nsIURI* aURL);
++
++ static void GetAppDescForScheme(const nsACString& aScheme, nsAString& aDesc);
++
++ static already_AddRefed<nsMIMEInfoBase> GetFromExtension(
++ const nsACString& aFileExt);
++
++ 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..082035566f0b
+--- /dev/null
++++ b/uriloader/exthandler/unix/nsKDERegistry.cpp
+@@ -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
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++#include "mozilla/StaticPrefs_browser.h"
++#include "nsKDERegistry.h"
++#include "prlink.h"
++#include "prmem.h"
++#include "nsString.h"
++#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 */ 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;
++}
++
++/* 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::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];
++ 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..c6a41b331b2b
+--- /dev/null
++++ b/uriloader/exthandler/unix/nsKDERegistry.h
+@@ -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/. */
++
++#ifndef nsKDERegistry_h__
++#define nsKDERegistry_h__
++
++#include "nsIURI.h"
++#include "nsCOMPtr.h"
++#include "nsTArray.h"
++
++class nsMIMEInfoBase;
++// class nsAutoCString;
++// class nsCString;
++
++class nsKDERegistry {
++ public:
++ static bool HandlerExists(const char* aProtocolScheme);
++
++ static nsresult LoadURL(nsIURI* aURL);
++
++ static void GetAppDescForScheme(const nsACString& aScheme, nsAString& aDesc);
++
++ 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);
++};
++
++#endif // nsKDERegistry_h__
+diff --git a/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp b/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
+index 330c4411597f..c96c1f3ca5a0 100644
+--- a/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
++++ b/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
+@@ -5,16 +5,19 @@
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ #include "nsMIMEInfoUnix.h"
+-#include "nsGNOMERegistry.h"
++#include "nsCommonRegistry.h"
+ #include "nsIGIOService.h"
+ #include "nsNetCID.h"
+ #include "nsIIOService.h"
+ #ifdef MOZ_ENABLE_DBUS
+ # include "nsDBusHandlerApp.h"
+ #endif
++#if defined(XP_UNIX) && !defined(XP_MACOSX)
++# include "nsKDEUtils.h"
++#endif
+
+ nsresult nsMIMEInfoUnix::LoadUriInternal(nsIURI* aURI) {
+- return nsGNOMERegistry::LoadURL(aURI);
++ return nsCommonRegistry::LoadURL(aURI);
+ }
+
+ NS_IMETHODIMP
+@@ -29,15 +32,15 @@ nsMIMEInfoUnix::GetHasDefaultHandler(bool* _retval) {
+ *_retval = false;
+
+ if (mClass == eProtocolInfo) {
+- *_retval = nsGNOMERegistry::HandlerExists(mSchemeOrType.get());
++ *_retval = nsCommonRegistry::HandlerExists(mSchemeOrType.get());
+ } else {
+ RefPtr<nsMIMEInfoBase> mimeInfo =
+- nsGNOMERegistry::GetFromType(mSchemeOrType);
++ nsCommonRegistry::GetFromType(mSchemeOrType);
+ if (!mimeInfo) {
+ nsAutoCString ext;
+ nsresult rv = GetPrimaryExtension(ext);
+ if (NS_SUCCEEDED(rv)) {
+- mimeInfo = nsGNOMERegistry::GetFromExtension(ext);
++ mimeInfo = nsCommonRegistry::GetFromExtension(ext);
+ }
+ }
+ if (mimeInfo) *_retval = true;
+@@ -59,6 +62,21 @@ nsresult nsMIMEInfoUnix::LaunchDefaultWithFile(nsIFile* aFile) {
+ nsAutoCString nativePath;
+ aFile->GetNativePath(nativePath);
+
++ if (nsKDEUtils::kdeSupport()) {
++ bool 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;
++ }
++ if (!GetDefaultApplication()) return NS_ERROR_FILE_NOT_FOUND;
++
++ 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 7f6eaa46f2ee..f7627e790c47 100644
+--- a/uriloader/exthandler/unix/nsOSHelperAppService.cpp
++++ b/uriloader/exthandler/unix/nsOSHelperAppService.cpp
+@@ -10,7 +10,7 @@
+ #include "nsOSHelperAppService.h"
+ #include "nsMIMEInfoUnix.h"
+ #ifdef MOZ_WIDGET_GTK
+-# include "nsGNOMERegistry.h"
++# include "nsCommonRegistry.h"
+ # ifdef MOZ_BUILD_APP_IS_BROWSER
+ # include "nsIToolkitShellService.h"
+ # include "nsIGNOMEShellService.h"
+@@ -1106,7 +1106,7 @@ nsresult nsOSHelperAppService::OSProtocolHandlerExists(
+ if (!XRE_IsContentProcess()) {
+ #ifdef MOZ_WIDGET_GTK
+ // Check the GNOME registry for a protocol handler
+- *aHandlerExists = nsGNOMERegistry::HandlerExists(aProtocolScheme);
++ *aHandlerExists = nsCommonRegistry::HandlerExists(aProtocolScheme);
+ #else
+ *aHandlerExists = false;
+ #endif
+@@ -1126,7 +1126,7 @@ nsresult nsOSHelperAppService::OSProtocolHandlerExists(
+ NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(
+ const nsACString& aScheme, nsAString& _retval) {
+ #ifdef MOZ_WIDGET_GTK
+- nsGNOMERegistry::GetAppDescForScheme(aScheme, _retval);
++ nsCommonRegistry::GetAppDescForScheme(aScheme, _retval);
+ return _retval.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
+ #else
+ return NS_ERROR_NOT_AVAILABLE;
+@@ -1231,7 +1231,7 @@ already_AddRefed<nsMIMEInfoBase> nsOSHelperAppService::GetFromExtension(
+ #ifdef MOZ_WIDGET_GTK
+ LOG("Looking in GNOME registry\n");
+ RefPtr<nsMIMEInfoBase> gnomeInfo =
+- nsGNOMERegistry::GetFromExtension(aFileExt);
++ nsCommonRegistry::GetFromExtension(aFileExt);
+ if (gnomeInfo) {
+ LOG("Got MIMEInfo from GNOME registry\n");
+ return gnomeInfo.forget();
+@@ -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 "
+ "to %s\n",
+diff --git a/widget/gtk/moz.build b/widget/gtk/moz.build
+index 90574c130f59..ef530396aa0b 100644
+--- a/widget/gtk/moz.build
++++ b/widget/gtk/moz.build
+@@ -159,6 +159,7 @@ LOCAL_INCLUDES += [
+ "/layout/xul",
+ "/other-licenses/atk-1.0",
+ "/third_party/cups/include",
++ "/toolkit/xre",
+ "/widget",
+ "/widget/headless",
+ ]
+diff --git a/widget/gtk/nsFilePicker.cpp b/widget/gtk/nsFilePicker.cpp
+index 22d0f46b9563..b2a68711eb34 100644
+--- a/widget/gtk/nsFilePicker.cpp
++++ b/widget/gtk/nsFilePicker.cpp
+@@ -5,6 +5,7 @@
+
+ #include <dlfcn.h>
+ #include <gtk/gtk.h>
++#include <gdk/gdkx.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <unistd.h>
+@@ -28,6 +29,8 @@
+ #include "WidgetUtilsGtk.h"
+
+ #include "nsFilePicker.h"
++#include "nsKDEUtils.h"
++#include "nsURLHelper.h"
+
+ #undef LOG
+ #ifdef MOZ_LOGGING
+@@ -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;
+ }
+
+ nsAutoCString filter, name;
+@@ -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()) {
++ mCallback = aCallback;
++ mRunning = true;
++ NS_ADDREF_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;
++ }
++
+ NS_ConvertUTF16toUTF8 title(mTitle);
+
+ GtkWindow* parent_widget =
+@@ -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;
++}
++
++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(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) {
++ mDisplayDirectory->GetNativePath(directory);
++ } else if (mPrevDisplayDirectory) {
++ mPrevDisplayDirectory->GetNativePath(directory);
++ }
++
++ nsAutoCString startdir;
++ if (!directory.IsEmpty()) {
++ startdir = directory;
++ }
++ if (mMode == nsIFilePicker::modeSave) {
++ if (!startdir.IsEmpty()) {
++ startdir += "/";
++ startdir += ToNewUTF8String(mDefault);
++ } 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);
++ }
++ }
++
++ 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(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);
++
++ 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> 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 496df4937277..a4c1862ec042 100644
+--- a/widget/gtk/nsFilePicker.h
++++ b/widget/gtk/nsFilePicker.h
+@@ -74,6 +74,12 @@ class nsFilePicker : public nsBaseFilePicker {
+ private:
+ static nsIFile* mPrevDisplayDirectory;
+
++ bool kdeRunning();
++ bool getKdeRunning();
++ 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 88ee06d78db6..834d6a2d353c 100644
+--- a/xpcom/components/ManifestParser.cpp
++++ b/xpcom/components/ManifestParser.cpp
+@@ -43,6 +43,7 @@
+ #include "nsIScriptError.h"
+ #include "nsIXULAppInfo.h"
+ #include "nsIXULRuntime.h"
++#include "nsKDEUtils.h"
+
+ using namespace mozilla;
+
+@@ -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;
++ constexpr auto kDesktop = u"desktop"_ns;
+ constexpr auto kProcess = u"process"_ns;
+ #if defined(MOZ_WIDGET_ANDROID)
+ constexpr auto kTablet = u"tablet"_ns;
+@@ -453,6 +455,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
+ }
+
+ nsAutoString osVersion;
++ nsAutoString desktop;
+ #if defined(XP_WIN)
+ # pragma warning(push)
+ # pragma warning(disable : 4996) // VC12+ deprecates GetVersionEx
+@@ -461,14 +464,17 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
+ nsTextFormatter::ssprintf(osVersion, u"%ld.%ld", info.dwMajorVersion,
+ info.dwMinorVersion);
+ }
++ desktop = u"win"_ns;
+ # pragma warning(pop)
+ #elif defined(MOZ_WIDGET_COCOA)
+ SInt32 majorVersion = nsCocoaFeatures::macOSVersionMajor();
+ SInt32 minorVersion = nsCocoaFeatures::macOSVersionMinor();
+ nsTextFormatter::ssprintf(osVersion, u"%ld.%ld", majorVersion, minorVersion);
++ desktop = u"macosx"_ns);
+ #elif defined(MOZ_WIDGET_GTK)
+ nsTextFormatter::ssprintf(osVersion, u"%ld.%ld", gtk_major_version,
+ gtk_minor_version);
++ desktop = nsKDEUtils::kdeSession() ? u"kde"_ns : u"gnome"_ns;
+ #elif defined(MOZ_WIDGET_ANDROID)
+ bool isTablet = false;
+ 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()) {
+@@ -576,6 +583,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
+ : eUnspecified;
+ #endif
+ int flags = 0;
++ TriState stDesktop = eUnspecified;
+
+ while ((token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) &&
+ ok) {
+@@ -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) ||
++ CheckStringFlag(kDesktop, wtoken, desktop, stDesktop) ||
+ CheckStringFlag(kProcess, wtoken, process, stProcess) ||
+ CheckVersionFlag(kOsVersion, wtoken, osVersion, stOsVersion) ||
+ CheckVersionFlag(kAppVersion, wtoken, appVersion, stAppVersion) ||
+@@ -644,6 +653,7 @@ void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
+
+ if (!ok || stApp == eBad || stAppVersion == 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 95ee64e985ac..9af8f80497b7 100644
+--- a/xpcom/components/moz.build
++++ b/xpcom/components/moz.build
+@@ -71,6 +71,7 @@ LOCAL_INCLUDES += [
+ "/js/xpconnect/loader",
+ "/layout/build",
+ "/modules/libjar",
++ "/toolkit/xre",
+ ]
+
+ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+diff --git a/xpcom/io/nsLocalFileUnix.cpp b/xpcom/io/nsLocalFileUnix.cpp
+index 08c77360de6f..eedd5bcf86bd 100644
+--- a/xpcom/io/nsLocalFileUnix.cpp
++++ b/xpcom/io/nsLocalFileUnix.cpp
+@@ -51,6 +51,7 @@
+
+ #ifdef MOZ_WIDGET_GTK
+ # include "nsIGIOService.h"
++# include "nsKDEUtils.h"
+ #endif
+
+ #ifdef MOZ_WIDGET_COCOA
+@@ -2172,10 +2173,18 @@ nsLocalFile::Reveal() {
+ }
+
+ #ifdef MOZ_WIDGET_GTK
++ nsAutoCString url;
+ nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
+- if (!giovfs) {
+- return NS_ERROR_FAILURE;
++ url = mPath;
++ if (nsKDEUtils::kdeSupport()) {
++ nsTArray<nsCString> command;
++ command.AppendElement("REVEAL"_ns);
++ command.AppendElement(mPath);
++ return nsKDEUtils::command(command) ? NS_OK : NS_ERROR_FAILURE;
+ }
++
++ if (!giovfs) return NS_ERROR_FAILURE;
++
+ return giovfs->RevealFile(this);
+ #elif defined(MOZ_WIDGET_COCOA)
+ CFURLRef url;
+@@ -2197,6 +2206,13 @@ nsLocalFile::Launch() {
+ }
+
+ #ifdef MOZ_WIDGET_GTK
++ if (nsKDEUtils::kdeSupport()) {
++ nsTArray<nsCString> command;
++ 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;
diff --git a/waterfox-g/debian/patches/libavcodec58_91.patch b/waterfox-g/debian/patches/libavcodec58_91.patch
new file mode 100644
index 0000000..48aa2af
--- /dev/null
+++ b/waterfox-g/debian/patches/libavcodec58_91.patch
@@ -0,0 +1,13 @@
+diff --git a/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp b/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
+index 7716162e0707..a9bed062905d 100644
+--- a/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
++++ b/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
+@@ -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",
diff --git a/waterfox-g/debian/patches/mach-depends.patch b/waterfox-g/debian/patches/mach-depends.patch
new file mode 100644
index 0000000..a499d45
--- /dev/null
+++ b/waterfox-g/debian/patches/mach-depends.patch
@@ -0,0 +1,21 @@
+diff --git a/python/sites/mach.txt b/python/sites/mach.txt
+index 83dec4a14237..effb59fc457f 100644
+--- a/python/sites/mach.txt
++++ b/python/sites/mach.txt
+@@ -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/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.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
diff --git a/waterfox-g/debian/patches/mozilla-ntlm-full-path.patch b/waterfox-g/debian/patches/mozilla-ntlm-full-path.patch
new file mode 100644
index 0000000..9c7a8db
--- /dev/null
+++ b/waterfox-g/debian/patches/mozilla-ntlm-full-path.patch
@@ -0,0 +1,15 @@
+# User Petr Cerny <pcerny@novell.com>
+
+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
+@@ -160,7 +160,7 @@ nsresult nsAuthSambaNTLM::SpawnNTLMAuthHelper() {
+ const char* username = PR_GetEnv("USER");
+ if (!username) return NS_ERROR_FAILURE;
+
+- const char* const args[] = {"ntlm_auth",
++ const char* const args[] = {"/usr/bin/ntlm_auth",
+ "--helper-protocol",
+ "ntlmssp-client-1",
+ "--use-cached-creds",
diff --git a/waterfox-g/debian/patches/nongnome-proxies.patch b/waterfox-g/debian/patches/nongnome-proxies.patch
new file mode 100644
index 0000000..ff5e4b9
--- /dev/null
+++ b/waterfox-g/debian/patches/nongnome-proxies.patch
@@ -0,0 +1,26 @@
+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
+@@ -52,10 +52,14 @@ nsUnixSystemProxySettings::GetMainThreadOnly(bool* aMainThreadOnly) {
+ }
+
+ void nsUnixSystemProxySettings::Init() {
+- mGSettings = do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
+- if (mGSettings) {
+- mGSettings->GetCollectionForSchema("org.gnome.system.proxy"_ns,
+- getter_AddRefs(mProxySettings));
++ const char* sessionType = PR_GetEnv("DESKTOP_SESSION");
++ if (sessionType && !strcmp(sessionType, "gnome")) {
++ mGSettings = do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
++ if (mGSettings) {
++ mGSettings->GetCollectionForSchema(
++ "org.gnome.system.proxy"_ns,
++ getter_AddRefs(mProxySettings));
++ }
+ }
+ }
+
diff --git a/waterfox-g/debian/patches/series b/waterfox-g/debian/patches/series
new file mode 100644
index 0000000..0b9bea1
--- /dev/null
+++ b/waterfox-g/debian/patches/series
@@ -0,0 +1,10 @@
+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
+fix-wayland-build.patch -p1
+debian-hacks/Relax-nodejs-minimum-version.patch -p1
+debian-hacks/Relax-cargo-version-requirement.patch -p1
bgstack15