Bug 1360566 - [Wayland] - Implement XRemote Server by D-Bus, r?jhorak draft
authorMartin Stransky <stransky@redhat.com>
Thu, 09 Nov 2017 12:13:32 +0100
changeset 699486 5f70efa8022eb494e8fe4bc9b157e16be8d4db02
parent 698679 f41930a869a84af81df1a88d8e82323ff3a6509a
child 740645 bbecd752a96bcad56a0c94a7383f0a9f271f1958
push id89590
push userstransky@redhat.com
push dateFri, 17 Nov 2017 08:47:25 +0000
reviewersjhorak
bugs1360566
milestone59.0a1
Bug 1360566 - [Wayland] - Implement XRemote Server by D-Bus, r?jhorak It creates new nsRemoteService instance which is parent (proxy) class which is registered as global nsIRemoteService. It provides basic functionality (watch observer for shutdown, launch firefox instance by HandleCommandLine()) for child services which are system specific. nsDBusRemoteService listens on DBus interface and it's available on DBus enabled systems only. nsGtkRemoteService is the former one based on X window propery mechanism. MozReview-Commit-ID: GHpXdjstwyY
toolkit/components/remote/moz.build
toolkit/components/remote/nsDBusRemoteService.cpp
toolkit/components/remote/nsDBusRemoteService.h
toolkit/components/remote/nsGTKRemoteService.cpp
toolkit/components/remote/nsGTKRemoteService.h
toolkit/components/remote/nsRemoteService.cpp
toolkit/components/remote/nsRemoteService.h
toolkit/components/remote/nsXRemoteService.cpp
toolkit/components/remote/nsXRemoteService.h
--- a/toolkit/components/remote/moz.build
+++ b/toolkit/components/remote/moz.build
@@ -15,13 +15,19 @@ XPIDL_MODULE = 'toolkitremote'
 
 SOURCES += [
     'nsXRemoteService.cpp',
 ]
 
 if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
     SOURCES += [
         'nsGTKRemoteService.cpp',
+        'nsRemoteService.cpp',
     ]
+    if CONFIG['MOZ_ENABLE_DBUS']:
+        SOURCES += [
+            'nsDBusRemoteService.cpp',
+        ]
+        CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
 
 FINAL_LIBRARY = 'xul'
 
 CXXFLAGS += CONFIG['TK_CFLAGS']
