forked from mirrors/gecko-dev
		
	 fed4a076d6
			
		
	
	
		fed4a076d6
		
	
	
	
	
		
			
			Another bug caused by GObject floating references :') The GVariant returned by builder_end() is floating, and the way g_variant_builder_add_value works is that it _only_ takes a reference if floating. So basically if you have a RefPtr<GVariant> with a floating reference, then pass it to g_variant_builder_add_value, the reference is "stolen", but RefPtr doesn't know about it. It really sucks. Differential Revision: https://phabricator.services.mozilla.com/D190240
		
			
				
	
	
		
			397 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			397 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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;
 | |
| }
 | |
| 
 | |
| 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(" ");
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| // GetInitialResultSet :: (as) → (as)
 | |
| // GetSubsearchResultSet :: (as,as) → (as)
 | |
| void DBusHandleResultSet(RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
 | |
|                          GVariant* aParameters, bool aInitialSearch,
 | |
|                          GDBusMethodInvocation* aReply) {
 | |
|   // Inital search has params (as), any following one has (as,as) and we want
 | |
|   // the second string array.
 | |
|   fprintf(stderr, "%s\n", g_variant_get_type_string(aParameters));
 | |
|   RefPtr<GVariant> variant = dont_AddRef(
 | |
|       g_variant_get_child_value(aParameters, aInitialSearch ? 0 : 1));
 | |
|   const char** stringArray = g_variant_get_strv(variant, nullptr);
 | |
|   if (!stringArray) {
 | |
|     g_dbus_method_invocation_return_error(
 | |
|         aReply, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Wrong params!");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   aSearchResult->SetReply(aReply);
 | |
|   nsAutoCString searchTerm;
 | |
|   ConcatArray(searchTerm, stringArray);
 | |
|   aSearchResult->SetSearchTerm(searchTerm.get());
 | |
|   GetGNOMEShellHistoryService()->QueryHistory(aSearchResult);
 | |
|   // DBus reply will be send asynchronously by
 | |
|   // nsGNOMEShellHistorySearchResult::SendDBusSearchResultReply()
 | |
|   // when GetGNOMEShellHistoryService() has the results.
 | |
| 
 | |
|   g_free(stringArray);
 | |
| }
 | |
| 
 | |
| /*
 | |
|   "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(GVariantBuilder* aBuilder, GnomeHistoryIcon* aIcon) {
 | |
|   GVariantBuilder b;
 | |
|   g_variant_builder_init(&b, G_VARIANT_TYPE("(iiibiiay)"));
 | |
|   g_variant_builder_add_value(&b, g_variant_new_int32(aIcon->GetWidth()));
 | |
|   g_variant_builder_add_value(&b, g_variant_new_int32(aIcon->GetHeight()));
 | |
|   g_variant_builder_add_value(&b, g_variant_new_int32(aIcon->GetWidth() * 4));
 | |
|   g_variant_builder_add_value(&b, g_variant_new_boolean(true));
 | |
|   g_variant_builder_add_value(&b, g_variant_new_int32(8));
 | |
|   g_variant_builder_add_value(&b, g_variant_new_int32(4));
 | |
|   g_variant_builder_add_value(
 | |
|       &b, g_variant_new_fixed_array(G_VARIANT_TYPE("y"), aIcon->GetData(),
 | |
|                                     aIcon->GetWidth() * aIcon->GetHeight() * 4,
 | |
|                                     sizeof(char)));
 | |
|   g_variant_builder_add(aBuilder, "{sv}", "icon-data",
 | |
|                         g_variant_builder_end(&b));
 | |
| }
 | |
| 
 | |
| /* 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 already_AddRefed<GVariant> DBusAppendResultID(
 | |
|     nsGNOMEShellHistorySearchResult* aSearchResult, const char* aID) {
 | |
|   nsCOMPtr<nsINavHistoryContainerResultNode> container =
 | |
|       aSearchResult->GetSearchResultContainer();
 | |
| 
 | |
|   int index = DBusGetIndexFromIDKey(aID);
 | |
|   nsCOMPtr<nsINavHistoryResultNode> child;
 | |
|   container->GetChild(index, getter_AddRefs(child));
 | |
|   nsAutoCString title;
 | |
|   if (!child || NS_FAILED(child->GetTitle(title))) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (title.IsEmpty()) {
 | |
|     if (NS_FAILED(child->GetUri(title)) || title.IsEmpty()) {
 | |
|       return nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   GVariantBuilder b;
 | |
|   g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}"));
 | |
| 
 | |
|   const char* titleStr = title.get();
 | |
|   g_variant_builder_add(&b, "{sv}", "id", g_variant_new_string(aID));
 | |
|   g_variant_builder_add(&b, "{sv}", "name", g_variant_new_string(titleStr));
 | |
| 
 | |
|   GnomeHistoryIcon* icon = aSearchResult->GetHistoryIcon(index);
 | |
|   if (icon) {
 | |
|     DBusAppendIcon(&b, icon);
 | |
|   } else {
 | |
|     g_variant_builder_add(&b, "{sv}", "gicon",
 | |
|                           g_variant_new_string("text-html"));
 | |
|   }
 | |
|   return dont_AddRef(g_variant_ref_sink(g_variant_builder_end(&b)));
 | |
| }
 | |
| 
 | |
| // Search the web for: "searchTerm" to the DBUS reply.
 | |
| static already_AddRefed<GVariant> DBusAppendSearchID(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 nullptr;
 | |
|   }
 | |
| 
 | |
|   GVariantBuilder b;
 | |
|   g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}"));
 | |
|   g_variant_builder_add(&b, "{sv}", "id",
 | |
|                         g_variant_new_string(KEYWORD_SEARCH_STRING));
 | |
| 
 | |
|   // Extract ssssss part from aID
 | |
|   nsAutoCString searchTerm(aID + KEYWORD_SEARCH_STRING_LEN + 1);
 | |
|   nsAutoCString gnomeSearchTitle;
 | |
|   if (GetGnomeSearchTitle(searchTerm.get(), gnomeSearchTitle)) {
 | |
|     g_variant_builder_add(&b, "{sv}", "name",
 | |
|                           g_variant_new_string(gnomeSearchTitle.get()));
 | |
|     // TODO: When running on flatpak/snap we may need to use
 | |
|     // icon like org.mozilla.Firefox or so.
 | |
|     g_variant_builder_add(&b, "{sv}", "gicon", g_variant_new_string("firefox"));
 | |
|   }
 | |
| 
 | |
|   return dont_AddRef(g_variant_ref_sink(g_variant_builder_end(&b)));
 | |
| }
 | |
| 
 | |
| // GetResultMetas :: (as) → (aa{sv})
 | |
| void DBusHandleResultMetas(
 | |
|     RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
 | |
|     GVariant* aParameters, GDBusMethodInvocation* aReply) {
 | |
|   RefPtr<GVariant> variant =
 | |
|       dont_AddRef(g_variant_get_child_value(aParameters, 0));
 | |
|   gsize elements;
 | |
|   const char** stringArray = g_variant_get_strv(variant, &elements);
 | |
|   if (!stringArray) {
 | |
|     g_dbus_method_invocation_return_error(
 | |
|         aReply, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Wrong params!");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   GVariantBuilder b;
 | |
|   g_variant_builder_init(&b, G_VARIANT_TYPE("aa{sv}"));
 | |
|   for (gsize i = 0; i < elements; i++) {
 | |
|     RefPtr<GVariant> value;
 | |
|     if (strncmp(stringArray[i], KEYWORD_SEARCH_STRING,
 | |
|                 KEYWORD_SEARCH_STRING_LEN) == 0) {
 | |
|       value = DBusAppendSearchID(stringArray[i]);
 | |
|     } else {
 | |
|       value = DBusAppendResultID(aSearchResult, stringArray[i]);
 | |
|     }
 | |
|     if (value) {
 | |
|       g_variant_builder_add_value(&b, value);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   GVariant* v = g_variant_builder_end(&b);
 | |
|   g_dbus_method_invocation_return_value(aReply, g_variant_new_tuple(&v, 1));
 | |
| 
 | |
|   g_free(stringArray);
 | |
| }  // 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);
 | |
| }
 | |
| 
 | |
| // ActivateResult :: (s,as,u) → ()
 | |
| void DBusActivateResult(RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
 | |
|                         GVariant* aParameters, GDBusMethodInvocation* aReply) {
 | |
|   const char* resultID;
 | |
| 
 | |
|   // aParameters is "(s,as,u)" type
 | |
|   RefPtr<GVariant> r = dont_AddRef(g_variant_get_child_value(aParameters, 0));
 | |
|   if (!(resultID = g_variant_get_string(r, nullptr))) {
 | |
|     g_dbus_method_invocation_return_error(
 | |
|         aReply, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Wrong params!");
 | |
|     return;
 | |
|   }
 | |
|   RefPtr<GVariant> t = dont_AddRef(g_variant_get_child_value(aParameters, 2));
 | |
|   uint32_t timestamp = g_variant_get_uint32(t);
 | |
| 
 | |
|   ActivateResultID(aSearchResult, resultID, timestamp);
 | |
|   g_dbus_method_invocation_return_value(aReply, nullptr);
 | |
| }
 | |
| 
 | |
| // LaunchSearch :: (as,u) → ()
 | |
| void DBusLaunchSearch(RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
 | |
|                       GVariant* aParameters, GDBusMethodInvocation* aReply) {
 | |
|   RefPtr<GVariant> variant =
 | |
|       dont_AddRef(g_variant_get_child_value(aParameters, 1));
 | |
|   if (!variant) {
 | |
|     g_dbus_method_invocation_return_error(
 | |
|         aReply, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Wrong params!");
 | |
|     return;
 | |
|   }
 | |
|   DBusLaunchWithAllResults(aSearchResult, g_variant_get_uint32(variant));
 | |
|   g_dbus_method_invocation_return_value(aReply, nullptr);
 | |
| }
 | |
| 
 | |
| 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();
 | |
| }
 |