diff options
Diffstat (limited to 'browser/components/shell/nsGNOMEShellDBusHelper.cpp')
-rw-r--r-- | browser/components/shell/nsGNOMEShellDBusHelper.cpp | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/browser/components/shell/nsGNOMEShellDBusHelper.cpp b/browser/components/shell/nsGNOMEShellDBusHelper.cpp new file mode 100644 index 0000000000..9e1681c902 --- /dev/null +++ b/browser/components/shell/nsGNOMEShellDBusHelper.cpp @@ -0,0 +1,535 @@ +/* -*- 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 "RemoteUtils.h" +#include "nsIStringBundle.h" +#include "nsServiceManagerUtils.h" +#include "nsPrintfCString.h" +#include "mozilla/XREAppData.h" +#include "nsAppRunner.h" + +#define DBUS_BUS_NAME_TEMPLATE "org.mozilla.%s.SearchProvider" +#define DBUS_OBJECT_PATH_TEMPLATE "/org/mozilla/%s/SearchProvider" + +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; +} + +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 bool GetGnomeSearchTitle(const char* aSearchedTerm, + nsAutoCString& aGnomeSearchTitle) { + static nsCOMPtr<nsIStringBundle> bundle; + if (!bundle) { + nsCOMPtr<nsIStringBundleService> sbs = + do_GetService(NS_STRINGBUNDLE_CONTRACTID); + if (NS_WARN_IF(!sbs)) { + return false; + } + + sbs->CreateBundle("chrome://browser/locale/browser.properties", + getter_AddRefs(bundle)); + if (NS_WARN_IF(!bundle)) { + return false; + } + } + + AutoTArray<nsString, 1> formatStrings; + CopyUTF8toUTF16(nsCString(aSearchedTerm), *formatStrings.AppendElement()); + + nsAutoString gnomeSearchTitle; + bundle->FormatStringFromName("gnomeSearchProviderSearchWeb", formatStrings, + gnomeSearchTitle); + AppendUTF16toUTF8(gnomeSearchTitle, aGnomeSearchTitle); + return true; +} + +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 DBusIntrospect(DBusConnection* aConnection, + 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(aConnection, reply, nullptr); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +int DBusGetIndexFromIDKey(const char* aIDKey) { + // ID is NN:URL where NN is index to our current history + // result container. + char tmp[] = {aIDKey[0], aIDKey[1], '\0'}; + return atoi(tmp); +} + +static void ConcatArray(nsACString& aOutputStr, const char** aStringArray) { + for (const char** term = aStringArray; *term; term++) { + aOutputStr.Append(*term); + if (*(term + 1)) { + aOutputStr.Append(" "); + } + } +} + +DBusHandlerResult DBusHandleInitialResultSet( + RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult, DBusMessage* aMsg) { + DBusMessage* reply; + char** stringArray = nullptr; + 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, GetDBusBusName(), "Wrong argument"); + dbus_connection_send(aSearchResult->GetDBusConnection(), reply, nullptr); + dbus_message_unref(reply); + } else { + aSearchResult->SetReply(dbus_message_new_method_return(aMsg)); + nsAutoCString searchTerm; + ConcatArray(searchTerm, const_cast<const char**>(stringArray)); + aSearchResult->SetSearchTerm(searchTerm.get()); + GetGNOMEShellHistoryService()->QueryHistory(aSearchResult); + // DBus reply will be send asynchronously by + // nsGNOMEShellHistorySearchResult::SendDBusSearchResultReply() + // when GetGNOMEShellHistoryService() has the results. + } + + if (stringArray) { + dbus_free_string_array(stringArray); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult DBusHandleSubsearchResultSet( + RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult, 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, GetDBusBusName(), "Wrong argument"); + dbus_connection_send(aSearchResult->GetDBusConnection(), reply, nullptr); + dbus_message_unref(reply); + } else { + aSearchResult->SetReply(dbus_message_new_method_return(aMsg)); + nsAutoCString searchTerm; + ConcatArray(searchTerm, const_cast<const char**>(stringArray)); + aSearchResult->SetSearchTerm(searchTerm.get()); + GetGNOMEShellHistoryService()->QueryHistory(aSearchResult); + // DBus reply will be send asynchronously by + // nsGNOMEShellHistorySearchResult::SendDBusSearchResultReply() + // when GetGNOMEShellHistoryService() has the results. + } + + if (unusedArray) { + dbus_free_string_array(unusedArray); + } + if (stringArray) { + dbus_free_string_array(stringArray); + } + + 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); +} + +/* + "icon-data": a tuple of type (iiibiiay) describing a pixbuf with width, + height, rowstride, has-alpha, + bits-per-sample, channels, + image data +*/ +static void DBusAppendIcon(GnomeHistoryIcon* aIcon, DBusMessageIter* aIter) { + DBusMessageIter iterDict, iterVar, iterStruct; + dbus_message_iter_open_container(aIter, DBUS_TYPE_DICT_ENTRY, nullptr, + &iterDict); + const char* key = "icon-data"; + dbus_message_iter_append_basic(&iterDict, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&iterDict, DBUS_TYPE_VARIANT, "(iiibiiay)", + &iterVar); + dbus_message_iter_open_container(&iterVar, DBUS_TYPE_STRUCT, nullptr, + &iterStruct); + + int width = aIcon->GetWidth(); + int height = aIcon->GetHeight(); + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_INT32, &width); + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_INT32, &height); + int rowstride = width * 4; + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_INT32, &rowstride); + int hasAlpha = true; + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_BOOLEAN, &hasAlpha); + int bitsPerSample = 8; + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_INT32, &bitsPerSample); + int channels = 4; + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_INT32, &channels); + + DBusMessageIter iterArray; + dbus_message_iter_open_container(&iterStruct, DBUS_TYPE_ARRAY, "y", + &iterArray); + unsigned char* array = aIcon->GetData(); + dbus_message_iter_append_fixed_array(&iterArray, DBUS_TYPE_BYTE, &array, + width * height * 4); + dbus_message_iter_close_container(&iterStruct, &iterArray); + + dbus_message_iter_close_container(&iterVar, &iterStruct); + dbus_message_iter_close_container(&iterDict, &iterVar); + dbus_message_iter_close_container(aIter, &iterDict); +} + +/* Appends history search results to the DBUS reply. + + 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) +*/ +static void DBusAppendResultID( + RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult, + DBusMessageIter* aIter, const char* aID) { + nsCOMPtr<nsINavHistoryContainerResultNode> container = + aSearchResult->GetSearchResultContainer(); + + int index = DBusGetIndexFromIDKey(aID); + nsCOMPtr<nsINavHistoryResultNode> child; + container->GetChild(index, 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); + + GnomeHistoryIcon* icon = aSearchResult->GetHistoryIcon(index); + if (icon) { + DBusAppendIcon(icon, aIter); + } else { + appendStringDictionary(aIter, "gicon", "text-html"); + } +} + +/* Search the web for: "searchTerm" to the DBUS reply. + */ +static void DBusAppendSearchID(DBusMessageIter* aIter, const char* aID) { + /* aID contains: + + KEYWORD_SEARCH_STRING:ssssss + + KEYWORD_SEARCH_STRING is a 'special:search' keyword + ssssss is a searched term, must be at least one character long + */ + + // aID contains only 'KEYWORD_SEARCH_STRING:' so we're missing searched + // string. + if (strlen(aID) <= KEYWORD_SEARCH_STRING_LEN + 1) { + return; + } + + appendStringDictionary(aIter, "id", KEYWORD_SEARCH_STRING); + + // Extract ssssss part from aID + auto searchTerm = nsAutoCStringN<32>(aID + KEYWORD_SEARCH_STRING_LEN + 1); + nsAutoCString gnomeSearchTitle; + if (GetGnomeSearchTitle(searchTerm.get(), gnomeSearchTitle)) { + appendStringDictionary(aIter, "name", gnomeSearchTitle.get()); + // TODO: When running on flatpak/snap we may need to use + // icon like org.mozilla.Firefox or so. + appendStringDictionary(aIter, "gicon", "firefox"); + } +} + +DBusHandlerResult DBusHandleResultMetas( + RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult, 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, GetDBusBusName(), "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) { + DBusAppendSearchID(&iterArray2, stringArray[i]); + } else { + DBusAppendResultID(aSearchResult, &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(aSearchResult->GetDBusConnection(), reply, nullptr); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} // namespace mozilla + +static void ActivateResultID( + RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult, + const char* aResultID, uint32_t aTimeStamp) { + char* commandLine = nullptr; + int tmp; + + if (strncmp(aResultID, KEYWORD_SEARCH_STRING, KEYWORD_SEARCH_STRING_LEN) == + 0) { + const char* urlList[3] = {"unused", "--search", + aSearchResult->GetSearchTerm().get()}; + commandLine = ConstructCommandLine(std::size(urlList), (char**)urlList, + nullptr, &tmp); + } else { + int keyIndex = atoi(aResultID); + nsCOMPtr<nsINavHistoryResultNode> child; + aSearchResult->GetSearchResultContainer()->GetChild(keyIndex, + getter_AddRefs(child)); + if (!child) { + return; + } + + nsAutoCString uri; + nsresult rv = child->GetUri(uri); + if (NS_FAILED(rv)) { + return; + } + + const char* urlList[2] = {"unused", uri.get()}; + commandLine = ConstructCommandLine(std::size(urlList), (char**)urlList, + nullptr, &tmp); + } + + if (commandLine) { + aSearchResult->HandleCommandLine(commandLine, aTimeStamp); + free(commandLine); + } +} + +static void DBusLaunchWithAllResults( + RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult, + uint32_t aTimeStamp) { + uint32_t childCount = 0; + nsresult rv = + aSearchResult->GetSearchResultContainer()->GetChildCount(&childCount); + if (NS_FAILED(rv) || childCount == 0) { + return; + } + + if (childCount > MAX_SEARCH_RESULTS_NUM) { + childCount = MAX_SEARCH_RESULTS_NUM; + } + + // Allocate space for all found results, "unused", "--search" and + // potential search request. + char** urlList = (char**)moz_xmalloc(sizeof(char*) * (childCount + 3)); + int urlListElements = 0; + + urlList[urlListElements++] = strdup("unused"); + + for (uint32_t i = 0; i < childCount; i++) { + nsCOMPtr<nsINavHistoryResultNode> child; + aSearchResult->GetSearchResultContainer()->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()); + } + + // When there isn't any uri to open pass search at least. + if (!childCount) { + urlList[urlListElements++] = strdup("--search"); + urlList[urlListElements++] = strdup(aSearchResult->GetSearchTerm().get()); + } + + int tmp; + char* commandLine = + ConstructCommandLine(urlListElements, urlList, nullptr, &tmp); + if (commandLine) { + aSearchResult->HandleCommandLine(commandLine, aTimeStamp); + free(commandLine); + } + + for (int i = 0; i < urlListElements; i++) { + free(urlList[i]); + } + free(urlList); +} + +DBusHandlerResult DBusActivateResult( + RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult, 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, ×tamp, + DBUS_TYPE_INVALID) || + resultID == nullptr) { + reply = dbus_message_new_error(aMsg, GetDBusBusName(), "Wrong argument"); + } else { + reply = dbus_message_new_method_return(aMsg); + ActivateResultID(aSearchResult, resultID, timestamp); + dbus_free_string_array(stringArray); + } + + dbus_connection_send(aSearchResult->GetDBusConnection(), reply, nullptr); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult DBusLaunchSearch( + RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult, 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, + ×tamp, DBUS_TYPE_INVALID) || + elements == 0) { + reply = dbus_message_new_error(aMsg, GetDBusBusName(), "Wrong argument"); + } else { + reply = dbus_message_new_method_return(aMsg); + DBusLaunchWithAllResults(aSearchResult, timestamp); + dbus_free_string_array(stringArray); + } + + dbus_connection_send(aSearchResult->GetDBusConnection(), reply, nullptr); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +bool 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(); +} |