new file mode 100644
--- /dev/null
+++ b/toolkit/components/remote/nsDBusRemoteService.cpp
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=2:tabstop=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 "nsDBusRemoteService.h"
+#include "nsRemoteService.h"
+
+#include "nsIBaseWindow.h"
+#include "nsIDocShell.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsIServiceManager.h"
+#include "nsIWeakReference.h"
+#include "nsIWidget.h"
+#include "nsIAppShellService.h"
+#include "nsAppShellCID.h"
+#include "nsPrintfCString.h"
+
+#include "nsCOMPtr.h"
+
+#include "nsGTKToolkit.h"
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+NS_IMPL_ISUPPORTS(nsDBusRemoteService,
+                  nsIRemoteService)
+
+NS_IMETHODIMP
+nsDBusRemoteService::RegisterWindow(mozIDOMWindow* aWindow)
+{
+  // We don't listen for property change events on DBus remote
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+const char* introspect_template =
+"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
+"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\";>\n"
+"<node>\n"
+" <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
+"   <method name=\"Introspect\">\n"
+"     <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
+"   </method>\n"
+" </interface>\n"
+" <interface name=\"org.mozilla.%s\">\n"
+"   <method name=\"OpenURL\">\n"
+"     <arg name=\"url\" direction=\"in\" type=\"s\"/>\n"
+"   </method>\n"
+" </interface>\n"
+"</node>\n";
+
+DBusHandlerResult
+nsDBusRemoteService::Introspect(DBusMessage *msg)
+{
+  DBusMessage *reply;
+
+  reply = dbus_message_new_method_return(msg);
+  if (!reply)
+   return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+  nsAutoCString introspect_xml;
+  introspect_xml = nsPrintfCString(introspect_template, mAppName.get());
+
+  const char *message = introspect_xml.get();
+  dbus_message_append_args(reply,
+     DBUS_TYPE_STRING, &message,
+     DBUS_TYPE_INVALID);
+
+  dbus_connection_send(mConnection, reply, nullptr);
+  dbus_message_unref(reply);
+
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+DBusHandlerResult
+nsDBusRemoteService::OpenURL(DBusMessage *msg)
+{
+  DBusMessage *reply = nullptr;
+  const char  *commandLine;
+  int          length;
+
+  if (!dbus_message_get_args(msg, nullptr, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+      &commandLine, &length, DBUS_TYPE_INVALID) || length == 0) {
+    nsAutoCString errorMsg;
+    errorMsg = nsPrintfCString("org.mozilla.%s.Error", mAppName.get());
+    reply = dbus_message_new_error(msg, errorMsg.get(), "Wrong argument");
+  } else {
+    nsRemoteService::HandleCommandLine(commandLine, nullptr, 0);
+    reply = dbus_message_new_method_return(msg);
+  }
+
+  dbus_connection_send(mConnection, reply, nullptr);
+  dbus_message_unref(reply);
+
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+DBusHandlerResult
+nsDBusRemoteService::HandleDBusMessage(DBusConnection *aConnection, DBusMessage *msg)
+{
+  NS_ASSERTION(mConnection == aConnection, "Wrong D-Bus connection.");
+
+  const char *method = dbus_message_get_member(msg);
+  const char *iface = dbus_message_get_interface(msg);
+
+  if ((strcmp("Introspect", method) == 0) &&
+    (strcmp("org.freedesktop.DBus.Introspectable", iface) == 0)) {
+    return Introspect(msg);
+  }
+
+  nsAutoCString ourInterfaceName;
+  ourInterfaceName = nsPrintfCString("org.mozilla.%s", mAppName.get());
+
+  if ((strcmp("OpenURL", method) == 0) &&
+     (strcmp(ourInterfaceName.get(), iface) == 0)) {
+    return OpenURL(msg);
+  }
+
+  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+void
+nsDBusRemoteService::UnregisterDBusInterface(DBusConnection *aConnection)
+{
+  NS_ASSERTION(mConnection == aConnection, "Wrong D-Bus connection.");
+  // Not implemented
+}
+
+static DBusHandlerResult
+message_handler(DBusConnection *conn, DBusMessage *msg, void *user_data)
+{
+  auto interface = static_cast<nsDBusRemoteService*>(user_data);
+  return interface->HandleDBusMessage(conn, msg);
+}
+
+static void
+unregister(DBusConnection *conn, void *user_data)
+{
+  auto interface = static_cast<nsDBusRemoteService*>(user_data);
+  interface->UnregisterDBusInterface(conn);
+}
+
+static DBusObjectPathVTable remoteHandlersTable = {
+  .unregister_function  = unregister,
+  .message_function = message_handler,
+};
+
+NS_IMETHODIMP
+nsDBusRemoteService::Startup(const char* aAppName, const char* aProfileName)
+{
+  if (!aAppName || !aProfileName)
+    return NS_ERROR_INVALID_ARG;
+
+  if (mConnection && dbus_connection_get_is_connected(mConnection)) {
+    // We're already connected so we don't need to reconnect
+    return NS_ERROR_ALREADY_INITIALIZED;
+  }
+
+  mAppName = aAppName;
+  ToLowerCase(mAppName);
+
+  mConnection = already_AddRefed<DBusConnection>(
+    dbus_bus_get(DBUS_BUS_SESSION, nullptr));
+  if (!mConnection)
+    return NS_ERROR_FAILURE;
+
+  dbus_connection_set_exit_on_disconnect(mConnection, false);
+
+  nsAutoCString interfaceName;
+  interfaceName = nsPrintfCString("org.mozilla.%s.%s", aAppName, aProfileName);
+
+  int ret = dbus_bus_request_name(mConnection, interfaceName.get(),
+                                 DBUS_NAME_FLAG_DO_NOT_QUEUE, nullptr);
+  // The interface is already owned - there is another application/profile
+  // instance already running.
+  if (ret == -1) {
+   mConnection = nullptr;
+   return NS_ERROR_FAILURE;
+  }
+
+  nsAutoCString objectName;
+  objectName = nsPrintfCString("/org/mozilla/%s/Remote", aAppName);
+
+  if (!dbus_connection_register_object_path(mConnection, objectName.get(),
+                                           &remoteHandlersTable, this)) {
+    mConnection = nullptr;
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDBusRemoteService::Shutdown()
+{
+  // dbus_connection_unref() will be called by RefPtr here.
+  mConnection = nullptr;
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/remote/nsDBusRemoteService.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=2:tabstop=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 __nsDBusRemoteService_h__
+#define __nsDBusRemoteService_h__
+
+#include "nsIRemoteService.h"
+#include "mozilla/DBusHelpers.h"
+#include "nsString.h"
+
+class nsDBusRemoteService final : public nsIRemoteService
+{
+public:
+  // We will be a static singleton, so don't use the ordinary methods.
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIREMOTESERVICE
+
+  nsDBusRemoteService()
+    : mConnection(nullptr)
+    , mAppName(nullptr)
+    { }
+
+  DBusHandlerResult HandleDBusMessage(DBusConnection *aConnection, DBusMessage *msg);
+  void UnregisterDBusInterface(DBusConnection *aConnection);
+
+private:
+  ~nsDBusRemoteService() { }
+
+  DBusHandlerResult OpenURL(DBusMessage *msg);
+  DBusHandlerResult Introspect(DBusMessage *msg);
+
+  // The connection is owned by DBus library
+  RefPtr<DBusConnection>  mConnection;
+  nsCString               mAppName;
+};
+
+#endif // __nsDBusRemoteService_h__
--- a/toolkit/components/remote/nsGTKRemoteService.cpp
+++ b/toolkit/components/remote/nsGTKRemoteService.cpp
@@ -21,24 +21,22 @@
 #include "nsIAppShellService.h"
 #include "nsAppShellCID.h"
 
 #include "nsCOMPtr.h"
 
 #include "nsGTKToolkit.h"
 
 NS_IMPL_ISUPPORTS(nsGTKRemoteService,
-                  nsIRemoteService,
-                  nsIObserver)
+                  nsIRemoteService)
 
 NS_IMETHODIMP
 nsGTKRemoteService::Startup(const char* aAppName, const char* aProfileName)
 {
   NS_ASSERTION(aAppName, "Don't pass a null appname!");
-  sRemoteImplementation = this;
 
   if (mServerWindow) return NS_ERROR_ALREADY_INITIALIZED;
 
   XRemoteBaseStartup(aAppName, aProfileName);
 
   mServerWindow = gtk_invisible_new();
   gtk_widget_realize(mServerWindow);
   HandleCommandsFor(mServerWindow, nullptr);
@@ -88,55 +86,35 @@ nsGTKRemoteService::RegisterWindow(mozID
 NS_IMETHODIMP
 nsGTKRemoteService::Shutdown()
 {
   if (!mServerWindow)
     return NS_ERROR_NOT_INITIALIZED;
 
   gtk_widget_destroy(mServerWindow);
   mServerWindow = nullptr;
+
   return NS_OK;
 }
 
-// Set desktop startup ID to the passed ID, if there is one, so that any created
-// windows get created with the right window manager metadata, and any windows
-// that get new tabs and are activated also get the right WM metadata.
-// The timestamp will be used if there is no desktop startup ID, or if we're
-// raising an existing window rather than showing a new window for the first time.
-void
-nsGTKRemoteService::SetDesktopStartupIDOrTimestamp(const nsACString& aDesktopStartupID,
-                                                   uint32_t aTimestamp) {
-  nsGTKToolkit* toolkit = nsGTKToolkit::GetToolkit();
-  if (!toolkit)
-    return;
-
-  if (!aDesktopStartupID.IsEmpty()) {
-    toolkit->SetDesktopStartupID(aDesktopStartupID);
-  }
-
-  toolkit->SetFocusTimestamp(aTimestamp);
-}
-
-
 void
 nsGTKRemoteService::HandleCommandsFor(GtkWidget* widget,
                                       nsIWeakReference* aWindow)
 {
   g_signal_connect(G_OBJECT(widget), "property_notify_event",
                    G_CALLBACK(HandlePropertyChange), aWindow);
 
   gtk_widget_add_events(widget, GDK_PROPERTY_CHANGE_MASK);
 
 #if (MOZ_WIDGET_GTK == 2)
   Window window = GDK_WINDOW_XWINDOW(widget->window);
 #else
   Window window = gdk_x11_window_get_xid(gtk_widget_get_window(widget));
 #endif
   nsXRemoteService::HandleCommandsFor(window);
-
 }
 
 gboolean
 nsGTKRemoteService::HandlePropertyChange(GtkWidget *aWidget,
                                          GdkEventProperty *pevent,
                                          nsIWeakReference *aThis)
 {
   if (pevent->state == GDK_PROPERTY_NEW_VALUE) {
@@ -149,33 +127,8 @@ nsGTKRemoteService::HandlePropertyChange
 #endif
     return HandleNewProperty(window,
                              GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
                              pevent->time, changedAtom, aThis);
   }
   return FALSE;
 }
 
-
-// {C0773E90-5799-4eff-AD03-3EBCD85624AC}
-#define NS_REMOTESERVICE_CID \
-  { 0xc0773e90, 0x5799, 0x4eff, { 0xad, 0x3, 0x3e, 0xbc, 0xd8, 0x56, 0x24, 0xac } }
-
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsGTKRemoteService)
-NS_DEFINE_NAMED_CID(NS_REMOTESERVICE_CID);
-
-static const mozilla::Module::CIDEntry kRemoteCIDs[] = {
-  { &kNS_REMOTESERVICE_CID, false, nullptr, nsGTKRemoteServiceConstructor },
-  { nullptr }
-};
-
-static const mozilla::Module::ContractIDEntry kRemoteContracts[] = {
-  { "@mozilla.org/toolkit/remote-service;1", &kNS_REMOTESERVICE_CID },
-  { nullptr }
-};
-
-static const mozilla::Module kRemoteModule = {
-  mozilla::Module::kVersion,
-  kRemoteCIDs,
-  kRemoteContracts
-};
-
-NSMODULE_DEFN(RemoteServiceModule) = &kRemoteModule;
--- a/toolkit/components/remote/nsGTKRemoteService.h
+++ b/toolkit/components/remote/nsGTKRemoteService.h
@@ -7,43 +7,38 @@
 
 #ifndef __nsGTKRemoteService_h__
 #define __nsGTKRemoteService_h__
 
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 #include <gtk/gtk.h>
 
+#include "nsIRemoteService.h"
 #include "nsInterfaceHashtable.h"
 #include "nsXRemoteService.h"
 #include "mozilla/Attributes.h"
 
-class nsGTKRemoteService final : public nsXRemoteService
+class nsGTKRemoteService final : public nsIRemoteService,
+                                 public nsXRemoteService
 {
 public:
-  // We will be a static singleton, so don't use the ordinary methods.
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREMOTESERVICE
 
+  nsGTKRemoteService()
+    : mServerWindow(nullptr)
+    { }
 
-  nsGTKRemoteService() :
-    mServerWindow(nullptr) { }
-
+  static gboolean HandlePropertyChange(GtkWidget *widget,
+                                       GdkEventProperty *event,
+                                       nsIWeakReference* aThis);
 private:
   ~nsGTKRemoteService() { }
 
   void HandleCommandsFor(GtkWidget* aWidget,
                          nsIWeakReference* aWindow);
 
-
-  static gboolean HandlePropertyChange(GtkWidget *widget,
-                                       GdkEventProperty *event,
-                                       nsIWeakReference* aThis);
-
-
-  virtual void SetDesktopStartupIDOrTimestamp(const nsACString& aDesktopStartupID,
-                                              uint32_t aTimestamp) override;
-
   nsInterfaceHashtable<nsPtrHashKey<GtkWidget>, nsIWeakReference> mWindows;
   GtkWidget* mServerWindow;
 };
 
 #endif // __nsGTKRemoteService_h__
new file mode 100644
--- /dev/null
+++ b/toolkit/components/remote/nsRemoteService.cpp
@@ -0,0 +1,242 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=2:tabstop=8:
+ */
+/* 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 "nsGTKRemoteService.h"
+#include "nsDBusRemoteService.h"
+#include "nsRemoteService.h"
+
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+
+#include "nsIServiceManager.h"
+#include "nsIAppShellService.h"
+#include "nsAppShellCID.h"
+#include "nsInterfaceHashtable.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsIWeakReference.h"
+#include "nsGTKToolkit.h"
+#include "nsICommandLineRunner.h"
+#include "nsICommandLine.h"
+#include "nsString.h"
+#include "nsIFile.h"
+
+NS_IMPL_ISUPPORTS(nsRemoteService,
+                  nsIRemoteService,
+                  nsIObserver)
+
+NS_IMETHODIMP
+nsRemoteService::Startup(const char* aAppName, const char* aProfileName)
+{
+#if defined(MOZ_ENABLE_DBUS)
+    nsresult rv;
+    mDBusRemoteService = new nsDBusRemoteService();
+    rv = mDBusRemoteService->Startup(aAppName, aProfileName);
+    if (NS_FAILED(rv)) {
+        mDBusRemoteService = nullptr;
+    }
+#endif
+
+    if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+        mGtkRemoteService = new nsGTKRemoteService();
+        mGtkRemoteService->Startup(aAppName, aProfileName);
+    }
+
+    if (!mDBusRemoteService && !mGtkRemoteService)
+        return NS_ERROR_FAILURE;
+
+    nsCOMPtr<nsIObserverService> obs(do_GetService("@mozilla.org/observer-service;1"));
+    if (obs) {
+        obs->AddObserver(this, "xpcom-shutdown", false);
+        obs->AddObserver(this, "quit-application", false);
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsRemoteService::RegisterWindow(mozIDOMWindow* aWindow)
+{
+    // Note: RegisterWindow() is not implemented/needed by DBus service.
+    if (mGtkRemoteService) {
+        mGtkRemoteService->RegisterWindow(aWindow);
+    }
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsRemoteService::Shutdown()
+{
+#if defined(MOZ_ENABLE_DBUS)
+    if (mDBusRemoteService) {
+        mDBusRemoteService->Shutdown();
+        mDBusRemoteService = nullptr;
+    }
+#endif
+    if (mGtkRemoteService) {
+        mGtkRemoteService->Shutdown();
+        mGtkRemoteService = nullptr;
+    }
+    return NS_OK;
+}
+
+nsRemoteService::~nsRemoteService()
+{
+    Shutdown();
+}
+
+NS_IMETHODIMP
+nsRemoteService::Observe(nsISupports* aSubject,
+                          const char *aTopic,
+                          const char16_t *aData)
+{
+    // This can be xpcom-shutdown or quit-application, but it's the same either
+    // way.
+    Shutdown();
+    return NS_OK;
+}
+
+// Set desktop startup ID to the passed ID, if there is one, so that any created
+// windows get created with the right window manager metadata, and any windows
+// that get new tabs and are activated also get the right WM metadata.
+// The timestamp will be used if there is no desktop startup ID, or if we're
+// raising an existing window rather than showing a new window for the first time.
+void
+nsRemoteService::SetDesktopStartupIDOrTimestamp(const nsACString& aDesktopStartupID,
+                                                uint32_t aTimestamp) {
+  nsGTKToolkit* toolkit = nsGTKToolkit::GetToolkit();
+  if (!toolkit)
+    return;
+
+  if (!aDesktopStartupID.IsEmpty()) {
+    toolkit->SetDesktopStartupID(aDesktopStartupID);
+  }
+
+  toolkit->SetFocusTimestamp(aTimestamp);
+}
+
+static bool
+FindExtensionParameterInCommand(const char* aParameterName,
+                                const nsACString& aCommand,
+                                char aSeparator,
+                                nsACString* aValue)
+{
+  nsAutoCString searchFor;
+  searchFor.Append(aSeparator);
+  searchFor.Append(aParameterName);
+  searchFor.Append('=');
+
+  nsACString::const_iterator start, end;
+  aCommand.BeginReading(start);
+  aCommand.EndReading(end);
+  if (!FindInReadable(searchFor, start, end))
+    return false;
+
+  nsACString::const_iterator charStart, charEnd;
+  charStart = end;
+  aCommand.EndReading(charEnd);
+  nsACString::const_iterator idStart = charStart, idEnd;
+  if (FindCharInReadable(aSeparator, charStart, charEnd)) {
+    idEnd = charStart;
+  } else {
+    idEnd = charEnd;
+  }
+  *aValue = nsDependentCSubstring(idStart, idEnd);
+  return true;
+}
+
+const char*
+nsRemoteService::HandleCommandLine(const char* aBuffer, nsIDOMWindow* aWindow,
+                                   uint32_t aTimestamp)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsICommandLineRunner> cmdline
+    (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
+  if (NS_FAILED(rv))
+    return "509 internal error";
+
+  // the commandline property is constructed as an array of int32_t
+  // followed by a series of null-terminated strings:
+  //
+  // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
+  // (offset is from the beginning of the buffer)
+
+  int32_t argc = TO_LITTLE_ENDIAN32(*reinterpret_cast<const int32_t*>(aBuffer));
+  const char *wd   = aBuffer + ((argc + 1) * sizeof(int32_t));
+
+  nsCOMPtr<nsIFile> lf;
+  rv = NS_NewNativeLocalFile(nsDependentCString(wd), true,
+                             getter_AddRefs(lf));
+  if (NS_FAILED(rv))
+    return "509 internal error";
+
+  nsAutoCString desktopStartupID;
+
+  const char **argv = (const char**) malloc(sizeof(char*) * argc);
+  if (!argv) return "509 internal error";
+
+  const int32_t *offset = reinterpret_cast<const int32_t*>(aBuffer) + 1;
+
+  for (int i = 0; i < argc; ++i) {
+    argv[i] = aBuffer + TO_LITTLE_ENDIAN32(offset[i]);
+
+    if (i == 0) {
+      nsDependentCString cmd(argv[0]);
+      FindExtensionParameterInCommand("DESKTOP_STARTUP_ID",
+                                      cmd, ' ',
+                                      &desktopStartupID);
+    }
+  }
+
+  rv = cmdline->Init(argc, argv, lf, nsICommandLine::STATE_REMOTE_AUTO);
+
+  free (argv);
+  if (NS_FAILED(rv)) {
+    return "509 internal error";
+  }
+
+  if (aWindow)
+    cmdline->SetWindowContext(aWindow);
+
+  SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp);
+
+  rv = cmdline->Run();
+
+  if (NS_ERROR_ABORT == rv)
+    return "500 command not parseable";
+
+  if (NS_FAILED(rv))
+    return "509 internal error";
+
+  return "200 executed command";
+}
+
+// {C0773E90-5799-4eff-AD03-3EBCD85624AC}
+#define NS_REMOTESERVICE_CID \
+  { 0xc0773e90, 0x5799, 0x4eff, { 0xad, 0x3, 0x3e, 0xbc, 0xd8, 0x56, 0x24, 0xac } }
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsRemoteService)
+NS_DEFINE_NAMED_CID(NS_REMOTESERVICE_CID);
+
+static const mozilla::Module::CIDEntry kRemoteCIDs[] = {
+  { &kNS_REMOTESERVICE_CID, false, nullptr, nsRemoteServiceConstructor },
+  { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kRemoteContracts[] = {
+  { "@mozilla.org/toolkit/remote-service;1", &kNS_REMOTESERVICE_CID },
+  { nullptr }
+};
+
+static const mozilla::Module kRemoteModule = {
+  mozilla::Module::kVersion,
+  kRemoteCIDs,
+  kRemoteContracts
+};
+
+NSMODULE_DEFN(RemoteServiceModule) = &kRemoteModule;
new file mode 100644
--- /dev/null
+++ b/toolkit/components/remote/nsRemoteService.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=2:tabstop=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 __nsRemoteService_h__
+#define __nsRemoteService_h__
+
+#include "nsIRemoteService.h"
+#include "nsIObserverService.h"
+#include "nsIObserver.h"
+#include "nsPIDOMWindow.h"
+
+class nsRemoteService final : public nsIRemoteService,
+                              public nsIObserver
+{
+public:
+  // We will be a static singleton, so don't use the ordinary methods.
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIREMOTESERVICE
+  NS_DECL_NSIOBSERVER
+
+  static const char*
+  HandleCommandLine(const char* aBuffer, nsIDOMWindow* aWindow,
+                    uint32_t aTimestamp);
+
+  nsCOMPtr<nsIRemoteService> mDBusRemoteService;
+  nsCOMPtr<nsIRemoteService> mGtkRemoteService;
+
+  nsRemoteService()
+    {}
+private:
+  ~nsRemoteService();
+
+  static void
+  SetDesktopStartupIDOrTimestamp(const nsACString& aDesktopStartupID,
+                                 uint32_t aTimestamp);
+};
+
+#endif // __nsRemoteService_h__
--- a/toolkit/components/remote/nsXRemoteService.cpp
+++ b/toolkit/components/remote/nsXRemoteService.cpp
@@ -3,16 +3,17 @@
  */
 /* 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 "nsXRemoteService.h"
+#include "nsRemoteService.h"
 #include "nsIObserverService.h"
 #include "nsCOMPtr.h"
 #include "nsIServiceManager.h"
 #include "nsICommandLineRunner.h"
 #include "nsICommandLine.h"
 
 #include "nsIBaseWindow.h"
 #include "nsIDocShell.h"
@@ -42,24 +43,16 @@ using namespace mozilla;
 #define MOZILLA_RESPONSE_PROP  "_MOZILLA_RESPONSE"
 #define MOZILLA_USER_PROP      "_MOZILLA_USER"
 #define MOZILLA_PROFILE_PROP   "_MOZILLA_PROFILE"
 #define MOZILLA_PROGRAM_PROP   "_MOZILLA_PROGRAM"
 #define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
 
 const unsigned char kRemoteVersion[] = "5.1";
 
-#ifdef IS_BIG_ENDIAN
-#define TO_LITTLE_ENDIAN32(x) \
-    ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
-    (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
-#else
-#define TO_LITTLE_ENDIAN32(x) (x)
-#endif
-
 // Minimize the roundtrips to the X server by getting all the atoms at once
 static const char *XAtomNames[] = {
   MOZILLA_VERSION_PROP,
   MOZILLA_LOCK_PROP,
   MOZILLA_RESPONSE_PROP,
   MOZILLA_USER_PROP,
   MOZILLA_PROFILE_PROP,
   MOZILLA_PROGRAM_PROP,
@@ -70,67 +63,27 @@ static Atom XAtoms[MOZ_ARRAY_LENGTH(XAto
 Atom nsXRemoteService::sMozVersionAtom;
 Atom nsXRemoteService::sMozLockAtom;
 Atom nsXRemoteService::sMozResponseAtom;
 Atom nsXRemoteService::sMozUserAtom;
 Atom nsXRemoteService::sMozProfileAtom;
 Atom nsXRemoteService::sMozProgramAtom;
 Atom nsXRemoteService::sMozCommandLineAtom;
 
-nsXRemoteService * nsXRemoteService::sRemoteImplementation = 0;
-
-
-static bool
-FindExtensionParameterInCommand(const char* aParameterName,
-                                const nsACString& aCommand,
-                                char aSeparator,
-                                nsACString* aValue)
-{
-  nsAutoCString searchFor;
-  searchFor.Append(aSeparator);
-  searchFor.Append(aParameterName);
-  searchFor.Append('=');
-
-  nsACString::const_iterator start, end;
-  aCommand.BeginReading(start);
-  aCommand.EndReading(end);
-  if (!FindInReadable(searchFor, start, end))
-    return false;
-
-  nsACString::const_iterator charStart, charEnd;
-  charStart = end;
-  aCommand.EndReading(charEnd);
-  nsACString::const_iterator idStart = charStart, idEnd;
-  if (FindCharInReadable(aSeparator, charStart, charEnd)) {
-    idEnd = charStart;
-  } else {
-    idEnd = charEnd;
-  }
-  *aValue = nsDependentCSubstring(idStart, idEnd);
-  return true;
-}
-
-
 nsXRemoteService::nsXRemoteService() = default;
 
 void
 nsXRemoteService::XRemoteBaseStartup(const char *aAppName, const char *aProfileName)
 {
     EnsureAtoms();
 
     mAppName = aAppName;
     ToLowerCase(mAppName);
 
     mProfileName = aProfileName;
-
-    nsCOMPtr<nsIObserverService> obs(do_GetService("@mozilla.org/observer-service;1"));
-    if (obs) {
-      obs->AddObserver(this, "xpcom-shutdown", false);
-      obs->AddObserver(this, "quit-application", false);
-    }
 }
 
 void
 nsXRemoteService::HandleCommandsFor(Window aWindowId)
 {
   // set our version
   XChangeProperty(mozilla::DefaultXDisplay(), aWindowId, sMozVersionAtom, XA_STRING,
                   8, PropModeReplace, kRemoteVersion, sizeof(kRemoteVersion) - 1);
@@ -151,27 +104,16 @@ nsXRemoteService::HandleCommandsFor(Wind
     XChangeProperty(mozilla::DefaultXDisplay(),
                     aWindowId, sMozProfileAtom, XA_STRING,
                     8, PropModeReplace,
                     (unsigned char*) mProfileName.get(), mProfileName.Length());
   }
 
 }
 
-NS_IMETHODIMP
-nsXRemoteService::Observe(nsISupports* aSubject,
-                          const char *aTopic,
-                          const char16_t *aData)
-{
-  // This can be xpcom-shutdown or quit-application, but it's the same either
-  // way.
-  Shutdown();
-  return NS_OK;
-}
-
 bool
 nsXRemoteService::HandleNewProperty(XID aWindowId, Display* aDisplay,
                                     Time aEventTime,
                                     Atom aChangedAtom,
                                     nsIWeakReference* aDomWindow)
 {
 
   nsCOMPtr<nsIDOMWindow> window (do_QueryReferent(aDomWindow));
@@ -203,17 +145,18 @@ nsXRemoteService::HandleNewProperty(XID 
     if (result != Success)
       return false;
 
     // Failed to get the data off the window or it was the wrong type?
     if (!data || !TO_LITTLE_ENDIAN32(*reinterpret_cast<int32_t*>(data)))
       return false;
 
     // cool, we got the property data.
-    const char *response = HandleCommandLine(data, window, aEventTime);
+    const char *response =
+      nsRemoteService::HandleCommandLine(data, window, aEventTime);
 
     // put the property onto the window as the response
     XChangeProperty (aDisplay, aWindowId,
                      sMozResponseAtom, XA_STRING,
                      8, PropModeReplace,
                      (const unsigned char *)response,
                      strlen (response));
     XFree(data);
@@ -228,84 +171,16 @@ nsXRemoteService::HandleNewProperty(XID 
   else if (aChangedAtom == sMozLockAtom) {
     // someone locked the window
     return true;
   }
 
   return false;
 }
 
-const char*
-nsXRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow,
-                                    uint32_t aTimestamp)
-{
-  nsresult rv;
-
-  nsCOMPtr<nsICommandLineRunner> cmdline
-    (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
-  if (NS_FAILED(rv))
-    return "509 internal error";
-
-  // the commandline property is constructed as an array of int32_t
-  // followed by a series of null-terminated strings:
-  //
-  // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
-  // (offset is from the beginning of the buffer)
-
-  int32_t argc = TO_LITTLE_ENDIAN32(*reinterpret_cast<int32_t*>(aBuffer));
-  char *wd   = aBuffer + ((argc + 1) * sizeof(int32_t));
-
-  nsCOMPtr<nsIFile> lf;
-  rv = NS_NewNativeLocalFile(nsDependentCString(wd), true,
-                             getter_AddRefs(lf));
-  if (NS_FAILED(rv))
-    return "509 internal error";
-
-  nsAutoCString desktopStartupID;
-
-  char **argv = (char**) malloc(sizeof(char*) * argc);
-  if (!argv) return "509 internal error";
-
-  int32_t  *offset = reinterpret_cast<int32_t*>(aBuffer) + 1;
-
-  for (int i = 0; i < argc; ++i) {
-    argv[i] = aBuffer + TO_LITTLE_ENDIAN32(offset[i]);
-
-    if (i == 0) {
-      nsDependentCString cmd(argv[0]);
-      FindExtensionParameterInCommand("DESKTOP_STARTUP_ID",
-                                      cmd, ' ',
-                                      &desktopStartupID);
-    }
-  }
-
-  rv = cmdline->Init(argc, argv, lf, nsICommandLine::STATE_REMOTE_AUTO);
-
-  free (argv);
-  if (NS_FAILED(rv)) {
-    return "509 internal error";
-  }
-
-  if (aWindow)
-    cmdline->SetWindowContext(aWindow);
-
-  if (sRemoteImplementation)
-    sRemoteImplementation->SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp);
-
-  rv = cmdline->Run();
-
-  if (NS_ERROR_ABORT == rv)
-    return "500 command not parseable";
-
-  if (NS_FAILED(rv))
-    return "509 internal error";
-
-  return "200 executed command";
-}
-
 void
 nsXRemoteService::EnsureAtoms(void)
 {
   if (sMozVersionAtom)
     return;
 
   XInternAtoms(mozilla::DefaultXDisplay(), const_cast<char**>(XAtomNames),
                ArrayLength(XAtomNames), False, XAtoms);
--- a/toolkit/components/remote/nsXRemoteService.h
+++ b/toolkit/components/remote/nsXRemoteService.h
@@ -5,52 +5,44 @@
  * 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 NSXREMOTESERVICE_H
 #define NSXREMOTESERVICE_H
 
 #include "nsString.h"
 
-#include "nsIRemoteService.h"
-#include "nsIObserver.h"
 #include <X11/Xlib.h>
 #include <X11/X.h>
 
 class nsIDOMWindow;
 class nsIWeakReference;
 
+#ifdef IS_BIG_ENDIAN
+#define TO_LITTLE_ENDIAN32(x) \
+    ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
+    (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
+#else
+#define TO_LITTLE_ENDIAN32(x) (x)
+#endif
+
 /**
   Base class for GTK/Qt remote service
 */
-class nsXRemoteService : public nsIRemoteService,
-                         public nsIObserver
+class nsXRemoteService
 {
-public:
-    NS_DECL_NSIOBSERVER
-
-
 protected:
     nsXRemoteService();
-
     static bool HandleNewProperty(Window aWindowId,Display* aDisplay,
-                                    Time aEventTime, Atom aChangedAtom,
-                                    nsIWeakReference* aDomWindow);
-
+                                  Time aEventTime, Atom aChangedAtom,
+                                  nsIWeakReference* aDomWindow);
     void XRemoteBaseStartup(const char *aAppName, const char *aProfileName);
-
     void HandleCommandsFor(Window aWindowId);
-    static nsXRemoteService *sRemoteImplementation;
 private:
     void EnsureAtoms();
-    static const char* HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow,
-                                         uint32_t aTimestamp);
-
-    virtual void SetDesktopStartupIDOrTimestamp(const nsACString& aDesktopStartupID,
-                                                uint32_t aTimestamp) = 0;
 
     nsCString mAppName;
     nsCString mProfileName;
 
     static Atom sMozVersionAtom;
     static Atom sMozLockAtom;
     static Atom sMozResponseAtom;
     static Atom sMozUserAtom;