1
0
MozillaFirefox/mozilla-kde.patch

1769 lines
58 KiB
Diff
Raw Normal View History

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Wolfgang Rosenauer <wolfgang@rosenauer.org>
Date: Tue, 8 Aug 2023 16:13:48 +0300
Subject: [PATCH] Add KDE integration to Firefox (toolkit parts)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=140751
Bug: https://bugzilla.suse.com/show_bug.cgi?id=170055
EDIT: Björn Bidar: Removed handling for obsolete special files
Co-authored-by: Wolfgang Rosenauer <wolfgang@rosenauer.org>
Co-authored-by: Lubos Lunak <lunak@suse.com>
Co-authored-by: Björn Bidar <bjorn.bidar@thaodan.de>
---
modules/libpref/Preferences.cpp | 1 +
modules/libpref/moz.build | 4 +
python/mozbuild/mozpack/chrome/flags.py | 1 +
python/mozbuild/mozpack/chrome/manifest.py | 1 +
toolkit/components/downloads/moz.build | 4 +
.../mozapps/downloads/HelperAppDlg.sys.mjs | 70 +++--
.../unixproxy/nsUnixSystemProxySettings.cpp | 29 ++
toolkit/xre/moz.build | 2 +
toolkit/xre/nsKDEUtils.cpp | 286 ++++++++++++++++++
toolkit/xre/nsKDEUtils.h | 53 ++++
uriloader/exthandler/HandlerServiceParent.cpp | 6 +-
uriloader/exthandler/moz.build | 3 +
.../exthandler/unix/nsCommonRegistry.cpp | 42 +++
uriloader/exthandler/unix/nsCommonRegistry.h | 28 ++
uriloader/exthandler/unix/nsKDERegistry.cpp | 75 +++++
uriloader/exthandler/unix/nsKDERegistry.h | 35 +++
uriloader/exthandler/unix/nsMIMEInfoUnix.cpp | 28 +-
.../exthandler/unix/nsOSHelperAppService.cpp | 10 +-
widget/gtk/moz.build | 1 +
widget/gtk/nsFilePicker.cpp | 230 +++++++++++++-
widget/gtk/nsFilePicker.h | 6 +
xpcom/components/ManifestParser.cpp | 10 +
xpcom/components/moz.build | 1 +
xpcom/io/nsLocalFileUnix.cpp | 20 +-
24 files changed, 910 insertions(+), 36 deletions(-)
create mode 100644 toolkit/xre/nsKDEUtils.cpp
create mode 100644 toolkit/xre/nsKDEUtils.h
create mode 100644 uriloader/exthandler/unix/nsCommonRegistry.cpp
create mode 100644 uriloader/exthandler/unix/nsCommonRegistry.h
create mode 100644 uriloader/exthandler/unix/nsKDERegistry.cpp
create mode 100644 uriloader/exthandler/unix/nsKDERegistry.h
diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -91,16 +91,17 @@
#include "PLDHashTable.h"
#include "prdtoa.h"
#include "prlink.h"
#include "xpcpublic.h"
#include "js/RootingAPI.h"
#ifdef MOZ_BACKGROUNDTASKS
# include "mozilla/BackgroundTasks.h"
#endif
+#include "nsKDEUtils.h"
#ifdef DEBUG
# include <map>
#endif
#ifdef MOZ_MEMORY
# include "mozmemory.h"
#endif
diff --git a/modules/libpref/moz.build b/modules/libpref/moz.build
--- a/modules/libpref/moz.build
+++ b/modules/libpref/moz.build
@@ -121,16 +121,20 @@ EXPORTS.mozilla += [
]
EXPORTS.mozilla += sorted(["!" + g for g in gen_h])
UNIFIED_SOURCES += [
"Preferences.cpp",
"SharedPrefMap.cpp",
]
+LOCAL_INCLUDES += [
+ '/toolkit/xre'
+]
+
gen_all_tuple = tuple(gen_h + gen_cpp + gen_rs)
GeneratedFile(
*gen_all_tuple,
script="init/generate_static_pref_list.py",
entry_point="emit_code",
inputs=["init/StaticPrefList.yaml"]
)
diff --git a/python/mozbuild/mozpack/chrome/flags.py b/python/mozbuild/mozpack/chrome/flags.py
--- a/python/mozbuild/mozpack/chrome/flags.py
+++ b/python/mozbuild/mozpack/chrome/flags.py
@@ -229,16 +229,17 @@ class Flags(OrderedDict):
"os": StringFlag,
"osversion": VersionFlag,
"abi": StringFlag,
"platform": Flag,
"xpcnativewrappers": Flag,
"tablet": Flag,
"process": StringFlag,
"backgroundtask": StringFlag,
+ "desktop": StringFlag,
}
RE = re.compile(r"([!<>=]+)")
def __init__(self, *flags):
"""
Initialize a set of flags given in string form.
flags = Flags('contentaccessible=yes', 'appversion>=3.5')
"""
diff --git a/python/mozbuild/mozpack/chrome/manifest.py b/python/mozbuild/mozpack/chrome/manifest.py
--- a/python/mozbuild/mozpack/chrome/manifest.py
+++ b/python/mozbuild/mozpack/chrome/manifest.py
@@ -38,16 +38,17 @@ class ManifestEntry(object):
"os",
"osversion",
"abi",
"xpcnativewrappers",
"tablet",
"process",
"contentaccessible",
"backgroundtask",
+ "desktop",
]
def __init__(self, base, *flags):
"""
Initialize a manifest entry with the given base path and flags.
"""
self.base = base
self.flags = Flags(*flags)
diff --git a/toolkit/components/downloads/moz.build b/toolkit/components/downloads/moz.build
--- a/toolkit/components/downloads/moz.build
+++ b/toolkit/components/downloads/moz.build
@@ -46,10 +46,14 @@ XPCOM_MANIFESTS += [
if CONFIG["MOZ_PLACES"]:
EXTRA_JS_MODULES += [
"DownloadHistory.sys.mjs",
]
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
--- a/toolkit/mozapps/downloads/HelperAppDlg.sys.mjs
+++ b/toolkit/mozapps/downloads/HelperAppDlg.sys.mjs
@@ -1241,36 +1241,66 @@ nsUnknownContentTypeDialog.prototype = {
params.handlerApp &&
params.handlerApp.executable &&
params.handlerApp.executable.isFile()
) {
// Remember the file they chose to run.
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);
fp.init(
this.mDialog,
this.dialogElement("strings").getString("chooseAppFilePickerTitle"),
nsIFilePicker.modeOpen
);
diff --git a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
--- a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
+++ b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
@@ -11,16 +11,18 @@
#include "prenv.h"
#include "nsInterfaceHashtable.h"
#include "nsHashtablesFwd.h"
#include "nsHashKeys.h"
#include "nsNetUtil.h"
#include "nsISupportsPrimitives.h"
#include "nsIGSettingsService.h"
#include "nsReadableUtils.h"
+#include "nsPrintfCString.h"
+#include "nsKDEUtils.h"
using namespace mozilla;
class nsUnixSystemProxySettings final : public nsISystemProxySettings {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISYSTEMPROXYSETTINGS
@@ -34,16 +36,18 @@ class nsUnixSystemProxySettings final :
nsCOMPtr<nsIGSettingsCollection> mProxySettings;
nsInterfaceHashtable<nsCStringHashKey, nsIGSettingsCollection>
mSchemeProxySettings;
nsresult GetProxyFromGSettings(const nsACString& aScheme,
const nsACString& aHost, int32_t aPort,
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)
NS_IMETHODIMP
nsUnixSystemProxySettings::GetMainThreadOnly(bool* aMainThreadOnly) {
// dbus prevents us from being threadsafe, but this routine should not block
// anyhow
@@ -391,21 +395,46 @@ nsresult nsUnixSystemProxySettings::GetP
return NS_OK;
}
nsresult nsUnixSystemProxySettings::GetProxyForURI(const nsACString& aSpec,
const nsACString& aScheme,
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;
}
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();
return result.forget().downcast<nsISupports>();
}
diff --git a/toolkit/xre/moz.build b/toolkit/xre/moz.build
--- a/toolkit/xre/moz.build
+++ b/toolkit/xre/moz.build
@@ -91,17 +91,19 @@ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "co
"../components/printingui",
]
elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "uikit":
UNIFIED_SOURCES += [
"nsNativeAppSupportDefault.cpp",
"UIKitDirProvider.mm",
]
elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ EXPORTS += ['nsKDEUtils.h']
UNIFIED_SOURCES += [
+ "nsKDEUtils.cpp",
"nsNativeAppSupportUnix.cpp",
]
CXXFLAGS += CONFIG["MOZ_X11_SM_CFLAGS"]
else:
UNIFIED_SOURCES += [
"nsNativeAppSupportDefault.cpp",
]
diff --git a/toolkit/xre/nsKDEUtils.cpp b/toolkit/xre/nsKDEUtils.cpp
new file mode 100644
--- /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 KMOZILLAHELPER "kmozillahelper"
+#else
+// not need for lib64, it's a binary
+# define KMOZILLAHELPER "/usr/lib/mozilla/kmozillahelper"
+#endif
+
+#define KMOZILLAHELPER_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("KMOZILLAHELPER_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*>(KMOZILLAHELPER), 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(KMOZILLAHELPER, args);
+#else
+ execv(KMOZILLAHELPER, 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
--- /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 Firefox). 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 Firefox. 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
--- a/uriloader/exthandler/HandlerServiceParent.cpp
+++ b/uriloader/exthandler/HandlerServiceParent.cpp
@@ -13,17 +13,17 @@
#include "ContentHandlerService.h"
#include "nsIExternalProtocolService.h"
#include "nsStringEnumerator.h"
#include "nsIMutableArray.h"
#include "nsCExternalHandlerService.h"
#include "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
#ifdef MOZ_WIDGET_GTK
-# include "unix/nsGNOMERegistry.h"
+# include "unix/nsCommonRegistry.h"
#endif
using mozilla::dom::ContentHandlerService;
using mozilla::dom::HandlerApp;
using mozilla::dom::HandlerInfo;
using mozilla::dom::RemoteHandlerApp;
namespace {
@@ -305,18 +305,18 @@ mozilla::ipc::IPCResult HandlerServicePa
mozilla::ipc::IPCResult HandlerServiceParent::RecvExistsForProtocolOS(
const nsACString& aProtocolScheme, bool* aHandlerExists) {
if (aProtocolScheme.Length() > MAX_SCHEME_LENGTH) {
*aHandlerExists = false;
return IPC_OK();
}
#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
return IPC_OK();
}
/*
* Check if a handler exists for the provided protocol. Check the datastore
diff --git a/uriloader/exthandler/moz.build b/uriloader/exthandler/moz.build
--- a/uriloader/exthandler/moz.build
+++ b/uriloader/exthandler/moz.build
@@ -81,17 +81,19 @@ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "ui
else:
# These files can't be built in unified mode because they redefine LOG.
SOURCES += [
osdir + "/nsOSHelperAppService.cpp",
]
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":
UNIFIED_SOURCES += [
"android/nsMIMEInfoAndroid.cpp",
]
elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
UNIFIED_SOURCES += [
@@ -129,15 +131,16 @@ include("/ipc/chromium/chromium-config.m
FINAL_LIBRARY = "xul"
LOCAL_INCLUDES += [
"/docshell/base",
"/dom/base",
"/dom/ipc",
"/netwerk/base",
"/netwerk/protocol/http",
+ "/toolkit/xre",
]
if CONFIG["MOZ_ENABLE_DBUS"]:
CXXFLAGS += CONFIG["MOZ_DBUS_CFLAGS"]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
diff --git a/uriloader/exthandler/unix/nsCommonRegistry.cpp b/uriloader/exthandler/unix/nsCommonRegistry.cpp
new file mode 100644
--- /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
--- /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
--- /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
--- /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
--- a/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
+++ b/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
@@ -1,48 +1,51 @@
/* -*- Mode: C++; tab-width: 3; 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 "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
nsMIMEInfoUnix::GetHasDefaultHandler(bool* _retval) {
// if a default app is set, it means the application has been set from
// either /etc/mailcap or ${HOME}/.mailcap, in which case we don't want to
// give the GNOME answer.
if (GetDefaultApplication()) {
return nsMIMEInfoImpl::GetHasDefaultHandler(_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;
}
if (*_retval) return NS_OK;
return NS_OK;
@@ -54,16 +57,31 @@ nsresult nsMIMEInfoUnix::LaunchDefaultWi
// give the GNOME answer.
if (GetDefaultApplication()) {
return nsMIMEInfoImpl::LaunchDefaultWithFile(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;
}
// nsGIOMimeApp->Launch wants a URI string instead of local file
nsresult rv;
nsCOMPtr<nsIIOService> ioservice =
diff --git a/uriloader/exthandler/unix/nsOSHelperAppService.cpp b/uriloader/exthandler/unix/nsOSHelperAppService.cpp
--- a/uriloader/exthandler/unix/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/unix/nsOSHelperAppService.cpp
@@ -5,17 +5,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <sys/types.h>
#include <sys/stat.h>
#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"
# endif
#endif
#include "nsISupports.h"
#include "nsString.h"
#include "nsReadableUtils.h"
@@ -1101,17 +1101,17 @@ nsresult nsOSHelperAppService::GetHandle
nsresult nsOSHelperAppService::OSProtocolHandlerExists(
const char* aProtocolScheme, bool* aHandlerExists) {
nsresult rv = NS_OK;
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
} else {
*aHandlerExists = false;
nsCOMPtr<nsIHandlerService> handlerSvc =
do_GetService(NS_HANDLERSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv) && handlerSvc) {
@@ -1121,17 +1121,17 @@ nsresult nsOSHelperAppService::OSProtoco
}
return rv;
}
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;
#endif
}
NS_IMETHODIMP nsOSHelperAppService::IsCurrentAppOSDefaultForProtocol(
const nsACString& aScheme, bool* _retval) {
@@ -1226,17 +1226,17 @@ already_AddRefed<nsMIMEInfoBase> nsOSHel
nsresult rv =
LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt), majorType,
minorType, mime_types_description, true);
if (NS_FAILED(rv) || majorType.IsEmpty()) {
#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();
}
#endif
rv = LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt), majorType,
minorType, mime_types_description, false);
@@ -1339,17 +1339,17 @@ already_AddRefed<nsMIMEInfoBase> nsOSHel
// Now look up our extensions
nsAutoString extensions, mime_types_description;
LookUpExtensionsAndDescription(majorType, minorType, extensions,
mime_types_description);
#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",
NS_LossyConvertUTF16toASCII(extensions).get());
NS_ASSERTION(!gnomeInfo->HasExtensions(), "How'd that happen?");
gnomeInfo->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions));
return gnomeInfo.forget();
diff --git a/widget/gtk/moz.build b/widget/gtk/moz.build
--- a/widget/gtk/moz.build
+++ b/widget/gtk/moz.build
@@ -146,16 +146,17 @@ FINAL_LIBRARY = "xul"
LOCAL_INCLUDES += [
"/layout/base",
"/layout/forms",
"/layout/generic",
"/layout/xul",
"/other-licenses/atk-1.0",
"/third_party/cups/include",
+ "/toolkit/xre",
"/widget",
"/widget/headless",
"/widget/x11",
]
DEFINES["CAIRO_GFX"] = True
DEFINES["MOZ_APP_NAME"] = '"%s"' % CONFIG["MOZ_APP_NAME"]
diff --git a/widget/gtk/nsFilePicker.cpp b/widget/gtk/nsFilePicker.cpp
--- a/widget/gtk/nsFilePicker.cpp
+++ b/widget/gtk/nsFilePicker.cpp
@@ -1,15 +1,16 @@
/* -*- 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 <dlfcn.h>
#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "mozilla/Types.h"
#include "AsyncDBus.h"
#include "nsGtkUtils.h"
#include "nsIFileURL.h"
@@ -25,16 +26,18 @@
#include "nsArrayEnumerator.h"
#include "nsEnumeratorUtils.h"
#include "nsNetUtil.h"
#include "nsReadableUtils.h"
#include "MozContainer.h"
#include "WidgetUtilsGtk.h"
#include "nsFilePicker.h"
+#include "nsKDEUtils.h"
+#include "nsURLHelper.h"
#undef LOG
#ifdef MOZ_LOGGING
# include "mozilla/Logging.h"
# include "nsTArray.h"
# include "Units.h"
extern mozilla::LazyLogModule gWidgetLog;
# define LOG(args) MOZ_LOG(gWidgetLog, mozilla::LogLevel::Debug, args)
@@ -305,17 +308,18 @@ nsFilePicker::AppendFilters(int32_t aFil
mAllowURLs = !!(aFilterMask & filterAllowURLs);
return nsBaseFilePicker::AppendFilters(aFilterMask);
}
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;
CopyUTF16toUTF8(aFilter, filter);
CopyUTF16toUTF8(aTitle, name);
mFilters.AppendElement(filter);
mFilterNames.AppendElement(name);
@@ -415,16 +419,41 @@ nsresult nsFilePicker::Show(nsIFilePicke
return NS_OK;
}
NS_IMETHODIMP
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 =
GTK_WINDOW(mParentWidget->GetNativeData(NS_NATIVE_SHELLWIDGET));
GtkFileChooserAction action = GetGtkFileChooserAction(mMode);
const gchar* accept_button;
@@ -696,16 +725,215 @@ void nsFilePicker::Done(void* file_choos
mCallback->Done(result);
mCallback = nullptr;
} else {
mResult = result;
}
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,
const gchar* accept_label) {
static auto sGtkFileChooserNativeNewPtr =
(void* (*)(const gchar*, GtkWindow*, GtkFileChooserAction, const gchar*,
const gchar*))dlsym(RTLD_DEFAULT,
"gtk_file_chooser_native_new");
diff --git a/widget/gtk/nsFilePicker.h b/widget/gtk/nsFilePicker.h
--- a/widget/gtk/nsFilePicker.h
+++ b/widget/gtk/nsFilePicker.h
@@ -71,16 +71,22 @@ class nsFilePicker : public nsBaseFilePi
nsString mDefaultExtension;
nsTArray<nsCString> mFilters;
nsTArray<nsCString> mFilterNames;
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);
void GtkFileChooserShow(void* file_chooser);
void GtkFileChooserDestroy(void* file_chooser);
void GtkFileChooserSetModal(void* file_chooser, GtkWindow* parent_widget,
gboolean modal);
diff --git a/xpcom/components/ManifestParser.cpp b/xpcom/components/ManifestParser.cpp
--- a/xpcom/components/ManifestParser.cpp
+++ b/xpcom/components/ManifestParser.cpp
@@ -38,16 +38,17 @@
#include "nsTextFormatter.h"
#include "nsVersionComparator.h"
#include "nsXPCOMCIDInternal.h"
#include "nsIConsoleService.h"
#include "nsIScriptError.h"
#include "nsIXULAppInfo.h"
#include "nsIXULRuntime.h"
+#include "nsKDEUtils.h"
using namespace mozilla;
struct ManifestDirective {
const char* directive;
int argc;
bool ischrome;
@@ -389,16 +390,17 @@ void ParseManifest(NSLocationType aType,
constexpr auto kRemoteEnabled = u"remoteenabled"_ns;
constexpr auto kRemoteRequired = u"remoterequired"_ns;
constexpr auto kApplication = u"application"_ns;
constexpr auto kAppVersion = u"appversion"_ns;
constexpr auto kGeckoVersion = u"platformversion"_ns;
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;
#endif
// You might expect this to be guarded by MOZ_BACKGROUNDTASKS, but it's not
// possible to have conditional manifest contents, so we need to recognize and
// discard these tokens even when MOZ_BACKGROUNDTASKS is not set.
constexpr auto kBackgroundTask = u"backgroundtask"_ns;
@@ -448,39 +450,44 @@ void ParseManifest(NSLocationType aType,
CopyUTF8toUTF16(s, abi);
abi.Insert(char16_t('_'), 0);
abi.Insert(osTarget, 0);
}
}
}
nsAutoString osVersion;
+ nsAutoString desktop;
#if defined(XP_WIN)
# pragma warning(push)
# pragma warning(disable : 4996) // VC12+ deprecates GetVersionEx
OSVERSIONINFO info = {sizeof(OSVERSIONINFO)};
if (GetVersionEx(&info)) {
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()) {
jni::String::LocalRef release = java::sdk::Build::VERSION::RELEASE();
osVersion.Assign(release->ToString());
isTablet = java::GeckoAppShell::IsTablet();
}
+ desktop = u"android"_ns;
#endif
if (XRE_IsContentProcess()) {
process = kContent;
} else {
process = kMain;
}
@@ -571,25 +578,27 @@ void ParseManifest(NSLocationType aType,
// When in background task mode, default to not registering
// category directivies unless backgroundtask=1 is specified.
TriState stBackgroundTask = (BackgroundTasks::IsBackgroundTaskMode() &&
strcmp("category", directive->directive) == 0)
? eBad
: eUnspecified;
#endif
int flags = 0;
+ TriState stDesktop = eUnspecified;
while ((token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) &&
ok) {
ToLowerCase(token);
NS_ConvertASCIItoUTF16 wtoken(token);
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) ||
CheckVersionFlag(kGeckoVersion, wtoken, geckoVersion,
stGeckoVersion)) {
continue;
}
@@ -639,16 +648,17 @@ void ParseManifest(NSLocationType aType,
LogMessageWithContext(
aFile, line, "Unrecognized chrome manifest modifier '%s'.", token);
ok = false;
}
if (!ok || stApp == eBad || stAppVersion == eBad ||
stGeckoVersion == eBad || stOs == eBad || stOsVersion == eBad ||
+ stDesktop == eBad ||
#ifdef MOZ_WIDGET_ANDROID
stTablet == eBad ||
#endif
#ifdef MOZ_BACKGROUNDTASKS
stBackgroundTask == eBad ||
#endif
stABI == eBad || stProcess == eBad) {
continue;
diff --git a/xpcom/components/moz.build b/xpcom/components/moz.build
--- a/xpcom/components/moz.build
+++ b/xpcom/components/moz.build
@@ -66,16 +66,17 @@ LOCAL_INCLUDES += [
"!..",
"../base",
"../build",
"../ds",
"/chrome",
"/js/xpconnect/loader",
"/layout/build",
"/modules/libjar",
+ "/toolkit/xre",
]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
include("/ipc/chromium/chromium-config.mozbuild")
PYTHON_UNITTEST_MANIFESTS += [
diff --git a/xpcom/io/nsLocalFileUnix.cpp b/xpcom/io/nsLocalFileUnix.cpp
--- a/xpcom/io/nsLocalFileUnix.cpp
+++ b/xpcom/io/nsLocalFileUnix.cpp
@@ -46,16 +46,17 @@
#include "nsString.h"
#include "nsIDirectoryEnumerator.h"
#include "nsSimpleEnumerator.h"
#include "private/pprio.h"
#include "prlink.h"
#ifdef MOZ_WIDGET_GTK
# include "nsIGIOService.h"
+# include "nsKDEUtils.h"
#endif
#ifdef MOZ_WIDGET_COCOA
# include <Carbon/Carbon.h>
# include "CocoaFileUtils.h"
# include "prmem.h"
# include "plbase64.h"
@@ -2205,20 +2206,28 @@ nsLocalFile::SetPersistentDescriptor(con
NS_IMETHODIMP
nsLocalFile::Reveal() {
if (!FilePreferences::IsAllowedPath(mPath)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
#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;
if (NS_SUCCEEDED(GetCFURL(&url))) {
nsresult rv = CocoaFileUtils::RevealFileInFinder(url);
::CFRelease(url);
return rv;
}
@@ -2230,16 +2239,23 @@ nsLocalFile::Reveal() {
NS_IMETHODIMP
nsLocalFile::Launch() {
if (!FilePreferences::IsAllowedPath(mPath)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
#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;
}
return giovfs->LaunchFile(mPath);
#elif defined(MOZ_WIDGET_ANDROID)
// Not supported on GeckoView