mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	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();
 | 
						|
}
 |