summaryrefslogtreecommitdiffstats
path: root/toolkit/system
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /toolkit/system
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'toolkit/system')
-rw-r--r--toolkit/system/androidproxy/moz.build11
-rw-r--r--toolkit/system/androidproxy/nsAndroidSystemProxySettings.cpp89
-rw-r--r--toolkit/system/gnome/moz.build37
-rw-r--r--toolkit/system/gnome/nsAlertsIconListener.cpp327
-rw-r--r--toolkit/system/gnome/nsAlertsIconListener.h91
-rw-r--r--toolkit/system/gnome/nsGConfService.cpp305
-rw-r--r--toolkit/system/gnome/nsGConfService.h31
-rw-r--r--toolkit/system/gnome/nsGIOService.cpp475
-rw-r--r--toolkit/system/gnome/nsGIOService.h24
-rw-r--r--toolkit/system/gnome/nsGSettingsService.cpp350
-rw-r--r--toolkit/system/gnome/nsGSettingsService.h27
-rw-r--r--toolkit/system/gnome/nsGnomeModule.cpp78
-rw-r--r--toolkit/system/gnome/nsPackageKitService.cpp267
-rw-r--r--toolkit/system/gnome/nsPackageKitService.h26
-rw-r--r--toolkit/system/gnome/nsSystemAlertsService.cpp118
-rw-r--r--toolkit/system/gnome/nsSystemAlertsService.h39
-rw-r--r--toolkit/system/osxproxy/ProxyUtils.h21
-rw-r--r--toolkit/system/osxproxy/ProxyUtils.mm182
-rw-r--r--toolkit/system/osxproxy/moz.build14
-rw-r--r--toolkit/system/osxproxy/nsOSXSystemProxySettings.mm326
-rw-r--r--toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp42
-rw-r--r--toolkit/system/osxproxy/tests/gtest/moz.build18
-rw-r--r--toolkit/system/unixproxy/moz.build17
-rw-r--r--toolkit/system/unixproxy/nsLibProxySettings.cpp141
-rw-r--r--toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp543
-rw-r--r--toolkit/system/windowsproxy/ProxyUtils.cpp182
-rw-r--r--toolkit/system/windowsproxy/ProxyUtils.h21
-rw-r--r--toolkit/system/windowsproxy/moz.build14
-rw-r--r--toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp304
-rw-r--r--toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp42
-rw-r--r--toolkit/system/windowsproxy/tests/gtest/moz.build18
31 files changed, 4180 insertions, 0 deletions
diff --git a/toolkit/system/androidproxy/moz.build b/toolkit/system/androidproxy/moz.build
new file mode 100644
index 000000000..5c6b0c105
--- /dev/null
+++ b/toolkit/system/androidproxy/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'nsAndroidSystemProxySettings.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/toolkit/system/androidproxy/nsAndroidSystemProxySettings.cpp b/toolkit/system/androidproxy/nsAndroidSystemProxySettings.cpp
new file mode 100644
index 000000000..71e12bb77
--- /dev/null
+++ b/toolkit/system/androidproxy/nsAndroidSystemProxySettings.cpp
@@ -0,0 +1,89 @@
+/* -*- 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 "nsIURI.h"
+
+#include "nsISystemProxySettings.h"
+#include "nsIServiceManager.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsPrintfCString.h"
+#include "nsNetCID.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIURI.h"
+
+#include "AndroidBridge.h"
+
+class nsAndroidSystemProxySettings : public nsISystemProxySettings
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISYSTEMPROXYSETTINGS
+
+ nsAndroidSystemProxySettings() {};
+ nsresult Init();
+
+private:
+ virtual ~nsAndroidSystemProxySettings() {}
+};
+
+NS_IMPL_ISUPPORTS(nsAndroidSystemProxySettings, nsISystemProxySettings)
+
+NS_IMETHODIMP
+nsAndroidSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly)
+{
+ *aMainThreadOnly = true;
+ return NS_OK;
+}
+
+
+nsresult
+nsAndroidSystemProxySettings::Init()
+{
+ return NS_OK;
+}
+
+nsresult
+nsAndroidSystemProxySettings::GetPACURI(nsACString& aResult)
+{
+ return NS_OK;
+}
+
+nsresult
+nsAndroidSystemProxySettings::GetProxyForURI(const nsACString & aSpec,
+ const nsACString & aScheme,
+ const nsACString & aHost,
+ const int32_t aPort,
+ nsACString & aResult)
+{
+ return mozilla::AndroidBridge::Bridge()->GetProxyForURI(aSpec, aScheme, aHost, aPort, aResult);
+}
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsAndroidSystemProxySettings, Init)
+
+#define NS_ANDROIDSYSTEMPROXYSERVICE_CID \
+ {0xf01f0060, 0x3708, 0x478e, \
+ {0xb9, 0x35, 0x3e, 0xce, 0x8b, 0xe2, 0x94, 0xb8}}
+
+NS_DEFINE_NAMED_CID(NS_ANDROIDSYSTEMPROXYSERVICE_CID);
+
+void test() {};
+
+static const mozilla::Module::CIDEntry kSysProxyCIDs[] = {
+ { &kNS_ANDROIDSYSTEMPROXYSERVICE_CID, false, nullptr, nsAndroidSystemProxySettingsConstructor },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kSysProxyContracts[] = {
+ { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_ANDROIDSYSTEMPROXYSERVICE_CID },
+ { nullptr }
+};
+
+static const mozilla::Module kSysProxyModule = {
+ mozilla::Module::kVersion,
+ kSysProxyCIDs,
+ kSysProxyContracts
+};
+
+NSMODULE_DEFN(nsAndroidProxyModule) = &kSysProxyModule;
diff --git a/toolkit/system/gnome/moz.build b/toolkit/system/gnome/moz.build
new file mode 100644
index 000000000..14b472be3
--- /dev/null
+++ b/toolkit/system/gnome/moz.build
@@ -0,0 +1,37 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'nsAlertsIconListener.cpp',
+ 'nsGnomeModule.cpp',
+ 'nsSystemAlertsService.cpp',
+]
+
+if CONFIG['MOZ_ENABLE_GCONF']:
+ SOURCES += [
+ 'nsGConfService.cpp',
+ ]
+
+if CONFIG['MOZ_ENABLE_GIO']:
+ SOURCES += [
+ 'nsGIOService.cpp',
+ 'nsGSettingsService.cpp',
+ 'nsPackageKitService.cpp'
+ ]
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '/toolkit/components/build/',
+]
+
+CXXFLAGS += CONFIG['MOZ_GCONF_CFLAGS']
+CXXFLAGS += CONFIG['MOZ_GIO_CFLAGS']
+CXXFLAGS += CONFIG['GLIB_CFLAGS']
+CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
+
+if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+ CXXFLAGS += CONFIG['TK_CFLAGS']
diff --git a/toolkit/system/gnome/nsAlertsIconListener.cpp b/toolkit/system/gnome/nsAlertsIconListener.cpp
new file mode 100644
index 000000000..b11672d30
--- /dev/null
+++ b/toolkit/system/gnome/nsAlertsIconListener.cpp
@@ -0,0 +1,327 @@
+/* -*- 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 "nsAlertsIconListener.h"
+#include "imgIContainer.h"
+#include "imgIRequest.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "nsSystemAlertsService.h"
+#include "nsIAlertsService.h"
+#include "nsICancelable.h"
+#include "nsIImageToPixbuf.h"
+#include "nsIStringBundle.h"
+#include "nsIObserverService.h"
+#include "nsIURI.h"
+#include "nsCRT.h"
+
+#include <dlfcn.h>
+#include <gdk/gdk.h>
+
+static bool gHasActions = false;
+static bool gHasCaps = false;
+
+void* nsAlertsIconListener::libNotifyHandle = nullptr;
+bool nsAlertsIconListener::libNotifyNotAvail = false;
+nsAlertsIconListener::notify_is_initted_t nsAlertsIconListener::notify_is_initted = nullptr;
+nsAlertsIconListener::notify_init_t nsAlertsIconListener::notify_init = nullptr;
+nsAlertsIconListener::notify_get_server_caps_t nsAlertsIconListener::notify_get_server_caps = nullptr;
+nsAlertsIconListener::notify_notification_new_t nsAlertsIconListener::notify_notification_new = nullptr;
+nsAlertsIconListener::notify_notification_show_t nsAlertsIconListener::notify_notification_show = nullptr;
+nsAlertsIconListener::notify_notification_set_icon_from_pixbuf_t nsAlertsIconListener::notify_notification_set_icon_from_pixbuf = nullptr;
+nsAlertsIconListener::notify_notification_add_action_t nsAlertsIconListener::notify_notification_add_action = nullptr;
+nsAlertsIconListener::notify_notification_close_t nsAlertsIconListener::notify_notification_close = nullptr;
+
+static void notify_action_cb(NotifyNotification *notification,
+ gchar *action, gpointer user_data)
+{
+ nsAlertsIconListener* alert = static_cast<nsAlertsIconListener*> (user_data);
+ alert->SendCallback();
+}
+
+static void notify_closed_marshal(GClosure* closure,
+ GValue* return_value,
+ guint n_param_values,
+ const GValue* param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ MOZ_ASSERT(n_param_values >= 1, "No object in params");
+
+ nsAlertsIconListener* alert =
+ static_cast<nsAlertsIconListener*>(closure->data);
+ alert->SendClosed();
+ NS_RELEASE(alert);
+}
+
+static GdkPixbuf*
+GetPixbufFromImgRequest(imgIRequest* aRequest)
+{
+ nsCOMPtr<imgIContainer> image;
+ nsresult rv = aRequest->GetImage(getter_AddRefs(image));
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIImageToPixbuf> imgToPixbuf =
+ do_GetService("@mozilla.org/widget/image-to-gdk-pixbuf;1");
+
+ return imgToPixbuf->ConvertImageToPixbuf(image);
+}
+
+NS_IMPL_ISUPPORTS(nsAlertsIconListener, nsIAlertNotificationImageListener,
+ nsIObserver, nsISupportsWeakReference)
+
+nsAlertsIconListener::nsAlertsIconListener(nsSystemAlertsService* aBackend,
+ const nsAString& aAlertName)
+: mAlertName(aAlertName),
+ mBackend(aBackend),
+ mNotification(nullptr)
+{
+ if (!libNotifyHandle && !libNotifyNotAvail) {
+ libNotifyHandle = dlopen("libnotify.so.4", RTLD_LAZY);
+ if (!libNotifyHandle) {
+ libNotifyHandle = dlopen("libnotify.so.1", RTLD_LAZY);
+ if (!libNotifyHandle) {
+ libNotifyNotAvail = true;
+ return;
+ }
+ }
+
+ notify_is_initted = (notify_is_initted_t)dlsym(libNotifyHandle, "notify_is_initted");
+ notify_init = (notify_init_t)dlsym(libNotifyHandle, "notify_init");
+ notify_get_server_caps = (notify_get_server_caps_t)dlsym(libNotifyHandle, "notify_get_server_caps");
+ notify_notification_new = (notify_notification_new_t)dlsym(libNotifyHandle, "notify_notification_new");
+ notify_notification_show = (notify_notification_show_t)dlsym(libNotifyHandle, "notify_notification_show");
+ notify_notification_set_icon_from_pixbuf = (notify_notification_set_icon_from_pixbuf_t)dlsym(libNotifyHandle, "notify_notification_set_icon_from_pixbuf");
+ notify_notification_add_action = (notify_notification_add_action_t)dlsym(libNotifyHandle, "notify_notification_add_action");
+ notify_notification_close = (notify_notification_close_t)dlsym(libNotifyHandle, "notify_notification_close");
+ if (!notify_is_initted || !notify_init || !notify_get_server_caps || !notify_notification_new || !notify_notification_show || !notify_notification_set_icon_from_pixbuf || !notify_notification_add_action || !notify_notification_close) {
+ dlclose(libNotifyHandle);
+ libNotifyHandle = nullptr;
+ }
+ }
+}
+
+nsAlertsIconListener::~nsAlertsIconListener()
+{
+ mBackend->RemoveListener(mAlertName, this);
+ // Don't dlclose libnotify as it uses atexit().
+}
+
+NS_IMETHODIMP
+nsAlertsIconListener::OnImageMissing(nsISupports*)
+{
+ // This notification doesn't have an image, or there was an error getting
+ // the image. Show the notification without an icon.
+ return ShowAlert(nullptr);
+}
+
+NS_IMETHODIMP
+nsAlertsIconListener::OnImageReady(nsISupports*, imgIRequest* aRequest)
+{
+ GdkPixbuf* imagePixbuf = GetPixbufFromImgRequest(aRequest);
+ if (!imagePixbuf) {
+ ShowAlert(nullptr);
+ } else {
+ ShowAlert(imagePixbuf);
+ g_object_unref(imagePixbuf);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf)
+{
+ if (!mBackend->IsActiveListener(mAlertName, this))
+ return NS_OK;
+
+ mNotification = notify_notification_new(mAlertTitle.get(), mAlertText.get(),
+ nullptr, nullptr);
+
+ if (!mNotification)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsCOMPtr<nsIObserverService> obsServ =
+ do_GetService("@mozilla.org/observer-service;1");
+ if (obsServ)
+ obsServ->AddObserver(this, "quit-application", true);
+
+ if (aPixbuf)
+ notify_notification_set_icon_from_pixbuf(mNotification, aPixbuf);
+
+ NS_ADDREF(this);
+ if (mAlertHasAction) {
+ // What we put as the label doesn't matter here, if the action
+ // string is "default" then that makes the entire bubble clickable
+ // rather than creating a button.
+ notify_notification_add_action(mNotification, "default", "Activate",
+ notify_action_cb, this, nullptr);
+ }
+
+ // Fedora 10 calls NotifyNotification "closed" signal handlers with a
+ // different signature, so a marshaller is used instead of a C callback to
+ // get the user_data (this) in a parseable format. |closure| is created
+ // with a floating reference, which gets sunk by g_signal_connect_closure().
+ GClosure* closure = g_closure_new_simple(sizeof(GClosure), this);
+ g_closure_set_marshal(closure, notify_closed_marshal);
+ mClosureHandler = g_signal_connect_closure(mNotification, "closed", closure, FALSE);
+ GError* error = nullptr;
+ if (!notify_notification_show(mNotification, &error)) {
+ NS_WARNING(error->message);
+ g_error_free(error);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mAlertListener)
+ mAlertListener->Observe(nullptr, "alertshow", mAlertCookie.get());
+
+ return NS_OK;
+}
+
+void
+nsAlertsIconListener::SendCallback()
+{
+ if (mAlertListener)
+ mAlertListener->Observe(nullptr, "alertclickcallback", mAlertCookie.get());
+}
+
+void
+nsAlertsIconListener::SendClosed()
+{
+ if (mNotification) {
+ g_object_unref(mNotification);
+ mNotification = nullptr;
+ }
+ NotifyFinished();
+}
+
+NS_IMETHODIMP
+nsAlertsIconListener::Observe(nsISupports *aSubject, const char *aTopic,
+ const char16_t *aData) {
+ // We need to close any open notifications upon application exit, otherwise
+ // we will leak since libnotify holds a ref for us.
+ if (!nsCRT::strcmp(aTopic, "quit-application") && mNotification) {
+ g_signal_handler_disconnect(mNotification, mClosureHandler);
+ g_object_unref(mNotification);
+ mNotification = nullptr;
+ Release(); // equivalent to NS_RELEASE(this)
+ }
+ return NS_OK;
+}
+
+nsresult
+nsAlertsIconListener::Close()
+{
+ if (mIconRequest) {
+ mIconRequest->Cancel(NS_BINDING_ABORTED);
+ mIconRequest = nullptr;
+ }
+
+ if (!mNotification) {
+ NotifyFinished();
+ return NS_OK;
+ }
+
+ GError* error = nullptr;
+ if (!notify_notification_close(mNotification, &error)) {
+ NS_WARNING(error->message);
+ g_error_free(error);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsAlertsIconListener::InitAlertAsync(nsIAlertNotification* aAlert,
+ nsIObserver* aAlertListener)
+{
+ if (!libNotifyHandle)
+ return NS_ERROR_FAILURE;
+
+ if (!notify_is_initted()) {
+ // Give the name of this application to libnotify
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ do_GetService(NS_STRINGBUNDLE_CONTRACTID);
+
+ nsAutoCString appShortName;
+ if (bundleService) {
+ nsCOMPtr<nsIStringBundle> bundle;
+ bundleService->CreateBundle("chrome://branding/locale/brand.properties",
+ getter_AddRefs(bundle));
+ nsAutoString appName;
+
+ if (bundle) {
+ bundle->GetStringFromName(u"brandShortName",
+ getter_Copies(appName));
+ appShortName = NS_ConvertUTF16toUTF8(appName);
+ } else {
+ NS_WARNING("brand.properties not present, using default application name");
+ appShortName.AssignLiteral("Mozilla");
+ }
+ } else {
+ appShortName.AssignLiteral("Mozilla");
+ }
+
+ if (!notify_init(appShortName.get()))
+ return NS_ERROR_FAILURE;
+
+ GList *server_caps = notify_get_server_caps();
+ if (server_caps) {
+ gHasCaps = true;
+ for (GList* cap = server_caps; cap != nullptr; cap = cap->next) {
+ if (!strcmp((char*) cap->data, "actions")) {
+ gHasActions = true;
+ break;
+ }
+ }
+ g_list_foreach(server_caps, (GFunc)g_free, nullptr);
+ g_list_free(server_caps);
+ }
+ }
+
+ if (!gHasCaps) {
+ // if notify_get_server_caps() failed above we need to assume
+ // there is no notification-server to display anything
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = aAlert->GetTextClickable(&mAlertHasAction);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!gHasActions && mAlertHasAction)
+ return NS_ERROR_FAILURE; // No good, fallback to XUL
+
+ nsAutoString title;
+ rv = aAlert->GetTitle(title);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // Workaround for a libnotify bug - blank titles aren't dealt with
+ // properly so we use a space
+ if (title.IsEmpty()) {
+ mAlertTitle = NS_LITERAL_CSTRING(" ");
+ } else {
+ mAlertTitle = NS_ConvertUTF16toUTF8(title);
+ }
+
+ nsAutoString text;
+ rv = aAlert->GetText(text);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mAlertText = NS_ConvertUTF16toUTF8(text);
+
+ mAlertListener = aAlertListener;
+
+ rv = aAlert->GetCookie(mAlertCookie);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aAlert->LoadImage(/* aTimeout = */ 0, this, /* aUserData = */ nullptr,
+ getter_AddRefs(mIconRequest));
+}
+
+void nsAlertsIconListener::NotifyFinished()
+{
+ if (mAlertListener)
+ mAlertListener->Observe(nullptr, "alertfinished", mAlertCookie.get());
+}
diff --git a/toolkit/system/gnome/nsAlertsIconListener.h b/toolkit/system/gnome/nsAlertsIconListener.h
new file mode 100644
index 000000000..f3a745d33
--- /dev/null
+++ b/toolkit/system/gnome/nsAlertsIconListener.h
@@ -0,0 +1,91 @@
+/* -*- 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 nsAlertsIconListener_h__
+#define nsAlertsIconListener_h__
+
+#include "nsCOMPtr.h"
+#include "nsIAlertsService.h"
+#include "nsString.h"
+#include "nsIObserver.h"
+#include "nsWeakReference.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+class nsIAlertNotification;
+class nsICancelable;
+class nsSystemAlertsService;
+
+struct NotifyNotification;
+
+class nsAlertsIconListener : public nsIAlertNotificationImageListener,
+ public nsIObserver,
+ public nsSupportsWeakReference
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIALERTNOTIFICATIONIMAGELISTENER
+ NS_DECL_NSIOBSERVER
+
+ nsAlertsIconListener(nsSystemAlertsService* aBackend,
+ const nsAString& aAlertName);
+
+ nsresult InitAlertAsync(nsIAlertNotification* aAlert,
+ nsIObserver* aAlertListener);
+ nsresult Close();
+
+ void SendCallback();
+ void SendClosed();
+
+protected:
+ virtual ~nsAlertsIconListener();
+
+ /**
+ * The only difference between libnotify.so.4 and libnotify.so.1 for these symbols
+ * is that notify_notification_new takes three arguments in libnotify.so.4 and
+ * four in libnotify.so.1.
+ * Passing the fourth argument as NULL is binary compatible.
+ */
+ typedef void (*NotifyActionCallback)(NotifyNotification*, char*, gpointer);
+ typedef bool (*notify_is_initted_t)(void);
+ typedef bool (*notify_init_t)(const char*);
+ typedef GList* (*notify_get_server_caps_t)(void);
+ typedef NotifyNotification* (*notify_notification_new_t)(const char*, const char*, const char*, const char*);
+ typedef bool (*notify_notification_show_t)(void*, GError**);
+ typedef void (*notify_notification_set_icon_from_pixbuf_t)(void*, GdkPixbuf*);
+ typedef void (*notify_notification_add_action_t)(void*, const char*, const char*, NotifyActionCallback, gpointer, GFreeFunc);
+ typedef bool (*notify_notification_close_t)(void*, GError**);
+
+ nsCOMPtr<nsICancelable> mIconRequest;
+ nsCString mAlertTitle;
+ nsCString mAlertText;
+
+ nsCOMPtr<nsIObserver> mAlertListener;
+ nsString mAlertCookie;
+ nsString mAlertName;
+
+ RefPtr<nsSystemAlertsService> mBackend;
+
+ bool mAlertHasAction;
+
+ static void* libNotifyHandle;
+ static bool libNotifyNotAvail;
+ static notify_is_initted_t notify_is_initted;
+ static notify_init_t notify_init;
+ static notify_get_server_caps_t notify_get_server_caps;
+ static notify_notification_new_t notify_notification_new;
+ static notify_notification_show_t notify_notification_show;
+ static notify_notification_set_icon_from_pixbuf_t notify_notification_set_icon_from_pixbuf;
+ static notify_notification_add_action_t notify_notification_add_action;
+ static notify_notification_close_t notify_notification_close;
+ NotifyNotification* mNotification;
+ gulong mClosureHandler;
+
+ nsresult ShowAlert(GdkPixbuf* aPixbuf);
+
+ void NotifyFinished();
+};
+
+#endif
diff --git a/toolkit/system/gnome/nsGConfService.cpp b/toolkit/system/gnome/nsGConfService.cpp
new file mode 100644
index 000000000..ede71e588
--- /dev/null
+++ b/toolkit/system/gnome/nsGConfService.cpp
@@ -0,0 +1,305 @@
+/* -*- 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 "nsGConfService.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIMutableArray.h"
+#include "prlink.h"
+
+#include <gconf/gconf-client.h>
+
+using namespace mozilla;
+
+#define GCONF_FUNCTIONS \
+ FUNC(gconf_client_get_default, GConfClient*, (void)) \
+ FUNC(gconf_client_get_bool, gboolean, (GConfClient*, const gchar*, GError**)) \
+ FUNC(gconf_client_get_string, gchar*, (GConfClient*, const gchar*, GError**)) \
+ FUNC(gconf_client_get_int, gint, (GConfClient*, const gchar*, GError**)) \
+ FUNC(gconf_client_get_float, gdouble, (GConfClient*, const gchar*, GError**)) \
+ FUNC(gconf_client_get_list, GSList*, (GConfClient*, const gchar*, GConfValueType, GError**)) \
+ FUNC(gconf_client_set_bool, gboolean, (GConfClient*, const gchar*, gboolean, GError**)) \
+ FUNC(gconf_client_set_string, gboolean, (GConfClient*, const gchar*, const gchar*, GError**)) \
+ FUNC(gconf_client_set_int, gboolean, (GConfClient*, const gchar*, gint, GError**)) \
+ FUNC(gconf_client_set_float, gboolean, (GConfClient*, const gchar*, gdouble, GError**)) \
+ FUNC(gconf_client_unset, gboolean, (GConfClient*, const gchar*, GError**))
+
+#define FUNC(name, type, params) \
+ typedef type (*_##name##_fn) params; \
+ static _##name##_fn _##name;
+
+GCONF_FUNCTIONS
+
+#undef FUNC
+
+#define gconf_client_get_default _gconf_client_get_default
+#define gconf_client_get_bool _gconf_client_get_bool
+#define gconf_client_get_string _gconf_client_get_string
+#define gconf_client_get_int _gconf_client_get_int
+#define gconf_client_get_float _gconf_client_get_float
+#define gconf_client_get_list _gconf_client_get_list
+#define gconf_client_set_bool _gconf_client_set_bool
+#define gconf_client_set_string _gconf_client_set_string
+#define gconf_client_set_int _gconf_client_set_int
+#define gconf_client_set_float _gconf_client_set_float
+#define gconf_client_unset _gconf_client_unset
+
+static PRLibrary *gconfLib = nullptr;
+
+typedef void (*nsGConfFunc)();
+struct nsGConfDynamicFunction {
+ const char *functionName;
+ nsGConfFunc *function;
+};
+
+nsGConfService::~nsGConfService()
+{
+ if (mClient)
+ g_object_unref(mClient);
+
+ // We don't unload gconf here because liborbit uses atexit(). In addition to
+ // this, it's not a good idea to unload any gobject based library, as it
+ // leaves types registered in glib's type system
+}
+
+nsresult
+nsGConfService::Init()
+{
+#define FUNC(name, type, params) { #name, (nsGConfFunc *)&_##name },
+ static const nsGConfDynamicFunction kGConfSymbols[] = {
+ GCONF_FUNCTIONS
+ };
+#undef FUNC
+
+ if (!gconfLib) {
+ gconfLib = PR_LoadLibrary("libgconf-2.so.4");
+ if (!gconfLib)
+ return NS_ERROR_FAILURE;
+ }
+
+ for (uint32_t i = 0; i < ArrayLength(kGConfSymbols); i++) {
+ *kGConfSymbols[i].function =
+ PR_FindFunctionSymbol(gconfLib, kGConfSymbols[i].functionName);
+ if (!*kGConfSymbols[i].function) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ mClient = gconf_client_get_default();
+ return mClient ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMPL_ISUPPORTS(nsGConfService, nsIGConfService)
+
+NS_IMETHODIMP
+nsGConfService::GetBool(const nsACString &aKey, bool *aResult)
+{
+ GError* error = nullptr;
+ *aResult = gconf_client_get_bool(mClient, PromiseFlatCString(aKey).get(),
+ &error);
+
+ if (error) {
+ g_error_free(error);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGConfService::GetString(const nsACString &aKey, nsACString &aResult)
+{
+ GError* error = nullptr;
+ gchar *result = gconf_client_get_string(mClient,
+ PromiseFlatCString(aKey).get(),
+ &error);
+
+ if (error) {
+ g_error_free(error);
+ return NS_ERROR_FAILURE;
+ }
+
+ // We do a string copy here so that the caller doesn't need to worry about
+ // freeing the string with g_free().
+
+ aResult.Assign(result);
+ g_free(result);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGConfService::GetInt(const nsACString &aKey, int32_t* aResult)
+{
+ GError* error = nullptr;
+ *aResult = gconf_client_get_int(mClient, PromiseFlatCString(aKey).get(),
+ &error);
+
+ if (error) {
+ g_error_free(error);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGConfService::GetFloat(const nsACString &aKey, float* aResult)
+{
+ GError* error = nullptr;
+ *aResult = gconf_client_get_float(mClient, PromiseFlatCString(aKey).get(),
+ &error);
+
+ if (error) {
+ g_error_free(error);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGConfService::GetStringList(const nsACString &aKey, nsIArray** aResult)
+{
+ nsCOMPtr<nsIMutableArray> items(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ if (!items)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ GError* error = nullptr;
+ GSList* list = gconf_client_get_list(mClient, PromiseFlatCString(aKey).get(),
+ GCONF_VALUE_STRING, &error);
+ if (error) {
+ g_error_free(error);
+ return NS_ERROR_FAILURE;
+ }
+
+ for (GSList* l = list; l; l = l->next) {
+ nsCOMPtr<nsISupportsString> obj(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+ if (!obj) {
+ g_slist_free(list);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ obj->SetData(NS_ConvertUTF8toUTF16((const char*)l->data));
+ items->AppendElement(obj, false);
+ g_free(l->data);
+ }
+
+ g_slist_free(list);
+ items.forget(aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGConfService::SetBool(const nsACString &aKey, bool aValue)
+{
+ bool res = gconf_client_set_bool(mClient, PromiseFlatCString(aKey).get(),
+ aValue, nullptr);
+
+ return res ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsGConfService::SetString(const nsACString &aKey, const nsACString &aValue)
+{
+ bool res = gconf_client_set_string(mClient, PromiseFlatCString(aKey).get(),
+ PromiseFlatCString(aValue).get(),
+ nullptr);
+
+ return res ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsGConfService::SetInt(const nsACString &aKey, int32_t aValue)
+{
+ bool res = gconf_client_set_int(mClient, PromiseFlatCString(aKey).get(),
+ aValue, nullptr);
+
+ return res ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsGConfService::SetFloat(const nsACString &aKey, float aValue)
+{
+ bool res = gconf_client_set_float(mClient, PromiseFlatCString(aKey).get(),
+ aValue, nullptr);
+
+ return res ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsGConfService::GetAppForProtocol(const nsACString &aScheme, bool *aEnabled,
+ nsACString &aHandler)
+{
+ nsAutoCString key("/desktop/gnome/url-handlers/");
+ key.Append(aScheme);
+ key.AppendLiteral("/command");
+
+ GError *err = nullptr;
+ gchar *command = gconf_client_get_string(mClient, key.get(), &err);
+ if (!err && command) {
+ key.Replace(key.Length() - 7, 7, NS_LITERAL_CSTRING("enabled"));
+ *aEnabled = gconf_client_get_bool(mClient, key.get(), &err);
+ } else {
+ *aEnabled = false;
+ }
+
+ aHandler.Assign(command);
+ g_free(command);
+
+ if (err) {
+ g_error_free(err);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGConfService::HandlerRequiresTerminal(const nsACString &aScheme,
+ bool *aResult)
+{
+ nsAutoCString key("/desktop/gnome/url-handlers/");
+ key.Append(aScheme);
+ key.AppendLiteral("/requires_terminal");
+
+ GError *err = nullptr;
+ *aResult = gconf_client_get_bool(mClient, key.get(), &err);
+ if (err) {
+ g_error_free(err);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGConfService::SetAppForProtocol(const nsACString &aScheme,
+ const nsACString &aCommand)
+{
+ nsAutoCString key("/desktop/gnome/url-handlers/");
+ key.Append(aScheme);
+ key.AppendLiteral("/command");
+
+ bool res = gconf_client_set_string(mClient, key.get(),
+ PromiseFlatCString(aCommand).get(),
+ nullptr);
+ if (res) {
+ key.Replace(key.Length() - 7, 7, NS_LITERAL_CSTRING("enabled"));
+ res = gconf_client_set_bool(mClient, key.get(), true, nullptr);
+ if (res) {
+ key.Replace(key.Length() - 7, 7, NS_LITERAL_CSTRING("needs_terminal"));
+ res = gconf_client_set_bool(mClient, key.get(), false, nullptr);
+ if (res) {
+ key.Replace(key.Length() - 14, 14, NS_LITERAL_CSTRING("command-id"));
+ res = gconf_client_unset(mClient, key.get(), nullptr);
+ }
+ }
+ }
+
+ return res ? NS_OK : NS_ERROR_FAILURE;
+}
diff --git a/toolkit/system/gnome/nsGConfService.h b/toolkit/system/gnome/nsGConfService.h
new file mode 100644
index 000000000..4e500a40d
--- /dev/null
+++ b/toolkit/system/gnome/nsGConfService.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsGConfService_h_
+#define nsGConfService_h_
+
+#include "nsIGConfService.h"
+#include "gconf/gconf-client.h"
+#include "mozilla/Attributes.h"
+
+#define NS_GCONFSERVICE_CID \
+{0xd96d5985, 0xa13a, 0x4bdc, {0x93, 0x86, 0xef, 0x34, 0x8d, 0x7a, 0x97, 0xa1}}
+
+class nsGConfService final : public nsIGConfService
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIGCONFSERVICE
+
+ nsGConfService() : mClient(nullptr) {}
+ nsresult Init();
+
+private:
+ ~nsGConfService();
+
+ GConfClient *mClient;
+};
+
+#endif
diff --git a/toolkit/system/gnome/nsGIOService.cpp b/toolkit/system/gnome/nsGIOService.cpp
new file mode 100644
index 000000000..9196c7d76
--- /dev/null
+++ b/toolkit/system/gnome/nsGIOService.cpp
@@ -0,0 +1,475 @@
+/* -*- 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 "nsGIOService.h"
+#include "nsString.h"
+#include "nsIURI.h"
+#include "nsTArray.h"
+#include "nsIStringEnumerator.h"
+#include "nsAutoPtr.h"
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#ifdef MOZ_ENABLE_DBUS
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#endif
+
+
+class nsGIOMimeApp final : public nsIGIOMimeApp
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIGIOMIMEAPP
+
+ explicit nsGIOMimeApp(GAppInfo* aApp) : mApp(aApp) {}
+
+private:
+ ~nsGIOMimeApp() { g_object_unref(mApp); }
+
+ GAppInfo *mApp;
+};
+
+NS_IMPL_ISUPPORTS(nsGIOMimeApp, nsIGIOMimeApp)
+
+NS_IMETHODIMP
+nsGIOMimeApp::GetId(nsACString& aId)
+{
+ aId.Assign(g_app_info_get_id(mApp));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGIOMimeApp::GetName(nsACString& aName)
+{
+ aName.Assign(g_app_info_get_name(mApp));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGIOMimeApp::GetCommand(nsACString& aCommand)
+{
+ const char *cmd = g_app_info_get_commandline(mApp);
+ if (!cmd)
+ return NS_ERROR_FAILURE;
+ aCommand.Assign(cmd);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGIOMimeApp::GetExpectsURIs(int32_t* aExpects)
+{
+ *aExpects = g_app_info_supports_uris(mApp);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGIOMimeApp::Launch(const nsACString& aUri)
+{
+ GList uris = { 0 };
+ nsPromiseFlatCString flatUri(aUri);
+ uris.data = const_cast<char*>(flatUri.get());
+
+ GError *error = nullptr;
+ gboolean result = g_app_info_launch_uris(mApp, &uris, nullptr, &error);
+
+ if (!result) {
+ g_warning("Cannot launch application: %s", error->message);
+ g_error_free(error);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+class GIOUTF8StringEnumerator final : public nsIUTF8StringEnumerator
+{
+ ~GIOUTF8StringEnumerator() { }
+
+public:
+ GIOUTF8StringEnumerator() : mIndex(0) { }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIUTF8STRINGENUMERATOR
+
+ nsTArray<nsCString> mStrings;
+ uint32_t mIndex;
+};
+
+NS_IMPL_ISUPPORTS(GIOUTF8StringEnumerator, nsIUTF8StringEnumerator)
+
+NS_IMETHODIMP
+GIOUTF8StringEnumerator::HasMore(bool* aResult)
+{
+ *aResult = mIndex < mStrings.Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GIOUTF8StringEnumerator::GetNext(nsACString& aResult)
+{
+ if (mIndex >= mStrings.Length())
+ return NS_ERROR_UNEXPECTED;
+
+ aResult.Assign(mStrings[mIndex]);
+ ++mIndex;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGIOMimeApp::GetSupportedURISchemes(nsIUTF8StringEnumerator** aSchemes)
+{
+ *aSchemes = nullptr;
+
+ RefPtr<GIOUTF8StringEnumerator> array = new GIOUTF8StringEnumerator();
+ NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
+
+ GVfs *gvfs = g_vfs_get_default();
+
+ if (!gvfs) {
+ g_warning("Cannot get GVfs object.");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ const gchar* const * uri_schemes = g_vfs_get_supported_uri_schemes(gvfs);
+
+ while (*uri_schemes != nullptr) {
+ if (!array->mStrings.AppendElement(*uri_schemes)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ uri_schemes++;
+ }
+
+ array.forget(aSchemes);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGIOMimeApp::SetAsDefaultForMimeType(nsACString const& aMimeType)
+{
+ char *content_type =
+ g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get());
+ if (!content_type)
+ return NS_ERROR_FAILURE;
+ GError *error = nullptr;
+ g_app_info_set_as_default_for_type(mApp,
+ content_type,
+ &error);
+ if (error) {
+ g_warning("Cannot set application as default for MIME type (%s): %s",
+ PromiseFlatCString(aMimeType).get(),
+ error->message);
+ g_error_free(error);
+ g_free(content_type);
+ return NS_ERROR_FAILURE;
+ }
+
+ g_free(content_type);
+ return NS_OK;
+}
+/**
+ * Set default application for files with given extensions
+ * @param fileExts string of space separated extensions
+ * @return NS_OK when application was set as default for given extensions,
+ * NS_ERROR_FAILURE otherwise
+ */
+NS_IMETHODIMP
+nsGIOMimeApp::SetAsDefaultForFileExtensions(nsACString const& fileExts)
+{
+ GError *error = nullptr;
+ char *extensions = g_strdup(PromiseFlatCString(fileExts).get());
+ char *ext_pos = extensions;
+ char *space_pos;
+
+ while ( (space_pos = strchr(ext_pos, ' ')) || (*ext_pos != '\0') ) {
+ if (space_pos) {
+ *space_pos = '\0';
+ }
+ g_app_info_set_as_default_for_extension(mApp, ext_pos, &error);
+ if (error) {
+ g_warning("Cannot set application as default for extension (%s): %s",
+ ext_pos,
+ error->message);
+ g_error_free(error);
+ g_free(extensions);
+ return NS_ERROR_FAILURE;
+ }
+ if (space_pos) {
+ ext_pos = space_pos + 1;
+ } else {
+ *ext_pos = '\0';
+ }
+ }
+ g_free(extensions);
+ return NS_OK;
+}
+
+/**
+ * Set default application for URI's of a particular scheme
+ * @param aURIScheme string containing the URI scheme
+ * @return NS_OK when application was set as default for URI scheme,
+ * NS_ERROR_FAILURE otherwise
+ */
+NS_IMETHODIMP
+nsGIOMimeApp::SetAsDefaultForURIScheme(nsACString const& aURIScheme)
+{
+ GError *error = nullptr;
+ nsAutoCString contentType("x-scheme-handler/");
+ contentType.Append(aURIScheme);
+
+ g_app_info_set_as_default_for_type(mApp,
+ contentType.get(),
+ &error);
+ if (error) {
+ g_warning("Cannot set application as default for URI scheme (%s): %s",
+ PromiseFlatCString(aURIScheme).get(),
+ error->message);
+ g_error_free(error);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsGIOService, nsIGIOService)
+
+NS_IMETHODIMP
+nsGIOService::GetMimeTypeFromExtension(const nsACString& aExtension,
+ nsACString& aMimeType)
+{
+ nsAutoCString fileExtToUse("file.");
+ fileExtToUse.Append(aExtension);
+
+ gboolean result_uncertain;
+ char *content_type = g_content_type_guess(fileExtToUse.get(),
+ nullptr,
+ 0,
+ &result_uncertain);
+ if (!content_type)
+ return NS_ERROR_FAILURE;
+
+ char *mime_type = g_content_type_get_mime_type(content_type);
+ if (!mime_type) {
+ g_free(content_type);
+ return NS_ERROR_FAILURE;
+ }
+
+ aMimeType.Assign(mime_type);
+
+ g_free(mime_type);
+ g_free(content_type);
+
+ return NS_OK;
+}
+// used in nsGNOMERegistry
+// -----------------------------------------------------------------------------
+NS_IMETHODIMP
+nsGIOService::GetAppForURIScheme(const nsACString& aURIScheme,
+ nsIGIOMimeApp** aApp)
+{
+ *aApp = nullptr;
+
+ GAppInfo *app_info = g_app_info_get_default_for_uri_scheme(
+ PromiseFlatCString(aURIScheme).get());
+ if (app_info) {
+ nsGIOMimeApp *mozApp = new nsGIOMimeApp(app_info);
+ NS_ADDREF(*aApp = mozApp);
+ } else {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGIOService::GetAppForMimeType(const nsACString& aMimeType,
+ nsIGIOMimeApp** aApp)
+{
+ *aApp = nullptr;
+ char *content_type =
+ g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get());
+ if (!content_type)
+ return NS_ERROR_FAILURE;
+
+ GAppInfo *app_info = g_app_info_get_default_for_type(content_type, false);
+ if (app_info) {
+ nsGIOMimeApp *mozApp = new nsGIOMimeApp(app_info);
+ NS_ENSURE_TRUE(mozApp, NS_ERROR_OUT_OF_MEMORY);
+ NS_ADDREF(*aApp = mozApp);
+ } else {
+ g_free(content_type);
+ return NS_ERROR_FAILURE;
+ }
+ g_free(content_type);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGIOService::GetDescriptionForMimeType(const nsACString& aMimeType,
+ nsACString& aDescription)
+{
+ char *content_type =
+ g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get());
+ if (!content_type)
+ return NS_ERROR_FAILURE;
+
+ char *desc = g_content_type_get_description(content_type);
+ if (!desc) {
+ g_free(content_type);
+ return NS_ERROR_FAILURE;
+ }
+
+ aDescription.Assign(desc);
+ g_free(content_type);
+ g_free(desc);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGIOService::ShowURI(nsIURI* aURI)
+{
+ nsAutoCString spec;
+ nsresult rv = aURI->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ GError *error = nullptr;
+ if (!g_app_info_launch_default_for_uri(spec.get(), nullptr, &error)) {
+ g_warning("Could not launch default application for URI: %s", error->message);
+ g_error_free(error);
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGIOService::ShowURIForInput(const nsACString& aUri)
+{
+ GFile *file = g_file_new_for_commandline_arg(PromiseFlatCString(aUri).get());
+ char* spec = g_file_get_uri(file);
+ nsresult rv = NS_ERROR_FAILURE;
+ GError *error = nullptr;
+
+ g_app_info_launch_default_for_uri(spec, nullptr, &error);
+ if (error) {
+ g_warning("Cannot launch default application: %s", error->message);
+ g_error_free(error);
+ } else {
+ rv = NS_OK;
+ }
+ g_object_unref(file);
+ g_free(spec);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsGIOService::OrgFreedesktopFileManager1ShowItems(const nsACString& aPath)
+{
+#ifndef MOZ_ENABLE_DBUS
+ return NS_ERROR_FAILURE;
+#else
+ GError* error = nullptr;
+ static bool org_freedesktop_FileManager1_exists = true;
+
+ if (!org_freedesktop_FileManager1_exists) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ DBusGConnection* dbusGConnection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+
+ if (!dbusGConnection) {
+ if (error) {
+ g_printerr("Failed to open connection to session bus: %s\n", error->message);
+ g_error_free(error);
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+ char *uri = g_filename_to_uri(PromiseFlatCString(aPath).get(), nullptr, nullptr);
+ if (uri == nullptr) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DBusConnection* dbusConnection = dbus_g_connection_get_connection(dbusGConnection);
+ // Make sure we do not exit the entire program if DBus connection get lost.
+ dbus_connection_set_exit_on_disconnect(dbusConnection, false);
+
+ DBusGProxy* dbusGProxy = dbus_g_proxy_new_for_name(dbusGConnection,
+ "org.freedesktop.FileManager1",
+ "/org/freedesktop/FileManager1",
+ "org.freedesktop.FileManager1");
+
+ const char *uris[2] = { uri, nullptr };
+ gboolean rv_dbus_call = dbus_g_proxy_call (dbusGProxy, "ShowItems", nullptr, G_TYPE_STRV, uris,
+ G_TYPE_STRING, "", G_TYPE_INVALID, G_TYPE_INVALID);
+
+ g_object_unref(dbusGProxy);
+ dbus_g_connection_unref(dbusGConnection);
+ g_free(uri);
+
+ if (!rv_dbus_call) {
+ org_freedesktop_FileManager1_exists = false;
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return NS_OK;
+#endif
+}
+
+/**
+ * Create or find already existing application info for specified command
+ * and application name.
+ * @param cmd command to execute
+ * @param appName application name
+ * @param appInfo location where created GAppInfo is stored
+ * @return NS_OK when object is created, NS_ERROR_FAILURE otherwise.
+ */
+NS_IMETHODIMP
+nsGIOService::CreateAppFromCommand(nsACString const& cmd,
+ nsACString const& appName,
+ nsIGIOMimeApp** appInfo)
+{
+ GError *error = nullptr;
+ *appInfo = nullptr;
+
+ GAppInfo *app_info = nullptr, *app_info_from_list = nullptr;
+ GList *apps = g_app_info_get_all();
+ GList *apps_p = apps;
+
+ // Try to find relevant and existing GAppInfo in all installed application
+ // We do this by comparing each GAppInfo's executable with out own
+ while (apps_p) {
+ app_info_from_list = (GAppInfo*) apps_p->data;
+ if (!app_info) {
+ // If the executable is not absolute, get it's full path
+ char *executable = g_find_program_in_path(g_app_info_get_executable(app_info_from_list));
+
+ if (executable && strcmp(executable, PromiseFlatCString(cmd).get()) == 0) {
+ g_object_ref (app_info_from_list);
+ app_info = app_info_from_list;
+ }
+ g_free(executable);
+ }
+
+ g_object_unref(app_info_from_list);
+ apps_p = apps_p->next;
+ }
+ g_list_free(apps);
+
+ if (!app_info) {
+ app_info = g_app_info_create_from_commandline(PromiseFlatCString(cmd).get(),
+ PromiseFlatCString(appName).get(),
+ G_APP_INFO_CREATE_SUPPORTS_URIS,
+ &error);
+ }
+
+ if (!app_info) {
+ g_warning("Cannot create application info from command: %s", error->message);
+ g_error_free(error);
+ return NS_ERROR_FAILURE;
+ }
+ nsGIOMimeApp *mozApp = new nsGIOMimeApp(app_info);
+ NS_ENSURE_TRUE(mozApp, NS_ERROR_OUT_OF_MEMORY);
+ NS_ADDREF(*appInfo = mozApp);
+ return NS_OK;
+}
diff --git a/toolkit/system/gnome/nsGIOService.h b/toolkit/system/gnome/nsGIOService.h
new file mode 100644
index 000000000..2ae7a7e1a
--- /dev/null
+++ b/toolkit/system/gnome/nsGIOService.h
@@ -0,0 +1,24 @@
+/* -*- 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 nsGIOService_h_
+#define nsGIOService_h_
+
+#include "nsIGIOService.h"
+
+#define NS_GIOSERVICE_CID \
+{0xe3a1f3c9, 0x3ae1, 0x4b40, {0xa5, 0xe0, 0x7b, 0x45, 0x7f, 0xc9, 0xa9, 0xad}}
+
+class nsGIOService final : public nsIGIOService
+{
+ ~nsGIOService() {}
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIGIOSERVICE
+};
+
+#endif
+
diff --git a/toolkit/system/gnome/nsGSettingsService.cpp b/toolkit/system/gnome/nsGSettingsService.cpp
new file mode 100644
index 000000000..d8c46b4fe
--- /dev/null
+++ b/toolkit/system/gnome/nsGSettingsService.cpp
@@ -0,0 +1,350 @@
+/* -*- 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 "nsGSettingsService.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsMemory.h"
+#include "prlink.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIMutableArray.h"
+#include "nsISupportsPrimitives.h"
+
+#include <glib.h>
+#include <glib-object.h>
+
+using namespace mozilla;
+
+typedef struct _GSettings GSettings;
+typedef struct _GVariantType GVariantType;
+typedef struct _GVariant GVariant;
+
+#ifndef G_VARIANT_TYPE_INT32
+# define G_VARIANT_TYPE_INT32 ((const GVariantType *) "i")
+# define G_VARIANT_TYPE_BOOLEAN ((const GVariantType *) "b")
+# define G_VARIANT_TYPE_STRING ((const GVariantType *) "s")
+# define G_VARIANT_TYPE_OBJECT_PATH ((const GVariantType *) "o")
+# define G_VARIANT_TYPE_SIGNATURE ((const GVariantType *) "g")
+#endif
+#ifndef G_VARIANT_TYPE_STRING_ARRAY
+# define G_VARIANT_TYPE_STRING_ARRAY ((const GVariantType *) "as")
+#endif
+
+#define GSETTINGS_FUNCTIONS \
+ FUNC(g_settings_new, GSettings *, (const char* schema)) \
+ FUNC(g_settings_list_schemas, const char * const *, (void)) \
+ FUNC(g_settings_list_keys, char **, (GSettings* settings)) \
+ FUNC(g_settings_get_value, GVariant *, (GSettings* settings, const char* key)) \
+ FUNC(g_settings_set_value, gboolean, (GSettings* settings, const char* key, GVariant* value)) \
+ FUNC(g_settings_range_check, gboolean, (GSettings* settings, const char* key, GVariant* value)) \
+ FUNC(g_variant_get_int32, gint32, (GVariant* variant)) \
+ FUNC(g_variant_get_boolean, gboolean, (GVariant* variant)) \
+ FUNC(g_variant_get_string, const char *, (GVariant* value, gsize* length)) \
+ FUNC(g_variant_get_strv, const char **, (GVariant* value, gsize* length)) \
+ FUNC(g_variant_is_of_type, gboolean, (GVariant* value, const GVariantType* type)) \
+ FUNC(g_variant_new_int32, GVariant *, (gint32 value)) \
+ FUNC(g_variant_new_boolean, GVariant *, (gboolean value)) \
+ FUNC(g_variant_new_string, GVariant *, (const char* string)) \
+ FUNC(g_variant_unref, void, (GVariant* value))
+
+#define FUNC(name, type, params) \
+ typedef type (*_##name##_fn) params; \
+ static _##name##_fn _##name;
+
+GSETTINGS_FUNCTIONS
+
+#undef FUNC
+
+#define g_settings_new _g_settings_new
+#define g_settings_list_schemas _g_settings_list_schemas
+#define g_settings_list_keys _g_settings_list_keys
+#define g_settings_get_value _g_settings_get_value
+#define g_settings_set_value _g_settings_set_value
+#define g_settings_range_check _g_settings_range_check
+#define g_variant_get_int32 _g_variant_get_int32
+#define g_variant_get_boolean _g_variant_get_boolean
+#define g_variant_get_string _g_variant_get_string
+#define g_variant_get_strv _g_variant_get_strv
+#define g_variant_is_of_type _g_variant_is_of_type
+#define g_variant_new_int32 _g_variant_new_int32
+#define g_variant_new_boolean _g_variant_new_boolean
+#define g_variant_new_string _g_variant_new_string
+#define g_variant_unref _g_variant_unref
+
+static PRLibrary *gioLib = nullptr;
+
+class nsGSettingsCollection final : public nsIGSettingsCollection
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIGSETTINGSCOLLECTION
+
+ explicit nsGSettingsCollection(GSettings* aSettings) : mSettings(aSettings),
+ mKeys(nullptr) {}
+private:
+ ~nsGSettingsCollection();
+
+ bool KeyExists(const nsACString& aKey);
+ bool SetValue(const nsACString& aKey,
+ GVariant *aValue);
+
+ GSettings *mSettings;
+ char **mKeys;
+};
+
+nsGSettingsCollection::~nsGSettingsCollection()
+{
+ g_strfreev(mKeys);
+ g_object_unref(mSettings);
+}
+
+bool
+nsGSettingsCollection::KeyExists(const nsACString& aKey)
+{
+ if (!mKeys)
+ mKeys = g_settings_list_keys(mSettings);
+
+ for (uint32_t i = 0; mKeys[i] != nullptr; i++) {
+ if (aKey.Equals(mKeys[i]))
+ return true;
+ }
+
+ return false;
+}
+
+bool
+nsGSettingsCollection::SetValue(const nsACString& aKey,
+ GVariant *aValue)
+{
+ if (!KeyExists(aKey) ||
+ !g_settings_range_check(mSettings,
+ PromiseFlatCString(aKey).get(),
+ aValue)) {
+ g_variant_unref(aValue);
+ return false;
+ }
+
+ return g_settings_set_value(mSettings,
+ PromiseFlatCString(aKey).get(),
+ aValue);
+}
+
+NS_IMPL_ISUPPORTS(nsGSettingsCollection, nsIGSettingsCollection)
+
+NS_IMETHODIMP
+nsGSettingsCollection::SetString(const nsACString& aKey,
+ const nsACString& aValue)
+{
+ GVariant *value = g_variant_new_string(PromiseFlatCString(aValue).get());
+ if (!value)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ bool res = SetValue(aKey, value);
+
+ return res ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsGSettingsCollection::SetBoolean(const nsACString& aKey,
+ bool aValue)
+{
+ GVariant *value = g_variant_new_boolean(aValue);
+ if (!value)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ bool res = SetValue(aKey, value);
+
+ return res ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsGSettingsCollection::SetInt(const nsACString& aKey,
+ int32_t aValue)
+{
+ GVariant *value = g_variant_new_int32(aValue);
+ if (!value)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ bool res = SetValue(aKey, value);
+
+ return res ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsGSettingsCollection::GetString(const nsACString& aKey,
+ nsACString& aResult)
+{
+ if (!KeyExists(aKey))
+ return NS_ERROR_INVALID_ARG;
+
+ GVariant *value = g_settings_get_value(mSettings,
+ PromiseFlatCString(aKey).get());
+ if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) &&
+ !g_variant_is_of_type(value, G_VARIANT_TYPE_OBJECT_PATH) &&
+ !g_variant_is_of_type(value, G_VARIANT_TYPE_SIGNATURE)) {
+ g_variant_unref(value);
+ return NS_ERROR_FAILURE;
+ }
+
+ aResult.Assign(g_variant_get_string(value, nullptr));
+ g_variant_unref(value);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGSettingsCollection::GetBoolean(const nsACString& aKey,
+ bool* aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ if (!KeyExists(aKey))
+ return NS_ERROR_INVALID_ARG;
+
+ GVariant *value = g_settings_get_value(mSettings,
+ PromiseFlatCString(aKey).get());
+ if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
+ g_variant_unref(value);
+ return NS_ERROR_FAILURE;
+ }
+
+ gboolean res = g_variant_get_boolean(value);
+ *aResult = res ? true : false;
+ g_variant_unref(value);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGSettingsCollection::GetInt(const nsACString& aKey,
+ int32_t* aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ if (!KeyExists(aKey))
+ return NS_ERROR_INVALID_ARG;
+
+ GVariant *value = g_settings_get_value(mSettings,
+ PromiseFlatCString(aKey).get());
+ if (!g_variant_is_of_type(value, G_VARIANT_TYPE_INT32)) {
+ g_variant_unref(value);
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = g_variant_get_int32(value);
+ g_variant_unref(value);
+
+ return NS_OK;
+}
+
+// These types are local to nsGSettingsService::Init, but ISO C++98 doesn't
+// allow a template (ArrayLength) to be instantiated based on a local type.
+// Boo-urns!
+typedef void (*nsGSettingsFunc)();
+struct nsGSettingsDynamicFunction {
+ const char *functionName;
+ nsGSettingsFunc *function;
+};
+
+NS_IMETHODIMP
+nsGSettingsCollection::GetStringList(const nsACString& aKey, nsIArray** aResult)
+{
+ if (!KeyExists(aKey))
+ return NS_ERROR_INVALID_ARG;
+
+ nsCOMPtr<nsIMutableArray> items(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ if (!items) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ GVariant *value = g_settings_get_value(mSettings,
+ PromiseFlatCString(aKey).get());
+
+ if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING_ARRAY)) {
+ g_variant_unref(value);
+ return NS_ERROR_FAILURE;
+ }
+
+ const gchar ** gs_strings = g_variant_get_strv(value, nullptr);
+ if (!gs_strings) {
+ // empty array
+ items.forget(aResult);
+ g_variant_unref(value);
+ return NS_OK;
+ }
+
+ const gchar** p_gs_strings = gs_strings;
+ while (*p_gs_strings != nullptr)
+ {
+ nsCOMPtr<nsISupportsCString> obj(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
+ if (obj) {
+ obj->SetData(nsDependentCString(*p_gs_strings));
+ items->AppendElement(obj, false);
+ }
+ p_gs_strings++;
+ }
+ g_free(gs_strings);
+ items.forget(aResult);
+ g_variant_unref(value);
+ return NS_OK;
+}
+
+nsresult
+nsGSettingsService::Init()
+{
+#define FUNC(name, type, params) { #name, (nsGSettingsFunc *)&_##name },
+ static const nsGSettingsDynamicFunction kGSettingsSymbols[] = {
+ GSETTINGS_FUNCTIONS
+ };
+#undef FUNC
+
+ if (!gioLib) {
+ gioLib = PR_LoadLibrary("libgio-2.0.so.0");
+ if (!gioLib)
+ return NS_ERROR_FAILURE;
+ }
+
+ for (uint32_t i = 0; i < ArrayLength(kGSettingsSymbols); i++) {
+ *kGSettingsSymbols[i].function =
+ PR_FindFunctionSymbol(gioLib, kGSettingsSymbols[i].functionName);
+ if (!*kGSettingsSymbols[i].function) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsGSettingsService, nsIGSettingsService)
+
+nsGSettingsService::~nsGSettingsService()
+{
+ if (gioLib) {
+ PR_UnloadLibrary(gioLib);
+ gioLib = nullptr;
+ }
+}
+
+NS_IMETHODIMP
+nsGSettingsService::GetCollectionForSchema(const nsACString& schema,
+ nsIGSettingsCollection** collection)
+{
+ NS_ENSURE_ARG_POINTER(collection);
+
+ const char * const *schemas = g_settings_list_schemas();
+
+ for (uint32_t i = 0; schemas[i] != nullptr; i++) {
+ if (schema.Equals(schemas[i])) {
+ GSettings *settings = g_settings_new(PromiseFlatCString(schema).get());
+ nsGSettingsCollection *mozGSettings = new nsGSettingsCollection(settings);
+ NS_ADDREF(*collection = mozGSettings);
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
diff --git a/toolkit/system/gnome/nsGSettingsService.h b/toolkit/system/gnome/nsGSettingsService.h
new file mode 100644
index 000000000..6ffcdedd1
--- /dev/null
+++ b/toolkit/system/gnome/nsGSettingsService.h
@@ -0,0 +1,27 @@
+/* -*- 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 nsGSettingsService_h_
+#define nsGSettingsService_h_
+
+#include "nsIGSettingsService.h"
+
+#define NS_GSETTINGSSERVICE_CID \
+{0xbfd4a9d8, 0xd886, 0x4161, {0x81, 0xef, 0x88, 0x68, 0xda, 0x11, 0x41, 0x70}}
+
+class nsGSettingsService final : public nsIGSettingsService
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIGSETTINGSSERVICE
+
+ nsresult Init();
+
+private:
+ ~nsGSettingsService();
+};
+
+#endif
+
diff --git a/toolkit/system/gnome/nsGnomeModule.cpp b/toolkit/system/gnome/nsGnomeModule.cpp
new file mode 100644
index 000000000..6ecebcc1f
--- /dev/null
+++ b/toolkit/system/gnome/nsGnomeModule.cpp
@@ -0,0 +1,78 @@
+/* -*- 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 "nsToolkitCompsCID.h"
+#include "mozilla/ModuleUtils.h"
+
+#include <glib-object.h>
+
+#ifdef MOZ_ENABLE_GCONF
+#include "nsGConfService.h"
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGConfService, Init)
+#endif
+#ifdef MOZ_ENABLE_GIO
+#include "nsGIOService.h"
+#include "nsGSettingsService.h"
+#include "nsPackageKitService.h"
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsGIOService)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGSettingsService, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPackageKitService, Init)
+#endif
+#include "nsSystemAlertsService.h"
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSystemAlertsService, Init)
+
+#ifdef MOZ_ENABLE_GCONF
+NS_DEFINE_NAMED_CID(NS_GCONFSERVICE_CID);
+#endif
+#ifdef MOZ_ENABLE_GIO
+NS_DEFINE_NAMED_CID(NS_GIOSERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_GSETTINGSSERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_PACKAGEKITSERVICE_CID);
+#endif
+NS_DEFINE_NAMED_CID(NS_SYSTEMALERTSSERVICE_CID);
+
+static const mozilla::Module::CIDEntry kGnomeCIDs[] = {
+#ifdef MOZ_ENABLE_GCONF
+ { &kNS_GCONFSERVICE_CID, false, nullptr, nsGConfServiceConstructor },
+#endif
+#ifdef MOZ_ENABLE_GIO
+ { &kNS_GIOSERVICE_CID, false, nullptr, nsGIOServiceConstructor },
+ { &kNS_GSETTINGSSERVICE_CID, false, nullptr, nsGSettingsServiceConstructor },
+ { &kNS_PACKAGEKITSERVICE_CID, false, nullptr, nsPackageKitServiceConstructor },
+#endif
+ { &kNS_SYSTEMALERTSSERVICE_CID, false, nullptr, nsSystemAlertsServiceConstructor },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kGnomeContracts[] = {
+#ifdef MOZ_ENABLE_GCONF
+ { NS_GCONFSERVICE_CONTRACTID, &kNS_GCONFSERVICE_CID },
+#endif
+#ifdef MOZ_ENABLE_GIO
+ { NS_GIOSERVICE_CONTRACTID, &kNS_GIOSERVICE_CID },
+ { NS_GSETTINGSSERVICE_CONTRACTID, &kNS_GSETTINGSSERVICE_CID },
+ { NS_PACKAGEKITSERVICE_CONTRACTID, &kNS_PACKAGEKITSERVICE_CID },
+#endif
+ { NS_SYSTEMALERTSERVICE_CONTRACTID, &kNS_SYSTEMALERTSSERVICE_CID },
+ { nullptr }
+};
+
+static nsresult
+InitGType ()
+{
+ g_type_init();
+ return NS_OK;
+}
+
+static const mozilla::Module kGnomeModule = {
+ mozilla::Module::kVersion,
+ kGnomeCIDs,
+ kGnomeContracts,
+ nullptr,
+ nullptr,
+ InitGType
+};
+
+NSMODULE_DEFN(mozgnome) = &kGnomeModule;
diff --git a/toolkit/system/gnome/nsPackageKitService.cpp b/toolkit/system/gnome/nsPackageKitService.cpp
new file mode 100644
index 000000000..c32a91367
--- /dev/null
+++ b/toolkit/system/gnome/nsPackageKitService.cpp
@@ -0,0 +1,267 @@
+/* -*- 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 "nsArrayUtils.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsISupportsPrimitives.h"
+#include "nsPackageKitService.h"
+#include "nsString.h"
+#include "prlink.h"
+#include "mozilla/Unused.h"
+#include "mozilla/UniquePtr.h"
+
+#include <glib.h>
+#include <glib-object.h>
+#include <limits>
+
+using namespace mozilla;
+
+typedef struct _GAsyncResult GAsyncResult;
+typedef enum {
+ G_BUS_TYPE_STARTER = -1,
+ G_BUS_TYPE_NONE = 0,
+ G_BUS_TYPE_SYSTEM = 1,
+ G_BUS_TYPE_SESSION = 2
+} GBusType;
+typedef struct _GCancellable GCancellable;
+typedef enum {
+ G_DBUS_CALL_FLAGS_NONE = 0,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START = (1<<0)
+} GDBusCallFlags;
+typedef struct _GDBusInterfaceInfo GDBusInterfaceInfo;
+typedef struct _GDBusProxy GDBusProxy;
+typedef enum {
+ G_DBUS_PROXY_FLAGS_NONE = 0,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = (1<<0),
+ G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1),
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START = (1<<2),
+ G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES = (1<<3)
+} GDBusProxyFlags;
+typedef struct _GVariant GVariant;
+typedef void (*GAsyncReadyCallback) (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data);
+
+#define GDBUS_FUNCTIONS \
+ FUNC(g_dbus_proxy_call, void, (GDBusProxy *proxy, const gchar *method_name, GVariant *parameters, GDBusCallFlags flags, gint timeout_msec, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)) \
+ FUNC(g_dbus_proxy_call_finish, GVariant*, (GDBusProxy *proxy, GAsyncResult *res, GError **error)) \
+ FUNC(g_dbus_proxy_new_finish, GDBusProxy*, (GAsyncResult *res, GError **error)) \
+ FUNC(g_dbus_proxy_new_for_bus, void, (GBusType bus_type, GDBusProxyFlags flags, GDBusInterfaceInfo *info, const gchar *name, const gchar *object_path, const gchar *interface_name, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)) \
+ FUNC(g_variant_is_floating, gboolean, (GVariant *value)) \
+ FUNC(g_variant_new, GVariant*, (const gchar *format_string, ...)) \
+ FUNC(g_variant_unref, void, (GVariant* value))
+
+#define FUNC(name, type, params) \
+ typedef type (*_##name##_fn) params; \
+ static _##name##_fn _##name;
+
+GDBUS_FUNCTIONS
+
+#undef FUNC
+
+#define g_dbus_proxy_call _g_dbus_proxy_call
+#define g_dbus_proxy_call_finish _g_dbus_proxy_call_finish
+#define g_dbus_proxy_new_finish _g_dbus_proxy_new_finish
+#define g_dbus_proxy_new_for_bus _g_dbus_proxy_new_for_bus
+#define g_variant_is_floating _g_variant_is_floating
+#define g_variant_new _g_variant_new
+#define g_variant_unref _g_variant_unref
+
+static PRLibrary *gioLib = nullptr;
+
+typedef void (*nsGDBusFunc)();
+struct nsGDBusDynamicFunction {
+ const char *functionName;
+ nsGDBusFunc *function;
+};
+
+nsresult
+nsPackageKitService::Init()
+{
+#define FUNC(name, type, params) { #name, (nsGDBusFunc *)&_##name },
+ const nsGDBusDynamicFunction kGDBusSymbols[] = {
+ GDBUS_FUNCTIONS
+ };
+#undef FUNC
+
+ if (!gioLib) {
+ gioLib = PR_LoadLibrary("libgio-2.0.so.0");
+ if (!gioLib)
+ return NS_ERROR_FAILURE;
+ }
+
+ for (uint32_t i = 0; i < ArrayLength(kGDBusSymbols); i++) {
+ *kGDBusSymbols[i].function =
+ PR_FindFunctionSymbol(gioLib, kGDBusSymbols[i].functionName);
+ if (!*kGDBusSymbols[i].function) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsPackageKitService, nsIPackageKitService)
+
+nsPackageKitService::~nsPackageKitService()
+{
+ if (gioLib) {
+ PR_UnloadLibrary(gioLib);
+ gioLib = nullptr;
+ }
+}
+
+static const char* InstallPackagesMethods[] = {
+ "InstallPackageNames",
+ "InstallMimeTypes",
+ "InstallFontconfigResources",
+ "InstallGStreamerResources"
+};
+
+struct InstallPackagesProxyNewData {
+ nsCOMPtr<nsIObserver> observer;
+ uint32_t method;
+ GVariant* parameters;
+};
+
+static void
+InstallPackagesNotifyObserver(nsIObserver* aObserver,
+ gchar* aErrorMessage)
+{
+ if (aObserver) {
+ aObserver->Observe(nullptr, "packagekit-install",
+ aErrorMessage ?
+ NS_ConvertUTF8toUTF16(aErrorMessage).get() :
+ nullptr);
+ }
+}
+
+static void
+InstallPackagesProxyCallCallback(GObject *aSourceObject,
+ GAsyncResult *aResult,
+ gpointer aUserData)
+{
+ nsCOMPtr<nsIObserver> observer = static_cast<nsIObserver*>(aUserData);
+ GDBusProxy* proxy = reinterpret_cast<GDBusProxy*>(aSourceObject);
+
+ GError* error = nullptr;
+ GVariant* result = g_dbus_proxy_call_finish(proxy, aResult, &error);
+ if (result) {
+ InstallPackagesNotifyObserver(observer, nullptr);
+ g_variant_unref(result);
+ } else {
+ NS_ASSERTION(error, "g_dbus_proxy_call_finish should set error when it returns NULL");
+ InstallPackagesNotifyObserver(observer, error->message);
+ g_error_free(error);
+ }
+
+ g_object_unref(proxy);
+ Unused << observer.forget().take();
+}
+
+static void
+InstallPackagesProxyNewCallback(GObject *aSourceObject,
+ GAsyncResult *aResult,
+ gpointer aUserData)
+{
+ InstallPackagesProxyNewData* userData =
+ static_cast<InstallPackagesProxyNewData*>(aUserData);
+
+ NS_ASSERTION(g_variant_is_floating(userData->parameters),
+ "userData->parameters should be a floating reference.");
+
+ GError* error = nullptr;
+ GDBusProxy* proxy = g_dbus_proxy_new_finish(aResult, &error);
+
+ if (proxy) {
+ // Send the asynchronous request to install the packages
+ // This call will consume the floating reference userData->parameters so we
+ // don't need to release it explicitly.
+ nsIObserver* observer;
+ userData->observer.forget(&observer);
+ g_dbus_proxy_call(proxy,
+ InstallPackagesMethods[userData->method],
+ userData->parameters,
+ G_DBUS_CALL_FLAGS_NONE,
+ G_MAXINT,
+ nullptr,
+ &InstallPackagesProxyCallCallback,
+ static_cast<gpointer>(observer));
+ } else {
+ NS_ASSERTION(error, "g_dbus_proxy_new_finish should set error when it returns NULL");
+ InstallPackagesNotifyObserver(userData->observer, error->message);
+ g_error_free(error);
+ g_variant_unref(userData->parameters);
+ }
+ delete userData;
+}
+
+NS_IMETHODIMP
+nsPackageKitService::InstallPackages(uint32_t aInstallMethod,
+ nsIArray* aPackageArray,
+ nsIObserver* aObserver)
+{
+ NS_ENSURE_ARG(aPackageArray);
+
+ uint32_t arrayLength;
+ aPackageArray->GetLength(&arrayLength);
+ if (arrayLength == 0 ||
+ arrayLength == std::numeric_limits<uint32_t>::max() ||
+ aInstallMethod >= PK_INSTALL_METHOD_COUNT) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // Create the GVariant* parameter from the list of packages.
+ GVariant* parameters = nullptr;
+ auto packages = MakeUnique<gchar*[]>(arrayLength + 1);
+
+ nsresult rv = NS_OK;
+ for (uint32_t i = 0; i < arrayLength; i++) {
+ nsCOMPtr<nsISupportsString> package =
+ do_QueryElementAt(aPackageArray, i);
+ if (!package) {
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ nsString data;
+ package->GetData(data);
+ packages[i] = g_strdup(NS_ConvertUTF16toUTF8(data).get());
+ if (!packages[i]) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ }
+ }
+ packages[arrayLength] = nullptr;
+
+ if (NS_SUCCEEDED(rv)) {
+ // We create a new GVariant object to send parameters to PackageKit.
+ parameters = g_variant_new("(u^ass)", static_cast<guint32>(0),
+ packages.get(), "hide-finished");
+ if (!parameters) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ for (uint32_t i = 0; i < arrayLength; i++) {
+ g_free(packages[i]);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Send the asynchronous request to load the bus proxy
+ InstallPackagesProxyNewData* data = new InstallPackagesProxyNewData;
+ data->observer = aObserver;
+ data->method = aInstallMethod;
+ data->parameters = parameters;
+ g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ nullptr,
+ "org.freedesktop.PackageKit",
+ "/org/freedesktop/PackageKit",
+ "org.freedesktop.PackageKit.Modify",
+ nullptr,
+ &InstallPackagesProxyNewCallback,
+ static_cast<gpointer>(data));
+ return NS_OK;
+}
diff --git a/toolkit/system/gnome/nsPackageKitService.h b/toolkit/system/gnome/nsPackageKitService.h
new file mode 100644
index 000000000..b14c7c51f
--- /dev/null
+++ b/toolkit/system/gnome/nsPackageKitService.h
@@ -0,0 +1,26 @@
+/* -*- 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 nsPackageKitService_h_
+#define nsPackageKitService_h_
+
+#include "nsIPackageKitService.h"
+
+#define NS_PACKAGEKITSERVICE_CID \
+{0x9c95515e, 0x611d, 0x11e4, {0xb9, 0x7e, 0x60, 0xa4, 0x4c, 0x71, 0x70, 0x42}}
+
+class nsPackageKitService final : public nsIPackageKitService
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPACKAGEKITSERVICE
+
+ nsresult Init();
+
+private:
+ ~nsPackageKitService();
+};
+
+#endif
diff --git a/toolkit/system/gnome/nsSystemAlertsService.cpp b/toolkit/system/gnome/nsSystemAlertsService.cpp
new file mode 100644
index 000000000..2af64383c
--- /dev/null
+++ b/toolkit/system/gnome/nsSystemAlertsService.cpp
@@ -0,0 +1,118 @@
+/* -*- 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 "nsComponentManagerUtils.h"
+#include "nsXULAppAPI.h"
+#include "nsSystemAlertsService.h"
+#include "nsAlertsIconListener.h"
+#include "nsAutoPtr.h"
+
+NS_IMPL_ADDREF(nsSystemAlertsService)
+NS_IMPL_RELEASE(nsSystemAlertsService)
+
+NS_INTERFACE_MAP_BEGIN(nsSystemAlertsService)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAlertsService)
+ NS_INTERFACE_MAP_ENTRY(nsIAlertsService)
+NS_INTERFACE_MAP_END_THREADSAFE
+
+nsSystemAlertsService::nsSystemAlertsService()
+{
+}
+
+nsSystemAlertsService::~nsSystemAlertsService()
+{}
+
+nsresult
+nsSystemAlertsService::Init()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsSystemAlertsService::ShowAlertNotification(const nsAString & aImageUrl, const nsAString & aAlertTitle,
+ const nsAString & aAlertText, bool aAlertTextClickable,
+ const nsAString & aAlertCookie,
+ nsIObserver * aAlertListener,
+ const nsAString & aAlertName,
+ const nsAString & aBidi,
+ const nsAString & aLang,
+ const nsAString & aData,
+ nsIPrincipal * aPrincipal,
+ bool aInPrivateBrowsing,
+ bool aRequireInteraction)
+{
+ nsCOMPtr<nsIAlertNotification> alert =
+ do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
+ NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE);
+ nsresult rv = alert->Init(aAlertName, aImageUrl, aAlertTitle,
+ aAlertText, aAlertTextClickable,
+ aAlertCookie, aBidi, aLang, aData,
+ aPrincipal, aInPrivateBrowsing,
+ aRequireInteraction);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ShowAlert(alert, aAlertListener);
+}
+
+NS_IMETHODIMP nsSystemAlertsService::ShowPersistentNotification(const nsAString& aPersistentData,
+ nsIAlertNotification* aAlert,
+ nsIObserver* aAlertListener)
+{
+ return ShowAlert(aAlert, aAlertListener);
+}
+
+NS_IMETHODIMP nsSystemAlertsService::ShowAlert(nsIAlertNotification* aAlert,
+ nsIObserver* aAlertListener)
+{
+ NS_ENSURE_ARG(aAlert);
+
+ nsAutoString alertName;
+ nsresult rv = aAlert->GetName(alertName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<nsAlertsIconListener> alertListener =
+ new nsAlertsIconListener(this, alertName);
+ if (!alertListener)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ AddListener(alertName, alertListener);
+ return alertListener->InitAlertAsync(aAlert, aAlertListener);
+}
+
+NS_IMETHODIMP nsSystemAlertsService::CloseAlert(const nsAString& aAlertName,
+ nsIPrincipal* aPrincipal)
+{
+ RefPtr<nsAlertsIconListener> listener = mActiveListeners.Get(aAlertName);
+ if (!listener) {
+ return NS_OK;
+ }
+ mActiveListeners.Remove(aAlertName);
+ return listener->Close();
+}
+
+bool nsSystemAlertsService::IsActiveListener(const nsAString& aAlertName,
+ nsAlertsIconListener* aListener)
+{
+ return mActiveListeners.Get(aAlertName) == aListener;
+}
+
+void nsSystemAlertsService::AddListener(const nsAString& aAlertName,
+ nsAlertsIconListener* aListener)
+{
+ RefPtr<nsAlertsIconListener> oldListener = mActiveListeners.Get(aAlertName);
+ mActiveListeners.Put(aAlertName, aListener);
+ if (oldListener) {
+ // If an alert with this name already exists, close it.
+ oldListener->Close();
+ }
+}
+
+void nsSystemAlertsService::RemoveListener(const nsAString& aAlertName,
+ nsAlertsIconListener* aListener)
+{
+ if (IsActiveListener(aAlertName, aListener)) {
+ // The alert may have been replaced; only remove it from the active
+ // listeners map if it's the same.
+ mActiveListeners.Remove(aAlertName);
+ }
+}
diff --git a/toolkit/system/gnome/nsSystemAlertsService.h b/toolkit/system/gnome/nsSystemAlertsService.h
new file mode 100644
index 000000000..66ae2ea27
--- /dev/null
+++ b/toolkit/system/gnome/nsSystemAlertsService.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSystemAlertsService_h__
+#define nsSystemAlertsService_h__
+
+#include "nsIAlertsService.h"
+#include "nsDataHashtable.h"
+#include "nsCOMPtr.h"
+
+class nsAlertsIconListener;
+
+class nsSystemAlertsService : public nsIAlertsService
+{
+public:
+ NS_DECL_NSIALERTSSERVICE
+ NS_DECL_ISUPPORTS
+
+ nsSystemAlertsService();
+
+ nsresult Init();
+
+ bool IsActiveListener(const nsAString& aAlertName,
+ nsAlertsIconListener* aListener);
+ void RemoveListener(const nsAString& aAlertName,
+ nsAlertsIconListener* aListener);
+
+protected:
+ virtual ~nsSystemAlertsService();
+
+ void AddListener(const nsAString& aAlertName,
+ nsAlertsIconListener* aListener);
+
+ nsDataHashtable<nsStringHashKey, nsAlertsIconListener*> mActiveListeners;
+};
+
+#endif /* nsSystemAlertsService_h__ */
diff --git a/toolkit/system/osxproxy/ProxyUtils.h b/toolkit/system/osxproxy/ProxyUtils.h
new file mode 100644
index 000000000..d6e5ddbd4
--- /dev/null
+++ b/toolkit/system/osxproxy/ProxyUtils.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_toolkit_system_osxproxy_ProxyUtils_h
+#define mozilla_toolkit_system_osxproxy_ProxyUtils_h
+
+#include "nsStringGlue.h"
+
+namespace mozilla {
+namespace toolkit {
+namespace system {
+
+bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride);
+
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
+
+#endif // mozilla_toolkit_system_osxproxy_ProxyUtils_h
diff --git a/toolkit/system/osxproxy/ProxyUtils.mm b/toolkit/system/osxproxy/ProxyUtils.mm
new file mode 100644
index 000000000..4e59f226a
--- /dev/null
+++ b/toolkit/system/osxproxy/ProxyUtils.mm
@@ -0,0 +1,182 @@
+/* -*- 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 "ProxyUtils.h"
+#include "nsTArray.h"
+#include "prnetdb.h"
+#include "prtypes.h"
+
+namespace mozilla {
+namespace toolkit {
+namespace system {
+
+/**
+ * Normalize the short IP form into the complete form.
+ * For example, it converts "192.168" into "192.168.0.0"
+ */
+static bool
+NormalizeAddr(const nsACString& aAddr, nsCString& aNormalized)
+{
+ nsTArray<nsCString> addr;
+ if (!ParseString(aAddr, '.', addr)) {
+ return false;
+ }
+ aNormalized = "";
+ for (uint32_t i = 0; i < 4; ++i) {
+ if (i != 0) {
+ aNormalized.Append(".");
+ }
+ if (i < addr.Length()) {
+ aNormalized.Append(addr[i]);
+ } else {
+ aNormalized.Append("0");
+ }
+ }
+ return true;
+}
+
+static PRUint32
+MaskIPv4Addr(PRUint32 aAddr, uint16_t aMaskLen)
+{
+ if (aMaskLen == 32) {
+ return aAddr;
+ }
+ return PR_htonl(PR_ntohl(aAddr) & (~0L << (32 - aMaskLen)));
+}
+
+static void
+MaskIPv6Addr(PRIPv6Addr& aAddr, uint16_t aMaskLen)
+{
+ if (aMaskLen == 128) {
+ return;
+ }
+
+ if (aMaskLen > 96) {
+ aAddr.pr_s6_addr32[3] = PR_htonl(
+ PR_ntohl(aAddr.pr_s6_addr32[3]) & (~0L << (128 - aMaskLen)));
+ } else if (aMaskLen > 64) {
+ aAddr.pr_s6_addr32[3] = 0;
+ aAddr.pr_s6_addr32[2] = PR_htonl(
+ PR_ntohl(aAddr.pr_s6_addr32[2]) & (~0L << (96 - aMaskLen)));
+ } else if (aMaskLen > 32) {
+ aAddr.pr_s6_addr32[3] = 0;
+ aAddr.pr_s6_addr32[2] = 0;
+ aAddr.pr_s6_addr32[1] = PR_htonl(
+ PR_ntohl(aAddr.pr_s6_addr32[1]) & (~0L << (64 - aMaskLen)));
+ } else {
+ aAddr.pr_s6_addr32[3] = 0;
+ aAddr.pr_s6_addr32[2] = 0;
+ aAddr.pr_s6_addr32[1] = 0;
+ aAddr.pr_s6_addr32[0] = PR_htonl(
+ PR_ntohl(aAddr.pr_s6_addr32[0]) & (~0L << (32 - aMaskLen)));
+ }
+
+ return;
+}
+
+static bool
+IsMatchMask(const nsACString& aHost, const nsACString& aOverride)
+{
+ nsresult rv;
+
+ auto tokenEnd = aOverride.FindChar('/');
+ if (tokenEnd == -1) {
+ return false;
+ }
+
+ nsAutoCString prefixStr(Substring(aOverride,
+ tokenEnd + 1,
+ aOverride.Length() - tokenEnd - 1));
+ auto maskLen = prefixStr.ToInteger(&rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ nsAutoCString override(aOverride);
+ if (!NormalizeAddr(Substring(aOverride, 0, tokenEnd), override)) {
+ return false;
+ }
+
+ PRNetAddr prAddrHost;
+ PRNetAddr prAddrOverride;
+ if (PR_SUCCESS != PR_StringToNetAddr(PromiseFlatCString(aHost).get(),
+ &prAddrHost) ||
+ PR_SUCCESS != PR_StringToNetAddr(override.get(),
+ &prAddrOverride)) {
+ return false;
+ }
+
+ if (prAddrHost.raw.family == PR_AF_INET &&
+ prAddrOverride.raw.family == PR_AF_INET) {
+ return MaskIPv4Addr(prAddrHost.inet.ip, maskLen) ==
+ MaskIPv4Addr(prAddrOverride.inet.ip, maskLen);
+ }
+ else if (prAddrHost.raw.family == PR_AF_INET6 &&
+ prAddrOverride.raw.family == PR_AF_INET6) {
+ MaskIPv6Addr(prAddrHost.ipv6.ip, maskLen);
+ MaskIPv6Addr(prAddrOverride.ipv6.ip, maskLen);
+
+ return memcmp(&prAddrHost.ipv6.ip,
+ &prAddrOverride.ipv6.ip,
+ sizeof(PRIPv6Addr)) == 0;
+ }
+
+ return false;
+}
+
+static bool
+IsMatchWildcard(const nsACString& aHost, const nsACString& aOverride)
+{
+ nsAutoCString host(aHost);
+ nsAutoCString override(aOverride);
+
+ int32_t overrideLength = override.Length();
+ int32_t tokenStart = 0;
+ int32_t offset = 0;
+ bool star = false;
+
+ while (tokenStart < overrideLength) {
+ int32_t tokenEnd = override.FindChar('*', tokenStart);
+ if (tokenEnd == tokenStart) {
+ // Star is the first character in the token.
+ star = true;
+ tokenStart++;
+ // If the character following the '*' is a '.' character then skip
+ // it so that "*.foo.com" allows "foo.com".
+ if (override.FindChar('.', tokenStart) == tokenStart) {
+ nsAutoCString token(Substring(override,
+ tokenStart + 1,
+ overrideLength - tokenStart - 1));
+ if (host.Equals(token)) {
+ return true;
+ }
+ }
+ } else {
+ if (tokenEnd == -1) {
+ tokenEnd = overrideLength; // no '*' char, match rest of string
+ }
+ nsAutoCString token(Substring(override, tokenStart, tokenEnd - tokenStart));
+ offset = host.Find(token, offset);
+ if (offset == -1 || (!star && offset)) {
+ return false;
+ }
+ star = false;
+ tokenStart = tokenEnd;
+ offset += token.Length();
+ }
+ }
+
+ return (star || (offset == static_cast<int32_t>(host.Length())));
+}
+
+bool
+IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride)
+{
+ return IsMatchMask(aHost, aOverride) || IsMatchWildcard(aHost, aOverride);
+}
+
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
diff --git a/toolkit/system/osxproxy/moz.build b/toolkit/system/osxproxy/moz.build
new file mode 100644
index 000000000..e9024f8cf
--- /dev/null
+++ b/toolkit/system/osxproxy/moz.build
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+TEST_DIRS += ['tests/gtest']
+
+SOURCES += [
+ 'nsOSXSystemProxySettings.mm',
+ 'ProxyUtils.mm',
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm b/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm
new file mode 100644
index 000000000..77fd2e482
--- /dev/null
+++ b/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm
@@ -0,0 +1,326 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+#import <Cocoa/Cocoa.h>
+#import <SystemConfiguration/SystemConfiguration.h>
+
+#include "nsISystemProxySettings.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsIServiceManager.h"
+#include "nsPrintfCString.h"
+#include "nsNetCID.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIURI.h"
+#include "nsObjCExceptions.h"
+#include "mozilla/Attributes.h"
+#include "ProxyUtils.h"
+
+class nsOSXSystemProxySettings final : public nsISystemProxySettings {
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISYSTEMPROXYSETTINGS
+
+ nsOSXSystemProxySettings();
+ nsresult Init();
+
+ // called by OSX when the proxy settings have changed
+ void ProxyHasChanged();
+
+ // is there a PAC url specified in the system configuration
+ bool IsAutoconfigEnabled() const;
+ // retrieve the pac url
+ nsresult GetAutoconfigURL(nsAutoCString& aResult) const;
+
+ // Find the SystemConfiguration proxy & port for a given URI
+ nsresult FindSCProxyPort(const nsACString &aScheme, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy);
+
+ // is host:port on the proxy exception list?
+ bool IsInExceptionList(const nsACString& aHost) const;
+
+private:
+ ~nsOSXSystemProxySettings();
+
+ SCDynamicStoreContext mContext;
+ SCDynamicStoreRef mSystemDynamicStore;
+ NSDictionary* mProxyDict;
+
+
+ // Mapping of URI schemes to SystemConfiguration keys
+ struct SchemeMapping {
+ const char* mScheme;
+ CFStringRef mEnabled;
+ CFStringRef mHost;
+ CFStringRef mPort;
+ bool mIsSocksProxy;
+ };
+ static const SchemeMapping gSchemeMappingList[];
+};
+
+NS_IMPL_ISUPPORTS(nsOSXSystemProxySettings, nsISystemProxySettings)
+
+NS_IMETHODIMP
+nsOSXSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly)
+{
+ *aMainThreadOnly = true;
+ return NS_OK;
+}
+
+// Mapping of URI schemes to SystemConfiguration keys
+const nsOSXSystemProxySettings::SchemeMapping nsOSXSystemProxySettings::gSchemeMappingList[] = {
+ {"http", kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort, false},
+ {"https", kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort, false},
+ {"ftp", kSCPropNetProxiesFTPEnable, kSCPropNetProxiesFTPProxy, kSCPropNetProxiesFTPPort, false},
+ {"socks", kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort, true},
+ {NULL, NULL, NULL, NULL, false},
+};
+
+static void
+ProxyHasChangedWrapper(SCDynamicStoreRef aStore, CFArrayRef aChangedKeys, void* aInfo)
+{
+ static_cast<nsOSXSystemProxySettings*>(aInfo)->ProxyHasChanged();
+}
+
+
+nsOSXSystemProxySettings::nsOSXSystemProxySettings()
+ : mSystemDynamicStore(NULL), mProxyDict(NULL)
+{
+ mContext = (SCDynamicStoreContext){0, this, NULL, NULL, NULL};
+}
+
+nsresult
+nsOSXSystemProxySettings::Init()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ // Register for notification of proxy setting changes
+ // See: http://developer.apple.com/documentation/Networking/Conceptual/CFNetwork/CFStreamTasks/chapter_4_section_5.html
+ mSystemDynamicStore = SCDynamicStoreCreate(NULL, CFSTR("Mozilla"), ProxyHasChangedWrapper, &mContext);
+ if (!mSystemDynamicStore)
+ return NS_ERROR_FAILURE;
+
+ // Set up the store to monitor any changes to the proxies
+ CFStringRef proxiesKey = SCDynamicStoreKeyCreateProxies(NULL);
+ if (!proxiesKey)
+ return NS_ERROR_FAILURE;
+
+ CFArrayRef keyArray = CFArrayCreate(NULL, (const void**)(&proxiesKey), 1, &kCFTypeArrayCallBacks);
+ CFRelease(proxiesKey);
+ if (!keyArray)
+ return NS_ERROR_FAILURE;
+
+ SCDynamicStoreSetNotificationKeys(mSystemDynamicStore, keyArray, NULL);
+ CFRelease(keyArray);
+
+ // Add the dynamic store to the run loop
+ CFRunLoopSourceRef storeRLSource = SCDynamicStoreCreateRunLoopSource(NULL, mSystemDynamicStore, 0);
+ if (!storeRLSource)
+ return NS_ERROR_FAILURE;
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLSource, kCFRunLoopCommonModes);
+ CFRelease(storeRLSource);
+
+ // Load the initial copy of proxy info
+ mProxyDict = (NSDictionary*)SCDynamicStoreCopyProxies(mSystemDynamicStore);
+ if (!mProxyDict)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsOSXSystemProxySettings::~nsOSXSystemProxySettings()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [mProxyDict release];
+
+ if (mSystemDynamicStore) {
+ // Invalidate the dynamic store's run loop source
+ // to get the store out of the run loop
+ CFRunLoopSourceRef rls = SCDynamicStoreCreateRunLoopSource(NULL, mSystemDynamicStore, 0);
+ if (rls) {
+ CFRunLoopSourceInvalidate(rls);
+ CFRelease(rls);
+ }
+ CFRelease(mSystemDynamicStore);
+ }
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+
+void
+nsOSXSystemProxySettings::ProxyHasChanged()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [mProxyDict release];
+ mProxyDict = (NSDictionary*)SCDynamicStoreCopyProxies(mSystemDynamicStore);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+nsresult
+nsOSXSystemProxySettings::FindSCProxyPort(const nsACString &aScheme, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NS_ENSURE_TRUE(mProxyDict != NULL, NS_ERROR_FAILURE);
+
+ for (const SchemeMapping* keys = gSchemeMappingList; keys->mScheme != NULL; ++keys) {
+ // Check for matching scheme (when appropriate)
+ if (strcasecmp(keys->mScheme, PromiseFlatCString(aScheme).get()) &&
+ !keys->mIsSocksProxy)
+ continue;
+
+ // Check the proxy is enabled
+ NSNumber* enabled = [mProxyDict objectForKey:(NSString*)keys->mEnabled];
+ NS_ENSURE_TRUE(enabled == NULL || [enabled isKindOfClass:[NSNumber class]], NS_ERROR_FAILURE);
+ if ([enabled intValue] == 0)
+ continue;
+
+ // Get the proxy host
+ NSString* host = [mProxyDict objectForKey:(NSString*)keys->mHost];
+ if (host == NULL)
+ break;
+ NS_ENSURE_TRUE([host isKindOfClass:[NSString class]], NS_ERROR_FAILURE);
+ aResultHost.Assign([host UTF8String]);
+
+ // Get the proxy port
+ NSNumber* port = [mProxyDict objectForKey:(NSString*)keys->mPort];
+ NS_ENSURE_TRUE([port isKindOfClass:[NSNumber class]], NS_ERROR_FAILURE);
+ aResultPort = [port intValue];
+
+ aResultSocksProxy = keys->mIsSocksProxy;
+
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+bool
+nsOSXSystemProxySettings::IsAutoconfigEnabled() const
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ NSNumber* value = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesProxyAutoConfigEnable];
+ NS_ENSURE_TRUE(value == NULL || [value isKindOfClass:[NSNumber class]], false);
+ return ([value intValue] != 0);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
+}
+
+nsresult
+nsOSXSystemProxySettings::GetAutoconfigURL(nsAutoCString& aResult) const
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NSString* value = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesProxyAutoConfigURLString];
+ if (value != NULL) {
+ NS_ENSURE_TRUE([value isKindOfClass:[NSString class]], NS_ERROR_FAILURE);
+ aResult.Assign([value UTF8String]);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+bool
+nsOSXSystemProxySettings::IsInExceptionList(const nsACString& aHost) const
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ NS_ENSURE_TRUE(mProxyDict != NULL, false);
+
+ NSArray* exceptionList = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesExceptionsList];
+ NS_ENSURE_TRUE(exceptionList == NULL || [exceptionList isKindOfClass:[NSArray class]], false);
+
+ NSEnumerator* exceptionEnumerator = [exceptionList objectEnumerator];
+ NSString* currentValue = NULL;
+ while ((currentValue = [exceptionEnumerator nextObject])) {
+ NS_ENSURE_TRUE([currentValue isKindOfClass:[NSString class]], false);
+ nsAutoCString overrideStr([currentValue UTF8String]);
+ if (mozilla::toolkit::system::IsHostProxyEntry(aHost, overrideStr))
+ return true;
+ }
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
+}
+
+nsresult
+nsOSXSystemProxySettings::GetPACURI(nsACString& aResult)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NS_ENSURE_TRUE(mProxyDict != NULL, NS_ERROR_FAILURE);
+
+ nsAutoCString pacUrl;
+ if (IsAutoconfigEnabled() && NS_SUCCEEDED(GetAutoconfigURL(pacUrl))) {
+ aResult.Assign(pacUrl);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult
+nsOSXSystemProxySettings::GetProxyForURI(const nsACString & aSpec,
+ const nsACString & aScheme,
+ const nsACString & aHost,
+ const int32_t aPort,
+ nsACString & aResult)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ int32_t proxyPort;
+ nsAutoCString proxyHost;
+ bool proxySocks;
+ nsresult rv = FindSCProxyPort(aScheme, proxyHost, proxyPort, proxySocks);
+
+ if (NS_FAILED(rv) || IsInExceptionList(aHost)) {
+ aResult.AssignLiteral("DIRECT");
+ } else if (proxySocks) {
+ aResult.Assign(NS_LITERAL_CSTRING("SOCKS ") + proxyHost + nsPrintfCString(":%d", proxyPort));
+ } else {
+ aResult.Assign(NS_LITERAL_CSTRING("PROXY ") + proxyHost + nsPrintfCString(":%d", proxyPort));
+ }
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+#define NS_OSXSYSTEMPROXYSERVICE_CID /* 9afcd4b8-2e0f-41f4-8f1f-3bf0d3cf67de */\
+ { 0x9afcd4b8, 0x2e0f, 0x41f4, \
+ { 0x8f, 0x1f, 0x3b, 0xf0, 0xd3, 0xcf, 0x67, 0xde } }
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsOSXSystemProxySettings, Init);
+NS_DEFINE_NAMED_CID(NS_OSXSYSTEMPROXYSERVICE_CID);
+
+static const mozilla::Module::CIDEntry kOSXSysProxyCIDs[] = {
+ { &kNS_OSXSYSTEMPROXYSERVICE_CID, false, NULL, nsOSXSystemProxySettingsConstructor },
+ { NULL }
+};
+
+static const mozilla::Module::ContractIDEntry kOSXSysProxyContracts[] = {
+ { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_OSXSYSTEMPROXYSERVICE_CID },
+ { NULL }
+};
+
+static const mozilla::Module kOSXSysProxyModule = {
+ mozilla::Module::kVersion,
+ kOSXSysProxyCIDs,
+ kOSXSysProxyContracts
+};
+
+NSMODULE_DEFN(nsOSXProxyModule) = &kOSXSysProxyModule;
diff --git a/toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp b/toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp
new file mode 100644
index 000000000..6d8000d71
--- /dev/null
+++ b/toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "gtest/gtest.h"
+#include "ProxyUtils.h"
+
+using namespace mozilla::toolkit::system;
+
+TEST(OSXProxy, TestProxyBypassRules)
+{
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"),NS_LITERAL_CSTRING("*mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("notmozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.com"), NS_LITERAL_CSTRING("*.mozilla.*")));
+}
+
+TEST(OSXProxy, TestProxyBypassRulesIPv4)
+{
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.1.*")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.2.*")));
+
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("10.1.2.3"), NS_LITERAL_CSTRING("10.0.0.0/8")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168/16")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168/17")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168.128/17")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.1.1/32")));
+}
+
+TEST(OSXProxy, TestProxyBypassRulesIPv6)
+{
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/64")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0000:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/80")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/80")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0000:0000:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/96")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/96")));
+}
diff --git a/toolkit/system/osxproxy/tests/gtest/moz.build b/toolkit/system/osxproxy/tests/gtest/moz.build
new file mode 100644
index 000000000..c36b3e9bc
--- /dev/null
+++ b/toolkit/system/osxproxy/tests/gtest/moz.build
@@ -0,0 +1,18 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES += [
+ 'TestProxyBypassRules.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '/toolkit/system/osxproxy',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wshadow']
diff --git a/toolkit/system/unixproxy/moz.build b/toolkit/system/unixproxy/moz.build
new file mode 100644
index 000000000..e3be2532e
--- /dev/null
+++ b/toolkit/system/unixproxy/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+if CONFIG['MOZ_ENABLE_LIBPROXY']:
+ CXXFLAGS += CONFIG['MOZ_LIBPROXY_CFLAGS']
+ SOURCES += [
+ 'nsLibProxySettings.cpp',
+ ]
+else:
+ SOURCES += [
+ 'nsUnixSystemProxySettings.cpp',
+ ]
+
+FINAL_LIBRARY = 'xul'
diff --git a/toolkit/system/unixproxy/nsLibProxySettings.cpp b/toolkit/system/unixproxy/nsLibProxySettings.cpp
new file mode 100644
index 000000000..e9179c114
--- /dev/null
+++ b/toolkit/system/unixproxy/nsLibProxySettings.cpp
@@ -0,0 +1,141 @@
+/* -*- 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 "nsISystemProxySettings.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsIServiceManager.h"
+#include "nsIURI.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nspr.h"
+
+extern "C" {
+#include <proxy.h>
+}
+
+class nsUnixSystemProxySettings : public nsISystemProxySettings {
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISYSTEMPROXYSETTINGS
+
+ nsUnixSystemProxySettings() { mProxyFactory = nullptr; }
+ nsresult Init();
+
+private:
+ ~nsUnixSystemProxySettings() {
+ if (mProxyFactory)
+ px_proxy_factory_free(mProxyFactory);
+ }
+
+ pxProxyFactory *mProxyFactory;
+};
+
+NS_IMPL_ISUPPORTS(nsUnixSystemProxySettings, nsISystemProxySettings)
+
+NS_IMETHODIMP
+nsUnixSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly)
+{
+ *aMainThreadOnly = false;
+ return NS_OK;
+}
+
+nsresult
+nsUnixSystemProxySettings::Init()
+{
+ return NS_OK;
+}
+
+nsresult
+nsUnixSystemProxySettings::GetPACURI(nsACString& aResult)
+{
+ // Make sure we return an empty result.
+ aResult.Truncate();
+ return NS_OK;
+}
+
+nsresult
+nsUnixSystemProxySettings::GetProxyForURI(const nsACString & aSpec,
+ const nsACString & aScheme,
+ const nsACString & aHost,
+ const int32_t aPort,
+ nsACString & aResult)
+{
+ nsresult rv;
+
+ if (!mProxyFactory) {
+ mProxyFactory = px_proxy_factory_new();
+ }
+ NS_ENSURE_TRUE(mProxyFactory, NS_ERROR_NOT_AVAILABLE);
+
+ char **proxyArray = nullptr;
+ proxyArray = px_proxy_factory_get_proxies(mProxyFactory,
+ PromiseFlatCString(aSpec).get());
+ NS_ENSURE_TRUE(proxyArray, NS_ERROR_NOT_AVAILABLE);
+
+ // Translate libproxy's output to PAC string as expected
+ // libproxy returns an array of proxies in the format:
+ // <procotol>://[username:password@]proxy:port
+ // or
+ // direct://
+ //
+ // PAC format: "PROXY proxy1.foo.com:8080; PROXY proxy2.foo.com:8080; DIRECT"
+ // but nsISystemProxySettings allows "PROXY http://proxy.foo.com:8080" as well.
+
+ int c = 0;
+ while (proxyArray[c] != nullptr) {
+ if (!aResult.IsEmpty()) {
+ aResult.AppendLiteral("; ");
+ }
+
+ // figure out the scheme, and we can't use nsIIOService::NewURI because
+ // this is not the main thread.
+ char *colon = strchr (proxyArray[c], ':');
+ uint32_t schemelen = colon ? colon - proxyArray[c] : 0;
+ if (schemelen < 1) {
+ c++;
+ continue;
+ }
+
+ if (schemelen == 6 && !strncasecmp(proxyArray[c], "direct", 6)) {
+ aResult.AppendLiteral("DIRECT");
+ }
+ else {
+ aResult.AppendLiteral("PROXY ");
+ aResult.Append(proxyArray[c]);
+ }
+
+ c++;
+ }
+
+ PR_Free(proxyArray);
+ return NS_OK;
+}
+
+#define NS_UNIXSYSTEMPROXYSERVICE_CID /* 0fa3158c-d5a7-43de-9181-a285e74cf1d4 */\
+ { 0x0fa3158c, 0xd5a7, 0x43de, \
+ {0x91, 0x81, 0xa2, 0x85, 0xe7, 0x4c, 0xf1, 0xd4 } }
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsUnixSystemProxySettings, Init)
+NS_DEFINE_NAMED_CID(NS_UNIXSYSTEMPROXYSERVICE_CID);
+
+static const mozilla::Module::CIDEntry kUnixProxyCIDs[] = {
+ { &kNS_UNIXSYSTEMPROXYSERVICE_CID, false, nullptr, nsUnixSystemProxySettingsConstructor },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kUnixProxyContracts[] = {
+ { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_UNIXSYSTEMPROXYSERVICE_CID },
+ { nullptr }
+};
+
+static const mozilla::Module kUnixProxyModule = {
+ mozilla::Module::kVersion,
+ kUnixProxyCIDs,
+ kUnixProxyContracts
+};
+
+NSMODULE_DEFN(nsUnixProxyModule) = &kUnixProxyModule;
+
diff --git a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
new file mode 100644
index 000000000..c190da91d
--- /dev/null
+++ b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
@@ -0,0 +1,543 @@
+/* -*- 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 "nsISystemProxySettings.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsIServiceManager.h"
+#include "nsIGConfService.h"
+#include "nsIURI.h"
+#include "nsReadableUtils.h"
+#include "nsArrayUtils.h"
+#include "prnetdb.h"
+#include "prenv.h"
+#include "nsPrintfCString.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIGSettingsService.h"
+#include "nsInterfaceHashtable.h"
+#include "mozilla/Attributes.h"
+#include "nsIURI.h"
+
+class nsUnixSystemProxySettings final : public nsISystemProxySettings {
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISYSTEMPROXYSETTINGS
+
+ nsUnixSystemProxySettings()
+ : mSchemeProxySettings(4)
+ {
+ }
+ nsresult Init();
+
+private:
+ ~nsUnixSystemProxySettings() {}
+
+ nsCOMPtr<nsIGConfService> mGConf;
+ nsCOMPtr<nsIGSettingsService> mGSettings;
+ nsCOMPtr<nsIGSettingsCollection> mProxySettings;
+ nsInterfaceHashtable<nsCStringHashKey, nsIGSettingsCollection> mSchemeProxySettings;
+ bool IsProxyMode(const char* aMode);
+ nsresult SetProxyResultFromGConf(const char* aKeyBase, const char* aType, nsACString& aResult);
+ nsresult GetProxyFromGConf(const nsACString& aScheme, const nsACString& aHost, int32_t aPort, nsACString& aResult);
+ nsresult GetProxyFromGSettings(const nsACString& aScheme, const nsACString& aHost, int32_t aPort, nsACString& aResult);
+ nsresult SetProxyResultFromGSettings(const char* aKeyBase, const char* aType, 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
+ *aMainThreadOnly = true;
+ return NS_OK;
+}
+
+nsresult
+nsUnixSystemProxySettings::Init()
+{
+ mGSettings = do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
+ if (mGSettings) {
+ mGSettings->GetCollectionForSchema(NS_LITERAL_CSTRING("org.gnome.system.proxy"),
+ getter_AddRefs(mProxySettings));
+ }
+ if (!mProxySettings) {
+ mGConf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
+ }
+
+ return NS_OK;
+}
+
+bool
+nsUnixSystemProxySettings::IsProxyMode(const char* aMode)
+{
+ nsAutoCString mode;
+ return NS_SUCCEEDED(mGConf->GetString(NS_LITERAL_CSTRING("/system/proxy/mode"), mode)) &&
+ mode.EqualsASCII(aMode);
+}
+
+nsresult
+nsUnixSystemProxySettings::GetPACURI(nsACString& aResult)
+{
+ if (mProxySettings) {
+ nsCString proxyMode;
+ // Check if mode is auto
+ nsresult rv = mProxySettings->GetString(NS_LITERAL_CSTRING("mode"), proxyMode);
+ if (rv == NS_OK && proxyMode.EqualsLiteral("auto")) {
+ return mProxySettings->GetString(NS_LITERAL_CSTRING("autoconfig-url"), aResult);
+ }
+ /* The org.gnome.system.proxy schema has been found, but auto mode is not set.
+ * Don't try the GConf and return empty string. */
+ aResult.Truncate();
+ return NS_OK;
+ }
+
+ if (mGConf && IsProxyMode("auto")) {
+ return mGConf->GetString(NS_LITERAL_CSTRING("/system/proxy/autoconfig_url"),
+ aResult);
+ }
+ // Return an empty string when auto mode is not set.
+ aResult.Truncate();
+ return NS_OK;
+}
+
+static bool
+IsInNoProxyList(const nsACString& aHost, int32_t aPort, const char* noProxyVal)
+{
+ NS_ASSERTION(aPort >= 0, "Negative port?");
+
+ nsAutoCString noProxy(noProxyVal);
+ if (noProxy.EqualsLiteral("*"))
+ return true;
+
+ noProxy.StripWhitespace();
+
+ nsReadingIterator<char> pos;
+ nsReadingIterator<char> end;
+ noProxy.BeginReading(pos);
+ noProxy.EndReading(end);
+ while (pos != end) {
+ nsReadingIterator<char> last = pos;
+ nsReadingIterator<char> nextPos;
+ if (FindCharInReadable(',', last, end)) {
+ nextPos = last;
+ ++nextPos;
+ } else {
+ last = end;
+ nextPos = end;
+ }
+
+ nsReadingIterator<char> colon = pos;
+ int32_t port = -1;
+ if (FindCharInReadable(':', colon, last)) {
+ ++colon;
+ nsDependentCSubstring portStr(colon, last);
+ nsAutoCString portStr2(portStr); // We need this for ToInteger. String API's suck.
+ nsresult err;
+ port = portStr2.ToInteger(&err);
+ if (NS_FAILED(err)) {
+ port = -2; // don't match any port, so we ignore this pattern
+ }
+ --colon;
+ } else {
+ colon = last;
+ }
+
+ if (port == -1 || port == aPort) {
+ nsDependentCSubstring hostStr(pos, colon);
+ // By using StringEndsWith instead of an equality comparator, we can include sub-domains
+ if (StringEndsWith(aHost, hostStr, nsCaseInsensitiveCStringComparator()))
+ return true;
+ }
+
+ pos = nextPos;
+ }
+
+ return false;
+}
+
+static void SetProxyResult(const char* aType, const nsACString& aHost,
+ int32_t aPort, nsACString& aResult)
+{
+ aResult.AppendASCII(aType);
+ aResult.Append(' ');
+ aResult.Append(aHost);
+ if (aPort > 0) {
+ aResult.Append(':');
+ aResult.Append(nsPrintfCString("%d", aPort));
+ }
+}
+
+static nsresult
+GetProxyFromEnvironment(const nsACString& aScheme,
+ const nsACString& aHost,
+ int32_t aPort,
+ nsACString& aResult)
+{
+ nsAutoCString envVar;
+ envVar.Append(aScheme);
+ envVar.AppendLiteral("_proxy");
+ const char* proxyVal = PR_GetEnv(envVar.get());
+ if (!proxyVal) {
+ proxyVal = PR_GetEnv("all_proxy");
+ if (!proxyVal) {
+ // Return failure so that the caller can detect the failure and
+ // fall back to other proxy detection (e.g., WPAD)
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ const char* noProxyVal = PR_GetEnv("no_proxy");
+ if (noProxyVal && IsInNoProxyList(aHost, aPort, noProxyVal)) {
+ aResult.AppendLiteral("DIRECT");
+ return NS_OK;
+ }
+
+ // Use our URI parser to crack the proxy URI
+ nsCOMPtr<nsIURI> proxyURI;
+ nsresult rv = NS_NewURI(getter_AddRefs(proxyURI), proxyVal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Is there a way to specify "socks://" or something in these environment
+ // variables? I can't find any documentation.
+ bool isHTTP;
+ rv = proxyURI->SchemeIs("http", &isHTTP);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!isHTTP)
+ return NS_ERROR_UNKNOWN_PROTOCOL;
+
+ nsAutoCString proxyHost;
+ rv = proxyURI->GetHost(proxyHost);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t proxyPort;
+ rv = proxyURI->GetPort(&proxyPort);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ SetProxyResult("PROXY", proxyHost, proxyPort, aResult);
+ return NS_OK;
+}
+
+nsresult
+nsUnixSystemProxySettings::SetProxyResultFromGConf(const char* aKeyBase, const char* aType,
+ nsACString& aResult)
+{
+ nsAutoCString hostKey;
+ hostKey.AppendASCII(aKeyBase);
+ hostKey.AppendLiteral("host");
+ nsAutoCString host;
+ nsresult rv = mGConf->GetString(hostKey, host);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (host.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ nsAutoCString portKey;
+ portKey.AppendASCII(aKeyBase);
+ portKey.AppendLiteral("port");
+ int32_t port;
+ rv = mGConf->GetInt(portKey, &port);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ /* When port is 0, proxy is not considered as enabled even if host is set. */
+ if (port == 0)
+ return NS_ERROR_FAILURE;
+
+ SetProxyResult(aType, host, port, aResult);
+ return NS_OK;
+}
+
+nsresult
+nsUnixSystemProxySettings::SetProxyResultFromGSettings(const char* aKeyBase, const char* aType,
+ nsACString& aResult)
+{
+ nsDependentCString key(aKeyBase);
+
+ nsCOMPtr<nsIGSettingsCollection> proxy_settings = mSchemeProxySettings.Get(key);
+ nsresult rv;
+ if (!proxy_settings) {
+ rv = mGSettings->GetCollectionForSchema(key, getter_AddRefs(proxy_settings));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mSchemeProxySettings.Put(key, proxy_settings);
+ }
+
+ nsAutoCString host;
+ rv = proxy_settings->GetString(NS_LITERAL_CSTRING("host"), host);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (host.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ int32_t port;
+ rv = proxy_settings->GetInt(NS_LITERAL_CSTRING("port"), &port);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ /* When port is 0, proxy is not considered as enabled even if host is set. */
+ if (port == 0)
+ return NS_ERROR_FAILURE;
+
+ SetProxyResult(aType, host, port, aResult);
+ return NS_OK;
+}
+
+/* copied from nsProtocolProxyService.cpp --- we should share this! */
+static void
+proxy_MaskIPv6Addr(PRIPv6Addr &addr, uint16_t mask_len)
+{
+ if (mask_len == 128)
+ return;
+
+ if (mask_len > 96) {
+ addr.pr_s6_addr32[3] = PR_htonl(
+ PR_ntohl(addr.pr_s6_addr32[3]) & (~0L << (128 - mask_len)));
+ }
+ else if (mask_len > 64) {
+ addr.pr_s6_addr32[3] = 0;
+ addr.pr_s6_addr32[2] = PR_htonl(
+ PR_ntohl(addr.pr_s6_addr32[2]) & (~0L << (96 - mask_len)));
+ }
+ else if (mask_len > 32) {
+ addr.pr_s6_addr32[3] = 0;
+ addr.pr_s6_addr32[2] = 0;
+ addr.pr_s6_addr32[1] = PR_htonl(
+ PR_ntohl(addr.pr_s6_addr32[1]) & (~0L << (64 - mask_len)));
+ }
+ else {
+ addr.pr_s6_addr32[3] = 0;
+ addr.pr_s6_addr32[2] = 0;
+ addr.pr_s6_addr32[1] = 0;
+ addr.pr_s6_addr32[0] = PR_htonl(
+ PR_ntohl(addr.pr_s6_addr32[0]) & (~0L << (32 - mask_len)));
+ }
+}
+
+static bool ConvertToIPV6Addr(const nsACString& aName,
+ PRIPv6Addr* aAddr, int32_t* aMask)
+{
+ PRNetAddr addr;
+ // try to convert hostname to IP
+ if (PR_StringToNetAddr(PromiseFlatCString(aName).get(), &addr) != PR_SUCCESS)
+ return false;
+
+ // convert parsed address to IPv6
+ if (addr.raw.family == PR_AF_INET) {
+ // convert to IPv4-mapped address
+ PR_ConvertIPv4AddrToIPv6(addr.inet.ip, aAddr);
+ if (aMask) {
+ if (*aMask <= 32)
+ *aMask += 96;
+ else
+ return false;
+ }
+ } else if (addr.raw.family == PR_AF_INET6) {
+ // copy the address
+ memcpy(aAddr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static bool HostIgnoredByProxy(const nsACString& aIgnore,
+ const nsACString& aHost)
+{
+ if (aIgnore.Equals(aHost, nsCaseInsensitiveCStringComparator()))
+ return true;
+
+ if (aIgnore.First() == '*' &&
+ StringEndsWith(aHost, nsDependentCSubstring(aIgnore, 1),
+ nsCaseInsensitiveCStringComparator()))
+ return true;
+
+ int32_t mask = 128;
+ nsReadingIterator<char> start;
+ nsReadingIterator<char> slash;
+ nsReadingIterator<char> end;
+ aIgnore.BeginReading(start);
+ aIgnore.BeginReading(slash);
+ aIgnore.EndReading(end);
+ if (FindCharInReadable('/', slash, end)) {
+ ++slash;
+ nsDependentCSubstring maskStr(slash, end);
+ nsAutoCString maskStr2(maskStr);
+ nsresult err;
+ mask = maskStr2.ToInteger(&err);
+ if (NS_FAILED(err)) {
+ mask = 128;
+ }
+ --slash;
+ } else {
+ slash = end;
+ }
+
+ nsDependentCSubstring ignoreStripped(start, slash);
+ PRIPv6Addr ignoreAddr, hostAddr;
+ if (!ConvertToIPV6Addr(ignoreStripped, &ignoreAddr, &mask) ||
+ !ConvertToIPV6Addr(aHost, &hostAddr, nullptr))
+ return false;
+
+ proxy_MaskIPv6Addr(ignoreAddr, mask);
+ proxy_MaskIPv6Addr(hostAddr, mask);
+
+ return memcmp(&ignoreAddr, &hostAddr, sizeof(PRIPv6Addr)) == 0;
+}
+
+nsresult
+nsUnixSystemProxySettings::GetProxyFromGConf(const nsACString& aScheme,
+ const nsACString& aHost,
+ int32_t aPort,
+ nsACString& aResult)
+{
+ bool masterProxySwitch = false;
+ mGConf->GetBool(NS_LITERAL_CSTRING("/system/http_proxy/use_http_proxy"), &masterProxySwitch);
+ // if no proxy is set in GConf return NS_ERROR_FAILURE
+ if (!(IsProxyMode("manual") || masterProxySwitch)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIArray> ignoreList;
+ if (NS_SUCCEEDED(mGConf->GetStringList(NS_LITERAL_CSTRING("/system/http_proxy/ignore_hosts"),
+ getter_AddRefs(ignoreList))) && ignoreList) {
+ uint32_t len = 0;
+ ignoreList->GetLength(&len);
+ for (uint32_t i = 0; i < len; ++i) {
+ nsCOMPtr<nsISupportsString> str = do_QueryElementAt(ignoreList, i);
+ if (str) {
+ nsAutoString s;
+ if (NS_SUCCEEDED(str->GetData(s)) && !s.IsEmpty()) {
+ if (HostIgnoredByProxy(NS_ConvertUTF16toUTF8(s), aHost)) {
+ aResult.AppendLiteral("DIRECT");
+ return NS_OK;
+ }
+ }
+ }
+ }
+ }
+
+ bool useHttpProxyForAll = false;
+ // This setting sometimes doesn't exist, don't bail on failure
+ mGConf->GetBool(NS_LITERAL_CSTRING("/system/http_proxy/use_same_proxy"), &useHttpProxyForAll);
+
+ nsresult rv;
+ if (!useHttpProxyForAll) {
+ rv = SetProxyResultFromGConf("/system/proxy/socks_", "SOCKS", aResult);
+ if (NS_SUCCEEDED(rv))
+ return rv;
+ }
+
+ if (aScheme.LowerCaseEqualsLiteral("http") || useHttpProxyForAll) {
+ rv = SetProxyResultFromGConf("/system/http_proxy/", "PROXY", aResult);
+ } else if (aScheme.LowerCaseEqualsLiteral("https")) {
+ rv = SetProxyResultFromGConf("/system/proxy/secure_", "PROXY", aResult);
+ } else if (aScheme.LowerCaseEqualsLiteral("ftp")) {
+ rv = SetProxyResultFromGConf("/system/proxy/ftp_", "PROXY", aResult);
+ } else {
+ rv = NS_ERROR_FAILURE;
+ }
+
+ return rv;
+}
+
+nsresult
+nsUnixSystemProxySettings::GetProxyFromGSettings(const nsACString& aScheme,
+ const nsACString& aHost,
+ int32_t aPort,
+ nsACString& aResult)
+{
+ nsCString proxyMode;
+ nsresult rv = mProxySettings->GetString(NS_LITERAL_CSTRING("mode"), proxyMode);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // return NS_ERROR_FAILURE when no proxy is set
+ if (!proxyMode.EqualsLiteral("manual")) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIArray> ignoreList;
+ if (NS_SUCCEEDED(mProxySettings->GetStringList(NS_LITERAL_CSTRING("ignore-hosts"),
+ getter_AddRefs(ignoreList))) && ignoreList) {
+ uint32_t len = 0;
+ ignoreList->GetLength(&len);
+ for (uint32_t i = 0; i < len; ++i) {
+ nsCOMPtr<nsISupportsCString> str = do_QueryElementAt(ignoreList, i);
+ if (str) {
+ nsCString s;
+ if (NS_SUCCEEDED(str->GetData(s)) && !s.IsEmpty()) {
+ if (HostIgnoredByProxy(s, aHost)) {
+ aResult.AppendLiteral("DIRECT");
+ return NS_OK;
+ }
+ }
+ }
+ }
+ }
+
+ if (aScheme.LowerCaseEqualsLiteral("http")) {
+ rv = SetProxyResultFromGSettings("org.gnome.system.proxy.http", "PROXY", aResult);
+ } else if (aScheme.LowerCaseEqualsLiteral("https")) {
+ rv = SetProxyResultFromGSettings("org.gnome.system.proxy.https", "PROXY", aResult);
+ /* Try to use HTTP proxy when HTTPS proxy is not explicitly defined */
+ if (rv != NS_OK)
+ rv = SetProxyResultFromGSettings("org.gnome.system.proxy.http", "PROXY", aResult);
+ } else if (aScheme.LowerCaseEqualsLiteral("ftp")) {
+ rv = SetProxyResultFromGSettings("org.gnome.system.proxy.ftp", "PROXY", aResult);
+ } else {
+ rv = NS_ERROR_FAILURE;
+ }
+ if (rv != NS_OK) {
+ /* If proxy for scheme is not specified, use SOCKS proxy for all schemes */
+ rv = SetProxyResultFromGSettings("org.gnome.system.proxy.socks", "SOCKS", aResult);
+ }
+
+ if (NS_FAILED(rv)) {
+ aResult.AppendLiteral("DIRECT");
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsUnixSystemProxySettings::GetProxyForURI(const nsACString & aSpec,
+ const nsACString & aScheme,
+ const nsACString & aHost,
+ const int32_t aPort,
+ nsACString & aResult)
+{
+ if (mProxySettings) {
+ nsresult rv = GetProxyFromGSettings(aScheme, aHost, aPort, aResult);
+ if (NS_SUCCEEDED(rv))
+ return rv;
+ }
+ if (mGConf)
+ return GetProxyFromGConf(aScheme, aHost, aPort, aResult);
+
+ return GetProxyFromEnvironment(aScheme, aHost, aPort, aResult);
+}
+
+#define NS_UNIXSYSTEMPROXYSERVICE_CID /* 0fa3158c-d5a7-43de-9181-a285e74cf1d4 */\
+ { 0x0fa3158c, 0xd5a7, 0x43de, \
+ {0x91, 0x81, 0xa2, 0x85, 0xe7, 0x4c, 0xf1, 0xd4 } }
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsUnixSystemProxySettings, Init)
+NS_DEFINE_NAMED_CID(NS_UNIXSYSTEMPROXYSERVICE_CID);
+
+static const mozilla::Module::CIDEntry kUnixProxyCIDs[] = {
+ { &kNS_UNIXSYSTEMPROXYSERVICE_CID, false, nullptr, nsUnixSystemProxySettingsConstructor },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kUnixProxyContracts[] = {
+ { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_UNIXSYSTEMPROXYSERVICE_CID },
+ { nullptr }
+};
+
+static const mozilla::Module kUnixProxyModule = {
+ mozilla::Module::kVersion,
+ kUnixProxyCIDs,
+ kUnixProxyContracts
+};
+
+NSMODULE_DEFN(nsUnixProxyModule) = &kUnixProxyModule;
diff --git a/toolkit/system/windowsproxy/ProxyUtils.cpp b/toolkit/system/windowsproxy/ProxyUtils.cpp
new file mode 100644
index 000000000..4e59f226a
--- /dev/null
+++ b/toolkit/system/windowsproxy/ProxyUtils.cpp
@@ -0,0 +1,182 @@
+/* -*- 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 "ProxyUtils.h"
+#include "nsTArray.h"
+#include "prnetdb.h"
+#include "prtypes.h"
+
+namespace mozilla {
+namespace toolkit {
+namespace system {
+
+/**
+ * Normalize the short IP form into the complete form.
+ * For example, it converts "192.168" into "192.168.0.0"
+ */
+static bool
+NormalizeAddr(const nsACString& aAddr, nsCString& aNormalized)
+{
+ nsTArray<nsCString> addr;
+ if (!ParseString(aAddr, '.', addr)) {
+ return false;
+ }
+ aNormalized = "";
+ for (uint32_t i = 0; i < 4; ++i) {
+ if (i != 0) {
+ aNormalized.Append(".");
+ }
+ if (i < addr.Length()) {
+ aNormalized.Append(addr[i]);
+ } else {
+ aNormalized.Append("0");
+ }
+ }
+ return true;
+}
+
+static PRUint32
+MaskIPv4Addr(PRUint32 aAddr, uint16_t aMaskLen)
+{
+ if (aMaskLen == 32) {
+ return aAddr;
+ }
+ return PR_htonl(PR_ntohl(aAddr) & (~0L << (32 - aMaskLen)));
+}
+
+static void
+MaskIPv6Addr(PRIPv6Addr& aAddr, uint16_t aMaskLen)
+{
+ if (aMaskLen == 128) {
+ return;
+ }
+
+ if (aMaskLen > 96) {
+ aAddr.pr_s6_addr32[3] = PR_htonl(
+ PR_ntohl(aAddr.pr_s6_addr32[3]) & (~0L << (128 - aMaskLen)));
+ } else if (aMaskLen > 64) {
+ aAddr.pr_s6_addr32[3] = 0;
+ aAddr.pr_s6_addr32[2] = PR_htonl(
+ PR_ntohl(aAddr.pr_s6_addr32[2]) & (~0L << (96 - aMaskLen)));
+ } else if (aMaskLen > 32) {
+ aAddr.pr_s6_addr32[3] = 0;
+ aAddr.pr_s6_addr32[2] = 0;
+ aAddr.pr_s6_addr32[1] = PR_htonl(
+ PR_ntohl(aAddr.pr_s6_addr32[1]) & (~0L << (64 - aMaskLen)));
+ } else {
+ aAddr.pr_s6_addr32[3] = 0;
+ aAddr.pr_s6_addr32[2] = 0;
+ aAddr.pr_s6_addr32[1] = 0;
+ aAddr.pr_s6_addr32[0] = PR_htonl(
+ PR_ntohl(aAddr.pr_s6_addr32[0]) & (~0L << (32 - aMaskLen)));
+ }
+
+ return;
+}
+
+static bool
+IsMatchMask(const nsACString& aHost, const nsACString& aOverride)
+{
+ nsresult rv;
+
+ auto tokenEnd = aOverride.FindChar('/');
+ if (tokenEnd == -1) {
+ return false;
+ }
+
+ nsAutoCString prefixStr(Substring(aOverride,
+ tokenEnd + 1,
+ aOverride.Length() - tokenEnd - 1));
+ auto maskLen = prefixStr.ToInteger(&rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ nsAutoCString override(aOverride);
+ if (!NormalizeAddr(Substring(aOverride, 0, tokenEnd), override)) {
+ return false;
+ }
+
+ PRNetAddr prAddrHost;
+ PRNetAddr prAddrOverride;
+ if (PR_SUCCESS != PR_StringToNetAddr(PromiseFlatCString(aHost).get(),
+ &prAddrHost) ||
+ PR_SUCCESS != PR_StringToNetAddr(override.get(),
+ &prAddrOverride)) {
+ return false;
+ }
+
+ if (prAddrHost.raw.family == PR_AF_INET &&
+ prAddrOverride.raw.family == PR_AF_INET) {
+ return MaskIPv4Addr(prAddrHost.inet.ip, maskLen) ==
+ MaskIPv4Addr(prAddrOverride.inet.ip, maskLen);
+ }
+ else if (prAddrHost.raw.family == PR_AF_INET6 &&
+ prAddrOverride.raw.family == PR_AF_INET6) {
+ MaskIPv6Addr(prAddrHost.ipv6.ip, maskLen);
+ MaskIPv6Addr(prAddrOverride.ipv6.ip, maskLen);
+
+ return memcmp(&prAddrHost.ipv6.ip,
+ &prAddrOverride.ipv6.ip,
+ sizeof(PRIPv6Addr)) == 0;
+ }
+
+ return false;
+}
+
+static bool
+IsMatchWildcard(const nsACString& aHost, const nsACString& aOverride)
+{
+ nsAutoCString host(aHost);
+ nsAutoCString override(aOverride);
+
+ int32_t overrideLength = override.Length();
+ int32_t tokenStart = 0;
+ int32_t offset = 0;
+ bool star = false;
+
+ while (tokenStart < overrideLength) {
+ int32_t tokenEnd = override.FindChar('*', tokenStart);
+ if (tokenEnd == tokenStart) {
+ // Star is the first character in the token.
+ star = true;
+ tokenStart++;
+ // If the character following the '*' is a '.' character then skip
+ // it so that "*.foo.com" allows "foo.com".
+ if (override.FindChar('.', tokenStart) == tokenStart) {
+ nsAutoCString token(Substring(override,
+ tokenStart + 1,
+ overrideLength - tokenStart - 1));
+ if (host.Equals(token)) {
+ return true;
+ }
+ }
+ } else {
+ if (tokenEnd == -1) {
+ tokenEnd = overrideLength; // no '*' char, match rest of string
+ }
+ nsAutoCString token(Substring(override, tokenStart, tokenEnd - tokenStart));
+ offset = host.Find(token, offset);
+ if (offset == -1 || (!star && offset)) {
+ return false;
+ }
+ star = false;
+ tokenStart = tokenEnd;
+ offset += token.Length();
+ }
+ }
+
+ return (star || (offset == static_cast<int32_t>(host.Length())));
+}
+
+bool
+IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride)
+{
+ return IsMatchMask(aHost, aOverride) || IsMatchWildcard(aHost, aOverride);
+}
+
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
diff --git a/toolkit/system/windowsproxy/ProxyUtils.h b/toolkit/system/windowsproxy/ProxyUtils.h
new file mode 100644
index 000000000..7d6ae220f
--- /dev/null
+++ b/toolkit/system/windowsproxy/ProxyUtils.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_toolkit_system_windowsproxy_ProxyUtils_h
+#define mozilla_toolkit_system_windowsproxy_ProxyUtils_h
+
+#include "nsStringGlue.h"
+
+namespace mozilla {
+namespace toolkit {
+namespace system {
+
+bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride);
+
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
+
+#endif // mozilla_toolkit_system_windowsproxy_ProxyUtils_h
diff --git a/toolkit/system/windowsproxy/moz.build b/toolkit/system/windowsproxy/moz.build
new file mode 100644
index 000000000..6a1dcc73c
--- /dev/null
+++ b/toolkit/system/windowsproxy/moz.build
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+TEST_DIRS += ['tests/gtest']
+
+SOURCES += [
+ 'nsWindowsSystemProxySettings.cpp',
+ 'ProxyUtils.cpp'
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp
new file mode 100644
index 000000000..bb5f72b69
--- /dev/null
+++ b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp
@@ -0,0 +1,304 @@
+/* -*- 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 <windows.h>
+#include <ras.h>
+#include <wininet.h>
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Attributes.h"
+#include "nsISystemProxySettings.h"
+#include "nsIServiceManager.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsPrintfCString.h"
+#include "nsNetCID.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIURI.h"
+#include "GeckoProfiler.h"
+#include "prnetdb.h"
+#include "ProxyUtils.h"
+
+class nsWindowsSystemProxySettings final : public nsISystemProxySettings
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISYSTEMPROXYSETTINGS
+
+ nsWindowsSystemProxySettings() {};
+ nsresult Init();
+
+private:
+ ~nsWindowsSystemProxySettings() {};
+
+ bool MatchOverride(const nsACString& aHost);
+ bool PatternMatch(const nsACString& aHost, const nsACString& aOverride);
+};
+
+NS_IMPL_ISUPPORTS(nsWindowsSystemProxySettings, nsISystemProxySettings)
+
+NS_IMETHODIMP
+nsWindowsSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly)
+{
+ *aMainThreadOnly = false;
+ return NS_OK;
+}
+
+
+nsresult
+nsWindowsSystemProxySettings::Init()
+{
+ return NS_OK;
+}
+
+static void SetProxyResult(const char* aType, const nsACString& aHostPort,
+ nsACString& aResult)
+{
+ aResult.AssignASCII(aType);
+ aResult.Append(' ');
+ aResult.Append(aHostPort);
+}
+
+static void SetProxyResultDirect(nsACString& aResult)
+{
+ // For whatever reason, a proxy is not to be used.
+ aResult.AssignASCII("DIRECT");
+}
+
+static nsresult ReadInternetOption(uint32_t aOption, uint32_t& aFlags,
+ nsAString& aValue)
+{
+ DWORD connFlags = 0;
+ WCHAR connName[RAS_MaxEntryName + 1];
+ MOZ_SEH_TRY {
+ InternetGetConnectedStateExW(&connFlags, connName,
+ mozilla::ArrayLength(connName), 0);
+ } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ return NS_ERROR_FAILURE;
+ }
+
+ INTERNET_PER_CONN_OPTIONW options[2];
+ options[0].dwOption = INTERNET_PER_CONN_FLAGS_UI;
+ options[1].dwOption = aOption;
+
+ INTERNET_PER_CONN_OPTION_LISTW list;
+ list.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LISTW);
+ list.pszConnection = connFlags & INTERNET_CONNECTION_MODEM ?
+ connName : nullptr;
+ list.dwOptionCount = mozilla::ArrayLength(options);
+ list.dwOptionError = 0;
+ list.pOptions = options;
+
+ unsigned long size = sizeof(INTERNET_PER_CONN_OPTION_LISTW);
+ if (!InternetQueryOptionW(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION,
+ &list, &size)) {
+ if (GetLastError() != ERROR_INVALID_PARAMETER) {
+ return NS_ERROR_FAILURE;
+ }
+ options[0].dwOption = INTERNET_PER_CONN_FLAGS;
+ size = sizeof(INTERNET_PER_CONN_OPTION_LISTW);
+ MOZ_SEH_TRY {
+ if (!InternetQueryOptionW(nullptr,
+ INTERNET_OPTION_PER_CONNECTION_OPTION,
+ &list, &size)) {
+ return NS_ERROR_FAILURE;
+ }
+ } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ aFlags = options[0].Value.dwValue;
+ aValue.Assign(options[1].Value.pszValue);
+ GlobalFree(options[1].Value.pszValue);
+
+ return NS_OK;
+}
+
+bool
+nsWindowsSystemProxySettings::MatchOverride(const nsACString& aHost)
+{
+ nsresult rv;
+ uint32_t flags = 0;
+ nsAutoString buf;
+
+ rv = ReadInternetOption(INTERNET_PER_CONN_PROXY_BYPASS, flags, buf);
+ if (NS_FAILED(rv))
+ return false;
+
+ NS_ConvertUTF16toUTF8 cbuf(buf);
+
+ nsAutoCString host(aHost);
+ int32_t start = 0;
+ int32_t end = cbuf.Length();
+
+ // Windows formats its proxy override list in the form:
+ // server;server;server where 'server' is a server name pattern or IP
+ // address, or "<local>". "<local>" must be translated to
+ // "localhost;127.0.0.1".
+ // In a server name pattern, a '*' character matches any substring and
+ // all other characters must match themselves; the whole pattern must match
+ // the whole hostname.
+ while (true) {
+ int32_t delimiter = cbuf.FindCharInSet(" ;", start);
+ if (delimiter == -1)
+ delimiter = end;
+
+ if (delimiter != start) {
+ const nsAutoCString override(Substring(cbuf, start,
+ delimiter - start));
+ if (override.EqualsLiteral("<local>")) {
+ PRNetAddr addr;
+ bool isIpAddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
+
+ // Don't use proxy for local hosts (plain hostname, no dots)
+ if (!isIpAddr && !host.Contains('.')) {
+ return true;
+ }
+
+ if (host.EqualsLiteral("127.0.0.1") ||
+ host.EqualsLiteral("::1")) {
+ return true;
+ }
+ } else if (PatternMatch(host, override)) {
+ return true;
+ }
+ }
+
+ if (delimiter == end)
+ break;
+ start = ++delimiter;
+ }
+
+ return false;
+}
+
+bool
+nsWindowsSystemProxySettings::PatternMatch(const nsACString& aHost,
+ const nsACString& aOverride)
+{
+ return mozilla::toolkit::system::IsHostProxyEntry(aHost, aOverride);
+}
+
+nsresult
+nsWindowsSystemProxySettings::GetPACURI(nsACString& aResult)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::STORAGE);
+ nsresult rv;
+ uint32_t flags = 0;
+ nsAutoString buf;
+
+ rv = ReadInternetOption(INTERNET_PER_CONN_AUTOCONFIG_URL, flags, buf);
+ if (!(flags & PROXY_TYPE_AUTO_PROXY_URL)) {
+ aResult.Truncate();
+ return rv;
+ }
+
+ if (NS_SUCCEEDED(rv))
+ aResult = NS_ConvertUTF16toUTF8(buf);
+ return rv;
+}
+
+nsresult
+nsWindowsSystemProxySettings::GetProxyForURI(const nsACString & aSpec,
+ const nsACString & aScheme,
+ const nsACString & aHost,
+ const int32_t aPort,
+ nsACString & aResult)
+{
+ nsresult rv;
+ uint32_t flags = 0;
+ nsAutoString buf;
+
+ rv = ReadInternetOption(INTERNET_PER_CONN_PROXY_SERVER, flags, buf);
+ if (NS_FAILED(rv) || !(flags & PROXY_TYPE_PROXY)) {
+ SetProxyResultDirect(aResult);
+ return NS_OK;
+ }
+
+ if (MatchOverride(aHost)) {
+ SetProxyResultDirect(aResult);
+ return NS_OK;
+ }
+
+ NS_ConvertUTF16toUTF8 cbuf(buf);
+
+ nsAutoCString prefix;
+ ToLowerCase(aScheme, prefix);
+
+ prefix.Append('=');
+
+ nsAutoCString specificProxy;
+ nsAutoCString defaultProxy;
+ nsAutoCString socksProxy;
+ int32_t start = 0;
+ int32_t end = cbuf.Length();
+
+ while (true) {
+ int32_t delimiter = cbuf.FindCharInSet(" ;", start);
+ if (delimiter == -1)
+ delimiter = end;
+
+ if (delimiter != start) {
+ const nsAutoCString proxy(Substring(cbuf, start,
+ delimiter - start));
+ if (proxy.FindChar('=') == -1) {
+ // If a proxy name is listed by itself, it is used as the
+ // default proxy for any protocols that do not have a specific
+ // proxy specified.
+ // (http://msdn.microsoft.com/en-us/library/aa383996%28VS.85%29.aspx)
+ defaultProxy = proxy;
+ } else if (proxy.Find(prefix) == 0) {
+ // To list a proxy for a specific protocol, the string must
+ // follow the format "<protocol>=<protocol>://<proxy_name>".
+ // (http://msdn.microsoft.com/en-us/library/aa383996%28VS.85%29.aspx)
+ specificProxy = Substring(proxy, prefix.Length());
+ break;
+ } else if (proxy.Find("socks=") == 0) {
+ // SOCKS proxy.
+ socksProxy = Substring(proxy, 5); // "socks=" length.
+ }
+ }
+
+ if (delimiter == end)
+ break;
+ start = ++delimiter;
+ }
+
+ if (!specificProxy.IsEmpty())
+ SetProxyResult("PROXY", specificProxy, aResult); // Protocol-specific proxy.
+ else if (!defaultProxy.IsEmpty())
+ SetProxyResult("PROXY", defaultProxy, aResult); // Default proxy.
+ else if (!socksProxy.IsEmpty())
+ SetProxyResult("SOCKS", socksProxy, aResult); // SOCKS proxy.
+ else
+ SetProxyResultDirect(aResult); // Direct connection.
+
+ return NS_OK;
+}
+
+#define NS_WINDOWSSYSTEMPROXYSERVICE_CID /* 4e22d3ea-aaa2-436e-ada4-9247de57d367 */\
+ { 0x4e22d3ea, 0xaaa2, 0x436e, \
+ {0xad, 0xa4, 0x92, 0x47, 0xde, 0x57, 0xd3, 0x67 } }
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowsSystemProxySettings, Init)
+NS_DEFINE_NAMED_CID(NS_WINDOWSSYSTEMPROXYSERVICE_CID);
+
+static const mozilla::Module::CIDEntry kSysProxyCIDs[] = {
+ { &kNS_WINDOWSSYSTEMPROXYSERVICE_CID, false, nullptr, nsWindowsSystemProxySettingsConstructor },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kSysProxyContracts[] = {
+ { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_WINDOWSSYSTEMPROXYSERVICE_CID },
+ { nullptr }
+};
+
+static const mozilla::Module kSysProxyModule = {
+ mozilla::Module::kVersion,
+ kSysProxyCIDs,
+ kSysProxyContracts
+};
+
+NSMODULE_DEFN(nsWindowsProxyModule) = &kSysProxyModule;
diff --git a/toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp b/toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp
new file mode 100644
index 000000000..9cd6bf981
--- /dev/null
+++ b/toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "gtest/gtest.h"
+#include "ProxyUtils.h"
+
+using namespace mozilla::toolkit::system;
+
+TEST(WindowsProxy, TestProxyBypassRules)
+{
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"),NS_LITERAL_CSTRING("*mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("notmozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.com"), NS_LITERAL_CSTRING("*.mozilla.*")));
+}
+
+TEST(WindowsProxy, TestProxyBypassRulesIPv4)
+{
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.1.*")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.2.*")));
+
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("10.1.2.3"), NS_LITERAL_CSTRING("10.0.0.0/8")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168/16")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168/17")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168.128/17")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.1.1/32")));
+}
+
+TEST(WindowsProxy, TestProxyBypassRulesIPv6)
+{
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/64")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0000:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/80")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/80")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0000:0000:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/96")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/96")));
+}
diff --git a/toolkit/system/windowsproxy/tests/gtest/moz.build b/toolkit/system/windowsproxy/tests/gtest/moz.build
new file mode 100644
index 000000000..3ae4e73c2
--- /dev/null
+++ b/toolkit/system/windowsproxy/tests/gtest/moz.build
@@ -0,0 +1,18 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES += [
+ 'TestProxyBypassRules.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '/toolkit/system/windowsproxy',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wshadow']