Blob Blame History Raw
diff -up firefox-126.0/toolkit/xre/DBusService.cpp.D209910 firefox-126.0/toolkit/xre/DBusService.cpp
--- firefox-126.0/toolkit/xre/DBusService.cpp.D209910	2024-05-09 12:20:44.081565311 +0200
+++ firefox-126.0/toolkit/xre/DBusService.cpp	2024-05-09 12:59:43.865983543 +0200
@@ -0,0 +1,299 @@
+/* -*- 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 <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <gdk/gdk.h>
+#include "DBusService.h"
+#include "nsAppRunner.h"
+#include "mozilla/Unused.h"
+#include "mozilla/GUniquePtr.h"
+#include "mozilla/WidgetUtils.h"
+#include <gio/gio.h>
+#include "nsIObserverService.h"
+#include "WidgetUtilsGtk.h"
+#include "prproces.h"
+#include "nsPrintfCString.h"
+
+using namespace mozilla;
+using namespace mozilla::widget;
+
+DBusService::~DBusService() { StopFreedesktopListener(); }
+
+bool DBusService::Init() { return StartFreedesktopListener(); }
+
+void DBusService::Run() {
+  GMainLoop* loop = g_main_loop_new(nullptr, false);
+  g_main_loop_run(loop);
+  g_main_loop_unref(loop);
+}
+
+// Mozilla has old GIO version in build roots
+#define G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE GBusNameOwnerFlags(1 << 2)
+
+#define DBUS_BUS_NAME_TEMPLATE "org.mozilla.%s"
+#define DBUS_OBJECT_PATH_TEMPLATE "/org/mozilla/%s"
+
+static const char* GetDBusBusName() {
+  static const char* name = []() {
+    nsAutoCString appName;
+    gAppData->GetDBusAppName(appName);
+    return ToNewCString(nsPrintfCString(DBUS_BUS_NAME_TEMPLATE,
+                                        appName.get()));  // Intentionally leak
+  }();
+  return name;
+}
+
+static const char* GetDBusObjectPath() {
+  static const char* path = []() {
+    nsAutoCString appName;
+    gAppData->GetDBusAppName(appName);
+    return ToNewCString(nsPrintfCString(DBUS_OBJECT_PATH_TEMPLATE,
+                                        appName.get()));  // Intentionally leak
+  }();
+  return path;
+}
+
+static 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.Application'>\n"
+    "<method name='Activate'>\n"
+    "  <arg type='a{sv}' name='platform_data' direction='in'/>\n"
+    "  </method>\n"
+    "  <method name='Open'>\n"
+    "  <arg type='as' name='uris' direction='in'/>\n"
+    "  <arg type='a{sv}' name='platform_data' direction='in'/>\n"
+    "</method>\n"
+    "<method name='ActivateAction'>\n"
+    "  <arg type='s' name='action_name' direction='in'/>\n"
+    "  <arg type='av' name='parameter' direction='in'/>\n"
+    "  <arg type='a{sv}' name='platform_data' direction='in'/>\n"
+    "</method>\n"
+    "</interface>\n"
+    "</node>\n";
+
+void DBusService::LaunchApp(const char* aCommand, const char** aURIList,
+                            int aURIListLen) {
+  // Allocate space for all uris, executable name, command if supplied and
+  // null terminator
+  int paramsNum = aURIListLen + 2;
+  if (aCommand) {
+    paramsNum++;
+  }
+
+  char** argv = (char**)moz_xmalloc(sizeof(char*) * (paramsNum));
+  int argc = 0;
+  argv[argc++] = strdup(mAppFile);
+  if (aCommand) {
+    argv[argc++] = strdup(aCommand);
+  }
+  for (int i = 0; aURIList && i < aURIListLen; i++) {
+    argv[argc++] = strdup(aURIList[i]);
+  }
+  argv[argc++] = nullptr;
+
+  nsAutoCString exePath;
+  nsCOMPtr<nsIFile> lf;
+  if (NS_SUCCEEDED(XRE_GetBinaryPath(getter_AddRefs(lf)))) {
+    if (NS_SUCCEEDED(lf->GetNativePath(exePath))) {
+      PR_CreateProcessDetached(exePath.get(), argv, nullptr, nullptr);
+    }
+  }
+
+  for (int i = 0; i < argc; i++) {
+    free(argv[i]);
+  }
+  free(argv);
+}
+
+// The Activate method is called when the application is started without
+// files to open.
+// Open :: (a{sv}) → ()
+void DBusService::HandleFreedesktopActivate(GVariant* aParameters,
+                                            GDBusMethodInvocation* aReply) {
+  LaunchApp(nullptr, nullptr, 0);
+  g_dbus_method_invocation_return_value(aReply, nullptr);
+}
+
+// The Open method is called when the application is started with files.
+// The array of strings is an array of URIs, in UTF-8.
+// Open :: (as,a{sv}) → ()
+void DBusService::HandleFreedesktopOpen(GVariant* aParameters,
+                                        GDBusMethodInvocation* aReply) {
+  RefPtr<GVariant> variant =
+      dont_AddRef(g_variant_get_child_value(aParameters, 0));
+  gsize uriNum = 0;
+  const char** uriArray = g_variant_get_strv(variant, &uriNum);
+  LaunchApp(nullptr, uriArray, uriNum);
+  g_dbus_method_invocation_return_value(aReply, nullptr);
+}
+
+// The ActivateAction method is called when Desktop Actions are activated.
+// The action-name parameter is the name of the action.
+// ActivateAction :: (s,av,a{sv}) → ()
+void DBusService::HandleFreedesktopActivateAction(
+    GVariant* aParameters, GDBusMethodInvocation* aReply) {
+  const char* actionName;
+
+  // aParameters is "(s,av,a{sv})" type
+  RefPtr<GVariant> r = dont_AddRef(g_variant_get_child_value(aParameters, 0));
+  if (!(actionName = g_variant_get_string(r, nullptr))) {
+    g_dbus_method_invocation_return_error(
+        aReply, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Wrong params!");
+    return;
+  }
+
+  // TODO: Read av params and pass them to LaunchApp?
+
+  // actionName matches desktop action defined in .desktop file.
+  // We implement it for .desktop file shipped by flatpak
+  // (taskcluster/docker/firefox-flatpak/org.mozilla.firefox.desktop)
+  if (!strcmp(actionName, "new-window")) {
+    LaunchApp(nullptr, nullptr, 0);
+  } else if (!strcmp(actionName, "new-private-window")) {
+    LaunchApp("--private-window", nullptr, 0);
+  } else if (!strcmp(actionName, "profile-manager-window")) {
+    LaunchApp("--ProfileManager", nullptr, 0);
+  }
+
+  g_dbus_method_invocation_return_value(aReply, nullptr);
+}
+
+static void HandleMethodCall(GDBusConnection* aConnection, const gchar* aSender,
+                             const gchar* aObjectPath,
+                             const gchar* aInterfaceName,
+                             const gchar* aMethodName, GVariant* aParameters,
+                             GDBusMethodInvocation* aInvocation,
+                             gpointer aUserData) {
+  MOZ_ASSERT(aUserData);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (strcmp("org.freedesktop.Application", aInterfaceName) == 0) {
+    if (strcmp("Activate", aMethodName) == 0) {
+      static_cast<DBusService*>(aUserData)->HandleFreedesktopActivate(
+          aParameters, aInvocation);
+    } else if (strcmp("Open", aMethodName) == 0) {
+      static_cast<DBusService*>(aUserData)->HandleFreedesktopOpen(aParameters,
+                                                                  aInvocation);
+    } else if (strcmp("ActivateAction", aMethodName) == 0) {
+      static_cast<DBusService*>(aUserData)->HandleFreedesktopActivateAction(
+          aParameters, aInvocation);
+    } else {
+      g_warning("DBusService: HandleMethodCall() wrong method %s", aMethodName);
+    }
+  }
+}
+
+static GVariant* HandleGetProperty(GDBusConnection* aConnection,
+                                   const gchar* aSender,
+                                   const gchar* aObjectPath,
+                                   const gchar* aInterfaceName,
+                                   const gchar* aPropertyName, GError** aError,
+                                   gpointer aUserData) {
+  MOZ_ASSERT(aUserData);
+  MOZ_ASSERT(NS_IsMainThread());
+  g_set_error(aError, G_IO_ERROR, G_IO_ERROR_FAILED,
+              "%s:%s setting is not supported", aInterfaceName, aPropertyName);
+  return nullptr;
+}
+
+static gboolean HandleSetProperty(GDBusConnection* aConnection,
+                                  const gchar* aSender,
+                                  const gchar* aObjectPath,
+                                  const gchar* aInterfaceName,
+                                  const gchar* aPropertyName, GVariant* aValue,
+                                  GError** aError, gpointer aUserData) {
+  MOZ_ASSERT(aUserData);
+  MOZ_ASSERT(NS_IsMainThread());
+  g_set_error(aError, G_IO_ERROR, G_IO_ERROR_FAILED,
+              "%s:%s setting is not supported", aInterfaceName, aPropertyName);
+  return false;
+}
+
+static const GDBusInterfaceVTable gInterfaceVTable = {
+    HandleMethodCall, HandleGetProperty, HandleSetProperty};
+
+void DBusService::OnBusAcquired(GDBusConnection* aConnection) {
+  GUniquePtr<GError> error;
+  mIntrospectionData = dont_AddRef(g_dbus_node_info_new_for_xml(
+      introspect_template, getter_Transfers(error)));
+  if (!mIntrospectionData) {
+    g_warning("DBusService: g_dbus_node_info_new_for_xml() failed! %s",
+              error->message);
+    return;
+  }
+
+  mRegistrationId = g_dbus_connection_register_object(
+      aConnection, GetDBusObjectPath(), mIntrospectionData->interfaces[0],
+      &gInterfaceVTable, this,  /* user_data */
+      nullptr,                  /* user_data_free_func */
+      getter_Transfers(error)); /* GError** */
+
+  if (mRegistrationId == 0) {
+    g_warning(
+        "DBusService: g_dbus_connection_register_object() "
+        "failed! %s",
+        error->message);
+    return;
+  }
+}
+
+void DBusService::OnNameAcquired(GDBusConnection* aConnection) {
+  mConnection = aConnection;
+}
+
+void DBusService::OnNameLost(GDBusConnection* aConnection) {
+  mConnection = nullptr;
+  if (!mRegistrationId) {
+    return;
+  }
+  if (g_dbus_connection_unregister_object(aConnection, mRegistrationId)) {
+    mRegistrationId = 0;
+  }
+}
+
+bool DBusService::StartFreedesktopListener() {
+  if (mDBusID) {
+    // We're already connected so we don't need to reconnect
+    return false;
+  }
+
+  mDBusID = g_bus_own_name(
+      G_BUS_TYPE_SESSION, GetDBusBusName(), G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE,
+      [](GDBusConnection* aConnection, const gchar*,
+         gpointer aUserData) -> void {
+        static_cast<DBusService*>(aUserData)->OnBusAcquired(aConnection);
+      },
+      [](GDBusConnection* aConnection, const gchar*,
+         gpointer aUserData) -> void {
+        static_cast<DBusService*>(aUserData)->OnNameAcquired(aConnection);
+      },
+      [](GDBusConnection* aConnection, const gchar*,
+         gpointer aUserData) -> void {
+        static_cast<DBusService*>(aUserData)->OnNameLost(aConnection);
+      },
+      this, nullptr);
+
+  if (!mDBusID) {
+    g_warning("DBusService: g_bus_own_name() failed!");
+    return false;
+  }
+
+  return true;
+}
+
+void DBusService::StopFreedesktopListener() {
+  OnNameLost(mConnection);
+  if (mDBusID) {
+    g_bus_unown_name(mDBusID);
+    mDBusID = 0;
+  }
+  mIntrospectionData = nullptr;
+}
diff -up firefox-126.0/toolkit/xre/DBusService.h.D209910 firefox-126.0/toolkit/xre/DBusService.h
--- firefox-126.0/toolkit/xre/DBusService.h.D209910	2024-05-09 12:20:44.081565311 +0200
+++ firefox-126.0/toolkit/xre/DBusService.h	2024-05-09 12:20:44.081565311 +0200
@@ -0,0 +1,64 @@
+/* -*- 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 DBusService_h__
+#define DBusService_h__
+
+#include <glib.h>
+#include <gio/gio.h>
+#include "mozilla/RefPtr.h"
+#include "mozilla/GRefPtr.h"
+
+class DBusService final {
+ public:
+  DBusService(const char* aAppFile) : mAppFile(aAppFile){};
+
+  // nsBaseAppShell overrides:
+  bool Init();
+  void Run();
+
+  void StartDBusListening();
+  void StopDBusListening();
+
+  static void DBusSessionSleepCallback(GDBusProxy* aProxy, gchar* aSenderName,
+                                       gchar* aSignalName,
+                                       GVariant* aParameters,
+                                       gpointer aUserData);
+  static void DBusTimedatePropertiesChangedCallback(GDBusProxy* aProxy,
+                                                    gchar* aSenderName,
+                                                    gchar* aSignalName,
+                                                    GVariant* aParameters,
+                                                    gpointer aUserData);
+  static void DBusConnectClientResponse(GObject* aObject, GAsyncResult* aResult,
+                                        gpointer aUserData);
+
+  void LaunchApp(const char* aCommand, const char** aURIList, int aURIListLen);
+
+  void HandleFreedesktopActivate(GVariant* aParameters,
+                                 GDBusMethodInvocation* aReply);
+  void HandleFreedesktopOpen(GVariant* aParameters,
+                             GDBusMethodInvocation* aReply);
+  void HandleFreedesktopActivateAction(GVariant* aParameters,
+                                       GDBusMethodInvocation* aReply);
+
+  bool StartFreedesktopListener();
+  void StopFreedesktopListener();
+
+  void OnBusAcquired(GDBusConnection* aConnection);
+  void OnNameAcquired(GDBusConnection* aConnection);
+  void OnNameLost(GDBusConnection* aConnection);
+
+ private:
+  virtual ~DBusService();
+
+  // The connection is owned by DBus library
+  uint mDBusID = 0;
+  uint mRegistrationId = 0;
+  GDBusConnection* mConnection = nullptr;
+  RefPtr<GDBusNodeInfo> mIntrospectionData;
+  const char* mAppFile = nullptr;
+};
+
+#endif  // DBusService_h__
diff -up firefox-126.0/toolkit/xre/moz.build.D209910 firefox-126.0/toolkit/xre/moz.build
--- firefox-126.0/toolkit/xre/moz.build.D209910	2024-05-06 22:50:25.000000000 +0200
+++ firefox-126.0/toolkit/xre/moz.build	2024-05-09 12:20:44.081565311 +0200
@@ -99,6 +99,10 @@ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "gt
     UNIFIED_SOURCES += [
         "nsNativeAppSupportUnix.cpp",
     ]
+    if CONFIG["MOZ_ENABLE_DBUS"]:
+        UNIFIED_SOURCES += [
+            "DBusService.cpp",
+        ]
     CXXFLAGS += CONFIG["MOZ_X11_SM_CFLAGS"]
 else:
     UNIFIED_SOURCES += [