summaryrefslogtreecommitdiff
path: root/mozilla-gnome-shell-search-provider.patch
diff options
context:
space:
mode:
Diffstat (limited to 'mozilla-gnome-shell-search-provider.patch')
-rw-r--r--mozilla-gnome-shell-search-provider.patch775
1 files changed, 775 insertions, 0 deletions
diff --git a/mozilla-gnome-shell-search-provider.patch b/mozilla-gnome-shell-search-provider.patch
new file mode 100644
index 0000000..b7e7b33
--- /dev/null
+++ b/mozilla-gnome-shell-search-provider.patch
@@ -0,0 +1,775 @@
+diff --git a/browser/components/shell/moz.build b/browser/components/shell/moz.build
+--- a/browser/components/shell/moz.build
++++ b/browser/components/shell/moz.build
+@@ -34,6 +34,11 @@
+ SOURCES += [
+ 'nsGNOMEShellService.cpp',
+ ]
++ if CONFIG['MOZ_ENABLE_DBUS']:
++ SOURCES += [
++ 'nsGNOMEShellSearchProvider.cpp',
++ ]
++
+ elif CONFIG['OS_ARCH'] == 'WINNT':
+ SOURCES += [
+ 'nsWindowsShellService.cpp',
+@@ -57,6 +62,8 @@
+ DEFINES[var] = '"%s"' % CONFIG[var]
+
+ CXXFLAGS += CONFIG['TK_CFLAGS']
++if CONFIG['MOZ_ENABLE_DBUS']:
++ CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
+
+ with Files('**'):
+ BUG_COMPONENT = ('Firefox', 'Shell Integration')
+diff --git a/browser/components/shell/nsGNOMEShellSearchProvider.h b/browser/components/shell/nsGNOMEShellSearchProvider.h
+new file mode 100644
+--- /dev/null
++++ b/browser/components/shell/nsGNOMEShellSearchProvider.h
+@@ -0,0 +1,53 @@
++/* -*- 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 __nsGNOMEShellSearchProvider_h__
++#define __nsGNOMEShellSearchProvider_h__
++
++#include "mozilla/DBusHelpers.h"
++#include "nsINavHistoryService.h"
++#include "nsUnixRemoteServer.h"
++#include "nsCOMPtr.h"
++
++class nsGNOMEShellSearchProvider : public nsUnixRemoteServer {
++ public:
++ nsGNOMEShellSearchProvider() : mConnection(nullptr) {}
++ ~nsGNOMEShellSearchProvider() { Shutdown(); }
++
++ nsresult Startup();
++ void Shutdown();
++
++ DBusHandlerResult HandleDBusMessage(DBusConnection* aConnection,
++ DBusMessage* msg);
++ void UnregisterDBusInterface(DBusConnection* aConnection);
++
++ private:
++ DBusHandlerResult Introspect(DBusMessage* msg);
++
++ DBusHandlerResult GetInitialResultSet(DBusMessage* msg);
++ DBusHandlerResult GetSubsearchResultSet(DBusMessage* msg);
++ DBusHandlerResult GetResultMetas(DBusMessage* msg);
++ DBusHandlerResult ActivateResult(DBusMessage* msg);
++ DBusHandlerResult LaunchSearch(DBusMessage* msg);
++
++ nsresult QueryHistory(const char* aSearchTerm);
++ bool IsHistoryResultNodeURI(nsINavHistoryResultNode* aHistoryNode);
++ void AppendResultID(DBusMessageIter* aIter, const char* aID);
++ void AppendSearchID(DBusMessageIter* aIter, const char* aID);
++ void ComposeSearchResultReply(DBusMessage* aReply, const char* aSearchTerm);
++ void LaunchWithID(const char* aID, uint32_t aTimeStamp);
++ void LaunchWithAllResults(uint32_t aTimeStamp);
++
++ // The connection is owned by DBus library
++ RefPtr<DBusConnection> mConnection;
++ nsCOMPtr<nsINavHistoryContainerResultNode> mHistResultContainer;
++ nsCOMPtr<nsINavHistoryService> mHistoryService;
++ nsAutoCStringN<32> mSearchTerm;
++ nsAutoCString mGnomeSearchTitle;
++};
++
++#endif // __nsGNOMEShellSearchProvider_h__
+diff --git a/browser/components/shell/nsGNOMEShellSearchProvider.cpp b/browser/components/shell/nsGNOMEShellSearchProvider.cpp
+new file mode 100644
+--- /dev/null
++++ b/browser/components/shell/nsGNOMEShellSearchProvider.cpp
+@@ -0,0 +1,607 @@
++/* -*- 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 "nsGNOMEShellSearchProvider.h"
++
++#include "nsIBaseWindow.h"
++#include "nsIDocShell.h"
++#include "nsPIDOMWindow.h"
++#include "mozilla/ModuleUtils.h"
++#include "mozilla/Base64.h"
++#include "nsIServiceManager.h"
++#include "nsIWidget.h"
++#include "nsIAppShellService.h"
++#include "nsAppShellCID.h"
++#include "nsPrintfCString.h"
++#include "nsCOMPtr.h"
++#include "nsGTKToolkit.h"
++#include "nsINavHistoryService.h"
++#include "nsToolkitCompsCID.h"
++#include "nsIFaviconService.h"
++#include "RemoteUtils.h"
++#include "nsIStringBundle.h"
++
++#include <dbus/dbus.h>
++#include <dbus/dbus-glib-lowlevel.h>
++
++#define MAX_SEARCH_RESULTS_NUM 9
++#define KEYWORD_SEARCH_STRING "special:search"
++#define KEYWORD_SEARCH_STRING_LEN 14
++
++#define DBUS_BUS_NAME "org.mozilla.Firefox.SearchProvider"
++#define DBUS_OBJECT_PATH "/org/mozilla/Firefox/SearchProvider"
++
++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.DBus.Introspectable\">\n"
++ " <method name=\"Introspect\">\n"
++ " <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
++ " </method>\n"
++ " </interface>\n"
++ " <interface name=\"org.gnome.Shell.SearchProvider2\">\n"
++ " <method name=\"GetInitialResultSet\">\n"
++ " <arg type=\"as\" name=\"terms\" direction=\"in\" />\n"
++ " <arg type=\"as\" name=\"results\" direction=\"out\" />\n"
++ " </method>\n"
++ " <method name=\"GetSubsearchResultSet\">\n"
++ " <arg type=\"as\" name=\"previous_results\" direction=\"in\" />\n"
++ " <arg type=\"as\" name=\"terms\" direction=\"in\" />\n"
++ " <arg type=\"as\" name=\"results\" direction=\"out\" />\n"
++ " </method>\n"
++ " <method name=\"GetResultMetas\">\n"
++ " <arg type=\"as\" name=\"identifiers\" direction=\"in\" />\n"
++ " <arg type=\"aa{sv}\" name=\"metas\" direction=\"out\" />\n"
++ " </method>\n"
++ " <method name=\"ActivateResult\">\n"
++ " <arg type=\"s\" name=\"identifier\" direction=\"in\" />\n"
++ " <arg type=\"as\" name=\"terms\" direction=\"in\" />\n"
++ " <arg type=\"u\" name=\"timestamp\" direction=\"in\" />\n"
++ " </method>\n"
++ " <method name=\"LaunchSearch\">\n"
++ " <arg type=\"as\" name=\"terms\" direction=\"in\" />\n"
++ " <arg type=\"u\" name=\"timestamp\" direction=\"in\" />\n"
++ " </method>\n"
++ "</interface>\n"
++ "</node>\n";
++
++DBusHandlerResult nsGNOMEShellSearchProvider::Introspect(DBusMessage* aMsg) {
++ DBusMessage* reply;
++
++ reply = dbus_message_new_method_return(aMsg);
++ if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY;
++
++ dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspect_template,
++ DBUS_TYPE_INVALID);
++
++ dbus_connection_send(mConnection, reply, nullptr);
++ dbus_message_unref(reply);
++
++ return DBUS_HANDLER_RESULT_HANDLED;
++}
++
++nsresult nsGNOMEShellSearchProvider::QueryHistory(const char* aSearchTerm) {
++ nsresult rv;
++ nsCOMPtr<nsINavHistoryQuery> histQuery;
++ rv = mHistoryService->GetNewQuery(getter_AddRefs(histQuery));
++ NS_ENSURE_SUCCESS(rv, rv);
++
++ nsAutoCString searchTerm(aSearchTerm);
++ rv = histQuery->SetSearchTerms(NS_ConvertUTF8toUTF16(searchTerm));
++ NS_ENSURE_SUCCESS(rv, rv);
++
++ nsCOMPtr<nsINavHistoryQueryOptions> histQueryOpts;
++ rv = mHistoryService->GetNewQueryOptions(getter_AddRefs(histQueryOpts));
++ NS_ENSURE_SUCCESS(rv, rv);
++
++ // We want to get the URIs for every item in the user's history with the
++ // given host
++ rv = histQueryOpts->SetResultType(nsINavHistoryQueryOptions::RESULTS_AS_URI);
++ NS_ENSURE_SUCCESS(rv, rv);
++
++ rv = histQueryOpts->SetSortingMode(
++ nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING);
++ NS_ENSURE_SUCCESS(rv, rv);
++
++ rv = histQueryOpts->SetMaxResults(10);
++ NS_ENSURE_SUCCESS(rv, rv);
++
++ // We only search history, because searching both bookmarks and history
++ // is not supported, and history tends to be more comprehensive.
++ rv = histQueryOpts->SetQueryType(
++ nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY);
++ NS_ENSURE_SUCCESS(rv, rv);
++
++ nsCOMPtr<nsINavHistoryResult> histResult;
++ rv = mHistoryService->ExecuteQuery(histQuery, histQueryOpts,
++ getter_AddRefs(histResult));
++ NS_ENSURE_SUCCESS(rv, rv);
++
++ // Delete former search results
++ mHistResultContainer = nullptr;
++
++ rv = histResult->GetRoot(getter_AddRefs(mHistResultContainer));
++ NS_ENSURE_SUCCESS(rv, rv);
++
++ rv = mHistResultContainer->SetContainerOpen(true);
++ NS_ENSURE_SUCCESS(rv, rv);
++
++ uint32_t childCount = 0;
++ rv = mHistResultContainer->GetChildCount(&childCount);
++ NS_ENSURE_SUCCESS(rv, rv);
++
++ return childCount != 0 ? NS_OK : NS_ERROR_FAILURE;
++}
++
++bool nsGNOMEShellSearchProvider::IsHistoryResultNodeURI(
++ nsINavHistoryResultNode* aHistoryNode) {
++ uint32_t type;
++ nsresult rv = aHistoryNode->GetType(&type);
++ if (NS_FAILED(rv) || type != nsINavHistoryResultNode::RESULT_TYPE_URI)
++ return false;
++
++ nsAutoCString title;
++ rv = aHistoryNode->GetTitle(title);
++ if (NS_SUCCEEDED(rv) && !title.IsEmpty()) {
++ return true;
++ }
++
++ rv = aHistoryNode->GetUri(title);
++ return NS_SUCCEEDED(rv) && !title.IsEmpty();
++}
++
++void nsGNOMEShellSearchProvider::ComposeSearchResultReply(
++ DBusMessage* reply, const char* aSearchTerm) {
++ uint32_t childCount = 0;
++ nsresult rv = mHistResultContainer->GetChildCount(&childCount);
++ if (NS_FAILED(rv) || childCount == 0) {
++ return;
++ }
++
++ if (childCount > MAX_SEARCH_RESULTS_NUM) {
++ childCount = MAX_SEARCH_RESULTS_NUM;
++ }
++
++ DBusMessageIter iter;
++ dbus_message_iter_init_append(reply, &iter);
++ DBusMessageIter iterArray;
++ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &iterArray);
++
++ for (uint32_t i = 0; i < childCount; i++) {
++ nsCOMPtr<nsINavHistoryResultNode> child;
++ mHistResultContainer->GetChild(i, getter_AddRefs(child));
++ if (NS_WARN_IF(NS_FAILED(rv))) {
++ continue;
++ }
++ if (!IsHistoryResultNodeURI(child)) {
++ continue;
++ }
++
++ nsAutoCString uri;
++ child->GetUri(uri);
++
++ nsPrintfCString idString("%d", i);
++ const char* id = idString.get();
++ dbus_message_iter_append_basic(&iterArray, DBUS_TYPE_STRING, &id);
++ }
++
++ nsPrintfCString searchString("%s:%s", KEYWORD_SEARCH_STRING, aSearchTerm);
++ const char* search = searchString.get();
++ dbus_message_iter_append_basic(&iterArray, DBUS_TYPE_STRING, &search);
++
++ dbus_message_iter_close_container(&iter, &iterArray);
++}
++
++DBusHandlerResult nsGNOMEShellSearchProvider::GetInitialResultSet(
++ DBusMessage* aMsg) {
++ DBusMessage* reply;
++ char** stringArray;
++ int elements;
++
++ if (!dbus_message_get_args(aMsg, nullptr, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
++ &stringArray, &elements, DBUS_TYPE_INVALID) ||
++ elements == 0) {
++ reply = dbus_message_new_error(aMsg, DBUS_BUS_NAME, "Wrong argument");
++ } else {
++ reply = dbus_message_new_method_return(aMsg);
++ nsresult rv = QueryHistory(stringArray[0]);
++ if (NS_SUCCEEDED(rv)) {
++ ComposeSearchResultReply(reply, stringArray[0]);
++ }
++ dbus_free_string_array(stringArray);
++ }
++
++ dbus_connection_send(mConnection, reply, nullptr);
++ dbus_message_unref(reply);
++
++ return DBUS_HANDLER_RESULT_HANDLED;
++}
++
++DBusHandlerResult nsGNOMEShellSearchProvider::GetSubsearchResultSet(
++ DBusMessage* aMsg) {
++ DBusMessage* reply;
++
++ char **unusedArray = nullptr, **stringArray = nullptr;
++ int unusedNum, elements;
++
++ if (!dbus_message_get_args(aMsg, nullptr, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
++ &unusedArray, &unusedNum, DBUS_TYPE_ARRAY,
++ DBUS_TYPE_STRING, &stringArray, &elements,
++ DBUS_TYPE_INVALID) ||
++ elements == 0) {
++ reply = dbus_message_new_error(aMsg, DBUS_BUS_NAME, "Wrong argument");
++ } else {
++ reply = dbus_message_new_method_return(aMsg);
++ nsresult rv = QueryHistory(stringArray[0]);
++ if (NS_SUCCEEDED(rv)) {
++ ComposeSearchResultReply(reply, stringArray[0]);
++ }
++ }
++
++ if (unusedArray) {
++ dbus_free_string_array(unusedArray);
++ }
++ if (stringArray) {
++ dbus_free_string_array(stringArray);
++ }
++
++ dbus_connection_send(mConnection, reply, nullptr);
++ dbus_message_unref(reply);
++
++ return DBUS_HANDLER_RESULT_HANDLED;
++}
++
++static void appendStringDictionary(DBusMessageIter* aIter, const char* aKey,
++ const char* aValue) {
++ DBusMessageIter iterDict, iterVar;
++ dbus_message_iter_open_container(aIter, DBUS_TYPE_DICT_ENTRY, nullptr,
++ &iterDict);
++ dbus_message_iter_append_basic(&iterDict, DBUS_TYPE_STRING, &aKey);
++ dbus_message_iter_open_container(&iterDict, DBUS_TYPE_VARIANT, "s", &iterVar);
++ dbus_message_iter_append_basic(&iterVar, DBUS_TYPE_STRING, &aValue);
++ dbus_message_iter_close_container(&iterDict, &iterVar);
++ dbus_message_iter_close_container(aIter, &iterDict);
++}
++
++/* We can return those fields at GetResultMetas:
++ "id": the result ID
++ "name": the display name for the result
++ "icon": a serialized GIcon (see g_icon_serialize()), or alternatively,
++ "gicon": a textual representation of a GIcon (see g_icon_to_string()),
++ or alternativly,
++ "icon-data": a tuple of type (iiibiiay) describing a pixbuf with width,
++ height, rowstride, has-alpha, bits-per-sample, and image data
++ "description": an optional short description (1-2 lines)
++*/
++void nsGNOMEShellSearchProvider::AppendResultID(DBusMessageIter* aIter,
++ const char* aID) {
++ int keyIndex = atoi(aID);
++ nsCOMPtr<nsINavHistoryResultNode> child;
++ mHistResultContainer->GetChild(keyIndex, getter_AddRefs(child));
++
++ nsAutoCString title;
++ if (NS_FAILED(child->GetTitle(title))) {
++ return;
++ }
++
++ if (title.IsEmpty()) {
++ if (NS_FAILED(child->GetUri(title)) || title.IsEmpty()) {
++ return;
++ }
++ }
++
++ const char* titleStr = title.get();
++ appendStringDictionary(aIter, "id", aID);
++ appendStringDictionary(aIter, "name", titleStr);
++ appendStringDictionary(aIter, "gicon", "text-html");
++}
++
++void nsGNOMEShellSearchProvider::AppendSearchID(DBusMessageIter* aIter,
++ const char* aID) {
++ if (strlen(aID) < KEYWORD_SEARCH_STRING_LEN + 2) {
++ return;
++ }
++ appendStringDictionary(aIter, "id", KEYWORD_SEARCH_STRING);
++ mSearchTerm = nsAutoCStringN<32>(aID + KEYWORD_SEARCH_STRING_LEN + 1);
++ nsPrintfCString searchString(mGnomeSearchTitle.get(), mSearchTerm.get());
++ appendStringDictionary(aIter, "name", searchString.get());
++ appendStringDictionary(aIter, "gicon", "org.mozilla.Firefox");
++}
++
++DBusHandlerResult nsGNOMEShellSearchProvider::GetResultMetas(
++ DBusMessage* aMsg) {
++ DBusMessage* reply;
++ char** stringArray;
++ int elements;
++
++ if (!dbus_message_get_args(aMsg, nullptr, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
++ &stringArray, &elements, DBUS_TYPE_INVALID) ||
++ elements == 0) {
++ reply = dbus_message_new_error(aMsg, DBUS_BUS_NAME, "Wrong argument");
++ } else {
++ reply = dbus_message_new_method_return(aMsg);
++
++ DBusMessageIter iter;
++ dbus_message_iter_init_append(reply, &iter);
++ DBusMessageIter iterArray;
++ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "a{sv}",
++ &iterArray);
++
++ DBusMessageIter iterArray2;
++ for (int i = 0; i < elements; i++) {
++ dbus_message_iter_open_container(&iterArray, DBUS_TYPE_ARRAY, "{sv}",
++ &iterArray2);
++ if (strncmp(stringArray[i], KEYWORD_SEARCH_STRING,
++ KEYWORD_SEARCH_STRING_LEN) == 0) {
++ AppendSearchID(&iterArray2, stringArray[i]);
++ } else {
++ AppendResultID(&iterArray2, stringArray[i]);
++ }
++ dbus_message_iter_close_container(&iterArray, &iterArray2);
++ }
++
++ dbus_message_iter_close_container(&iter, &iterArray);
++ dbus_free_string_array(stringArray);
++ }
++
++ dbus_connection_send(mConnection, reply, nullptr);
++ dbus_message_unref(reply);
++
++ return DBUS_HANDLER_RESULT_HANDLED;
++}
++
++void nsGNOMEShellSearchProvider::LaunchWithID(const char* aID,
++ uint32_t aTimeStamp) {
++ int keyIndex = atoi(aID);
++ nsCOMPtr<nsINavHistoryResultNode> child;
++ mHistResultContainer->GetChild(keyIndex, getter_AddRefs(child));
++
++ nsAutoCString uri;
++ nsresult rv = child->GetUri(uri);
++ if (NS_FAILED(rv)) {
++ return;
++ }
++
++ char* commandLine = nullptr;
++ int tmp;
++
++ if (strncmp(aID, KEYWORD_SEARCH_STRING, KEYWORD_SEARCH_STRING_LEN) == 0) {
++ nsPrintfCString searchString("search:%s", mSearchTerm.get());
++ const char* urlList[2] = {"unused", searchString.get()};
++ commandLine = ConstructCommandLine(2, (char**)urlList, 0, &tmp);
++ } else {
++ const char* urlList[2] = {"unused", uri.get()};
++ commandLine = ConstructCommandLine(2, (char**)urlList, 0, &tmp);
++ }
++
++ if (commandLine) {
++ HandleCommandLine(commandLine, aTimeStamp);
++ free(commandLine);
++ }
++}
++
++void nsGNOMEShellSearchProvider::LaunchWithAllResults(uint32_t aTimeStamp) {
++ uint32_t childCount = 0;
++ nsresult rv = mHistResultContainer->GetChildCount(&childCount);
++ if (NS_FAILED(rv) || childCount == 0) {
++ return;
++ }
++
++ if (childCount > MAX_SEARCH_RESULTS_NUM) {
++ childCount = MAX_SEARCH_RESULTS_NUM;
++ }
++
++ char** urlList = (char**)moz_xmalloc(sizeof(char*) * (childCount + 2));
++ int urlListElements = 0;
++
++ urlList[urlListElements++] = strdup("unused");
++
++ for (uint32_t i = 0; i < childCount; i++) {
++ nsCOMPtr<nsINavHistoryResultNode> child;
++ mHistResultContainer->GetChild(i, getter_AddRefs(child));
++
++ if (!IsHistoryResultNodeURI(child)) {
++ continue;
++ }
++
++ nsAutoCString uri;
++ nsresult rv = child->GetUri(uri);
++ if (NS_FAILED(rv)) {
++ continue;
++ }
++ urlList[urlListElements++] = strdup(uri.get());
++ }
++
++ nsPrintfCString searchString("search:%s", mSearchTerm.get());
++ urlList[urlListElements++] = strdup(searchString.get());
++
++ int tmp;
++ char* commandLine = ConstructCommandLine(urlListElements, urlList, 0, &tmp);
++ if (commandLine) {
++ HandleCommandLine(commandLine, aTimeStamp);
++ free(commandLine);
++ }
++
++ for (int i = 0; i < urlListElements; i++) {
++ free(urlList[i]);
++ }
++ free(urlList);
++}
++
++DBusHandlerResult nsGNOMEShellSearchProvider::ActivateResult(
++ DBusMessage* aMsg) {
++ DBusMessage* reply;
++ char* resultID;
++ char** stringArray;
++ int elements;
++ uint32_t timestamp;
++
++ if (!dbus_message_get_args(aMsg, nullptr, DBUS_TYPE_STRING, &resultID,
++ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &stringArray,
++ &elements, DBUS_TYPE_UINT32, &timestamp,
++ DBUS_TYPE_INVALID) ||
++ resultID == nullptr) {
++ reply = dbus_message_new_error(aMsg, DBUS_BUS_NAME, "Wrong argument");
++ } else {
++ reply = dbus_message_new_method_return(aMsg);
++ LaunchWithID(resultID, timestamp);
++ dbus_free_string_array(stringArray);
++ }
++
++ dbus_connection_send(mConnection, reply, nullptr);
++ dbus_message_unref(reply);
++
++ return DBUS_HANDLER_RESULT_HANDLED;
++}
++
++DBusHandlerResult nsGNOMEShellSearchProvider::LaunchSearch(DBusMessage* aMsg) {
++ DBusMessage* reply;
++ char** stringArray;
++ int elements;
++ uint32_t timestamp;
++
++ if (!dbus_message_get_args(aMsg, nullptr, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
++ &stringArray, &elements, DBUS_TYPE_UINT32,
++ &timestamp, DBUS_TYPE_INVALID) ||
++ elements == 0) {
++ reply = dbus_message_new_error(aMsg, DBUS_BUS_NAME, "Wrong argument");
++ } else {
++ reply = dbus_message_new_method_return(aMsg);
++ LaunchWithAllResults(timestamp);
++ dbus_free_string_array(stringArray);
++ }
++
++ dbus_connection_send(mConnection, reply, nullptr);
++ dbus_message_unref(reply);
++
++ return DBUS_HANDLER_RESULT_HANDLED;
++}
++
++DBusHandlerResult nsGNOMEShellSearchProvider::HandleDBusMessage(
++ DBusConnection* aConnection, DBusMessage* aMsg) {
++ NS_ASSERTION(mConnection == aConnection, "Wrong D-Bus connection.");
++
++ const char* method = dbus_message_get_member(aMsg);
++ const char* iface = dbus_message_get_interface(aMsg);
++
++ if ((strcmp("Introspect", method) == 0) &&
++ (strcmp("org.freedesktop.DBus.Introspectable", iface) == 0)) {
++ return Introspect(aMsg);
++ }
++
++ if (strcmp("org.gnome.Shell.SearchProvider2", iface) == 0) {
++ if (strcmp("GetInitialResultSet", method) == 0) {
++ return GetInitialResultSet(aMsg);
++ }
++ if (strcmp("GetSubsearchResultSet", method) == 0) {
++ return GetSubsearchResultSet(aMsg);
++ }
++ if (strcmp("GetResultMetas", method) == 0) {
++ return GetResultMetas(aMsg);
++ }
++ if (strcmp("ActivateResult", method) == 0) {
++ return ActivateResult(aMsg);
++ }
++ if (strcmp("LaunchSearch", method) == 0) {
++ return LaunchSearch(aMsg);
++ }
++ }
++
++ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
++}
++
++void nsGNOMEShellSearchProvider::UnregisterDBusInterface(
++ DBusConnection* aConnection) {
++ NS_ASSERTION(mConnection == aConnection, "Wrong D-Bus connection.");
++ // Not implemented
++}
++
++static DBusHandlerResult message_handler(DBusConnection* conn,
++ DBusMessage* aMsg, void* user_data) {
++ auto interface = static_cast<nsGNOMEShellSearchProvider*>(user_data);
++ return interface->HandleDBusMessage(conn, aMsg);
++}
++
++static void unregister(DBusConnection* conn, void* user_data) {
++ auto interface = static_cast<nsGNOMEShellSearchProvider*>(user_data);
++ interface->UnregisterDBusInterface(conn);
++}
++
++static DBusObjectPathVTable remoteHandlersTable = {
++ .unregister_function = unregister,
++ .message_function = message_handler,
++};
++
++nsresult nsGNOMEShellSearchProvider::Startup() {
++ if (mConnection && dbus_connection_get_is_connected(mConnection)) {
++ // We're already connected so we don't need to reconnect
++ return NS_ERROR_ALREADY_INITIALIZED;
++ }
++
++ nsCOMPtr<nsIStringBundleService> sbs =
++ do_GetService(NS_STRINGBUNDLE_CONTRACTID);
++ if (NS_WARN_IF(!sbs)) {
++ return NS_ERROR_FAILURE;
++ }
++
++ nsCOMPtr<nsIStringBundle> bundle;
++ sbs->CreateBundle("chrome://browser/locale/browser.properties",
++ getter_AddRefs(bundle));
++ if (NS_WARN_IF(!bundle)) {
++ return NS_ERROR_FAILURE;
++ }
++
++ nsAutoString searchTitle;
++ bundle->GetStringFromName("gnomeSearchProviderSearch", searchTitle);
++ mGnomeSearchTitle = NS_ConvertUTF16toUTF8(searchTitle);
++
++ mHistoryService = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
++ if (!mHistoryService) {
++ return NS_ERROR_FAILURE;
++ }
++
++ 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);
++ dbus_connection_setup_with_g_main(mConnection, nullptr);
++
++ DBusError err;
++ dbus_error_init(&err);
++ dbus_bus_request_name(mConnection, DBUS_BUS_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE,
++ &err);
++ // The interface is already owned - there is another application/profile
++ // instance already running.
++ if (dbus_error_is_set(&err)) {
++ dbus_error_free(&err);
++ mConnection = nullptr;
++ return NS_ERROR_FAILURE;
++ }
++
++ if (!dbus_connection_register_object_path(mConnection, DBUS_OBJECT_PATH,
++ &remoteHandlersTable, this)) {
++ mConnection = nullptr;
++ return NS_ERROR_FAILURE;
++ }
++
++ return NS_OK;
++}
++
++void nsGNOMEShellSearchProvider::Shutdown() {
++ if (!mConnection) {
++ return;
++ }
++
++ dbus_connection_unregister_object_path(mConnection, DBUS_OBJECT_PATH);
++
++ // dbus_connection_unref() will be called by RefPtr here.
++ mConnection = nullptr;
++}
+diff --git a/browser/components/shell/nsGNOMEShellService.h b/browser/components/shell/nsGNOMEShellService.h
+--- a/browser/components/shell/nsGNOMEShellService.h
++++ b/browser/components/shell/nsGNOMEShellService.h
+@@ -10,6 +10,9 @@
+ #include "nsToolkitShellService.h"
+ #include "nsString.h"
+ #include "mozilla/Attributes.h"
++#ifdef MOZ_ENABLE_DBUS
++# include "nsGNOMEShellSearchProvider.h"
++#endif
+
+ class nsGNOMEShellService final : public nsIGNOMEShellService,
+ public nsToolkitShellService {
+@@ -28,6 +31,9 @@
+ bool KeyMatchesAppName(const char* aKeyValue) const;
+ bool CheckHandlerMatchesAppName(const nsACString& handler) const;
+
++#ifdef MOZ_ENABLE_DBUS
++ nsGNOMEShellSearchProvider mSearchProvider;
++#endif
+ bool GetAppPathFromLauncher();
+ bool mUseLocaleFilenames;
+ nsCString mAppPath;
+diff --git a/browser/components/shell/nsGNOMEShellService.cpp b/browser/components/shell/nsGNOMEShellService.cpp
+--- a/browser/components/shell/nsGNOMEShellService.cpp
++++ b/browser/components/shell/nsGNOMEShellService.cpp
+@@ -107,7 +107,15 @@
+ getter_AddRefs(appPath));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+- return appPath->GetNativePath(mAppPath);
++ rv = appPath->GetNativePath(mAppPath);
++ NS_ENSURE_SUCCESS(rv, rv);
++
++#ifdef MOZ_ENABLE_DBUS
++ if (Preferences::GetBool("widget.gnome-search-provider.enabled", false)) {
++ mSearchProvider.Startup();
++ }
++#endif
++ return NS_OK;
+ }
+
+ NS_IMPL_ISUPPORTS(nsGNOMEShellService, nsIGNOMEShellService, nsIShellService,
+diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties
+--- a/browser/locales/en-US/chrome/browser/browser.properties
++++ b/browser/locales/en-US/chrome/browser/browser.properties
+@@ -1026,3 +1026,7 @@
+ # Used by the export of user's live bookmarks to an OPML file as a title for the file.
+ # %S will be replaced with brandShortName
+ livebookmarkMigration.title = %S Live Bookmarks
++
++# LOCALIZATION NOTE (gnomeSearchProviderSearch):
++# Used for search by Gnome Shell activity screen, %s is a searched string.
++gnomeSearchProviderSearch=Search the web for ā€œ%sā€
+diff --git a/toolkit/components/remote/moz.build b/toolkit/components/remote/moz.build
+--- a/toolkit/components/remote/moz.build
++++ b/toolkit/components/remote/moz.build
+@@ -25,6 +25,10 @@
+ 'nsDBusRemoteServer.cpp',
+ ]
+ CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
++ EXPORTS += [
++ 'nsUnixRemoteServer.h',
++ 'RemoteUtils.h',
++ ]
+ CXXFLAGS += CONFIG['TK_CFLAGS']
+
+ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+diff --git a/toolkit/components/remote/nsDBusRemoteServer.cpp b/toolkit/components/remote/nsDBusRemoteServer.cpp
+--- a/toolkit/components/remote/nsDBusRemoteServer.cpp
++++ b/toolkit/components/remote/nsDBusRemoteServer.cpp
+@@ -27,7 +27,7 @@
+
+ #include <dlfcn.h>
+
+-const char* introspect_template =
++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"
+
bgstack15