tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsGNOMEShellDBusHelper.cpp (14963B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:expandtab:shiftwidth=2:tabstop=2:
      3 */
      4 /* This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 #include "nsGNOMEShellSearchProvider.h"
      9 
     10 #include "RemoteUtils.h"
     11 #include "nsIStringBundle.h"
     12 #include "nsServiceManagerUtils.h"
     13 #include "nsPrintfCString.h"
     14 #include "mozilla/XREAppData.h"
     15 #include "nsAppRunner.h"
     16 #include "nsImportModule.h"
     17 #include "nsIOpenTabsProvider.h"
     18 
     19 #define DBUS_BUS_NAME_TEMPLATE "org.mozilla.%s.SearchProvider"
     20 #define DBUS_OBJECT_PATH_TEMPLATE "/org/mozilla/%s/SearchProvider"
     21 
     22 const char* GetDBusBusName() {
     23  static const char* name = []() {
     24    nsAutoCString appName;
     25    gAppData->GetDBusAppName(appName);
     26    return ToNewCString(nsPrintfCString(DBUS_BUS_NAME_TEMPLATE,
     27                                        appName.get()));  // Intentionally leak
     28  }();
     29  return name;
     30 }
     31 
     32 const char* GetDBusObjectPath() {
     33  static const char* path = []() {
     34    nsAutoCString appName;
     35    gAppData->GetDBusAppName(appName);
     36    return ToNewCString(nsPrintfCString(DBUS_OBJECT_PATH_TEMPLATE,
     37                                        appName.get()));  // Intentionally leak
     38  }();
     39  return path;
     40 }
     41 
     42 static bool GetGnomeSearchTitle(const char* aSearchedTerm,
     43                                nsAutoCString& aGnomeSearchTitle) {
     44  static nsCOMPtr<nsIStringBundle> bundle;
     45  if (!bundle) {
     46    nsCOMPtr<nsIStringBundleService> sbs =
     47        do_GetService(NS_STRINGBUNDLE_CONTRACTID);
     48    if (NS_WARN_IF(!sbs)) {
     49      return false;
     50    }
     51 
     52    sbs->CreateBundle("chrome://browser/locale/browser.properties",
     53                      getter_AddRefs(bundle));
     54    if (NS_WARN_IF(!bundle)) {
     55      return false;
     56    }
     57  }
     58 
     59  AutoTArray<nsString, 1> formatStrings;
     60  CopyUTF8toUTF16(nsCString(aSearchedTerm), *formatStrings.AppendElement());
     61 
     62  nsAutoString gnomeSearchTitle;
     63  bundle->FormatStringFromName("gnomeSearchProviderSearchWeb", formatStrings,
     64                               gnomeSearchTitle);
     65  AppendUTF16toUTF8(gnomeSearchTitle, aGnomeSearchTitle);
     66  return true;
     67 }
     68 
     69 int DBusGetIndexFromIDKey(const char* aIDKey) {
     70  // ID is NN:S:URL where NN is index to our current history
     71  // result container.
     72  char tmp[] = {aIDKey[0], aIDKey[1], '\0'};
     73  return atoi(tmp);
     74 }
     75 
     76 char DBusGetStateFromIDKey(const char* aIDKey) {
     77  // ID is NN:S:URL where NN is index to our current history
     78  // result container, and S is the state, which can be 'o'pen or 'h'istory
     79  if (std::strlen(aIDKey) > 3) {
     80    return aIDKey[3];
     81  }
     82  // Should never happen, but just to avoid any possible segfault, we
     83  // default to state 'history'.
     84  return 'h';
     85 }
     86 
     87 static void ConcatArray(nsACString& aOutputStr, const char** aStringArray) {
     88  for (const char** term = aStringArray; *term; term++) {
     89    aOutputStr.Append(*term);
     90    if (*(term + 1)) {
     91      aOutputStr.Append(" ");
     92    }
     93  }
     94 }
     95 
     96 // GetInitialResultSet :: (as) → (as)
     97 // GetSubsearchResultSet :: (as,as) → (as)
     98 void DBusHandleResultSet(RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
     99                         GVariant* aParameters, bool aInitialSearch,
    100                         GDBusMethodInvocation* aReply) {
    101  // Inital search has params (as), any following one has (as,as) and we want
    102  // the second string array.
    103  fprintf(stderr, "%s\n", g_variant_get_type_string(aParameters));
    104  RefPtr<GVariant> variant = dont_AddRef(
    105      g_variant_get_child_value(aParameters, aInitialSearch ? 0 : 1));
    106  const char** stringArray = g_variant_get_strv(variant, nullptr);
    107  if (!stringArray) {
    108    g_dbus_method_invocation_return_error(
    109        aReply, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Wrong params!");
    110    return;
    111  }
    112 
    113  aSearchResult->SetReply(aReply);
    114  nsAutoCString searchTerm;
    115  ConcatArray(searchTerm, stringArray);
    116  aSearchResult->SetSearchTerm(searchTerm.get());
    117  GetGNOMEShellHistoryService()->QueryHistory(aSearchResult);
    118  // DBus reply will be send asynchronously by
    119  // nsGNOMEShellHistorySearchResult::SendDBusSearchResultReply()
    120  // when GetGNOMEShellHistoryService() has the results.
    121 
    122  g_free(stringArray);
    123 }
    124 
    125 /*
    126  "icon-data": a tuple of type (iiibiiay) describing a pixbuf with width,
    127              height, rowstride, has-alpha,
    128              bits-per-sample, channels,
    129              image data
    130 */
    131 static void DBusAppendIcon(GVariantBuilder* aBuilder, GnomeHistoryIcon* aIcon) {
    132  GVariantBuilder b;
    133  g_variant_builder_init(&b, G_VARIANT_TYPE("(iiibiiay)"));
    134  g_variant_builder_add_value(&b, g_variant_new_int32(aIcon->GetWidth()));
    135  g_variant_builder_add_value(&b, g_variant_new_int32(aIcon->GetHeight()));
    136  g_variant_builder_add_value(&b, g_variant_new_int32(aIcon->GetWidth() * 4));
    137  g_variant_builder_add_value(&b, g_variant_new_boolean(true));
    138  g_variant_builder_add_value(&b, g_variant_new_int32(8));
    139  g_variant_builder_add_value(&b, g_variant_new_int32(4));
    140  g_variant_builder_add_value(
    141      &b, g_variant_new_fixed_array(G_VARIANT_TYPE("y"), aIcon->GetData(),
    142                                    aIcon->GetWidth() * aIcon->GetHeight() * 4,
    143                                    sizeof(char)));
    144  g_variant_builder_add(aBuilder, "{sv}", "icon-data",
    145                        g_variant_builder_end(&b));
    146 }
    147 
    148 /* Appends history search results to the DBUS reply.
    149 
    150  We can return those fields at GetResultMetas:
    151 
    152  "id": the result ID
    153  "name": the display name for the result
    154  "icon": a serialized GIcon (see g_icon_serialize()), or alternatively,
    155  "gicon": a textual representation of a GIcon (see g_icon_to_string()),
    156           or alternativly,
    157  "icon-data": a tuple of type (iiibiiay) describing a pixbuf with width,
    158              height, rowstride, has-alpha, bits-per-sample, and image data
    159  "description": an optional short description (1-2 lines)
    160 */
    161 static already_AddRefed<GVariant> DBusAppendResultID(
    162    nsGNOMEShellHistorySearchResult* aSearchResult, const char* aID) {
    163  nsCOMPtr<nsINavHistoryContainerResultNode> container =
    164      aSearchResult->GetSearchResultContainer();
    165 
    166  int index = DBusGetIndexFromIDKey(aID);
    167  char state = DBusGetStateFromIDKey(aID);
    168  nsCOMPtr<nsINavHistoryResultNode> child;
    169  container->GetChild(index, getter_AddRefs(child));
    170  nsAutoCString title;
    171  if (!child || NS_FAILED(child->GetTitle(title))) {
    172    return nullptr;
    173  }
    174 
    175  if (title.IsEmpty()) {
    176    if (NS_FAILED(child->GetUri(title)) || title.IsEmpty()) {
    177      return nullptr;
    178    }
    179  }
    180 
    181  // Check if the URI state is "open tab". If so, mark it with an asterisk to
    182  // indicate this to the user.
    183  if (state == 'o') {
    184    title = "(*) "_ns + title;
    185  }
    186 
    187  GVariantBuilder b;
    188  g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}"));
    189 
    190  const char* titleStr = title.get();
    191  g_variant_builder_add(&b, "{sv}", "id", g_variant_new_string(aID));
    192  g_variant_builder_add(&b, "{sv}", "name", g_variant_new_string(titleStr));
    193 
    194  GnomeHistoryIcon* icon = aSearchResult->GetHistoryIcon(index);
    195  if (icon) {
    196    DBusAppendIcon(&b, icon);
    197  } else {
    198    g_variant_builder_add(&b, "{sv}", "gicon",
    199                          g_variant_new_string("text-html"));
    200  }
    201  return dont_AddRef(g_variant_ref_sink(g_variant_builder_end(&b)));
    202 }
    203 
    204 // Search the web for: "searchTerm" to the DBUS reply.
    205 static already_AddRefed<GVariant> DBusAppendSearchID(const char* aID) {
    206  /* aID contains:
    207 
    208     KEYWORD_SEARCH_STRING:ssssss
    209 
    210     KEYWORD_SEARCH_STRING is a 'special:search' keyword
    211     ssssss is a searched term, must be at least one character long
    212  */
    213 
    214  // aID contains only 'KEYWORD_SEARCH_STRING:' so we're missing searched
    215  // string.
    216  if (strlen(aID) <= KEYWORD_SEARCH_STRING_LEN + 1) {
    217    return nullptr;
    218  }
    219 
    220  GVariantBuilder b;
    221  g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}"));
    222  g_variant_builder_add(&b, "{sv}", "id",
    223                        g_variant_new_string(KEYWORD_SEARCH_STRING));
    224 
    225  // Extract ssssss part from aID
    226  nsAutoCString searchTerm(aID + KEYWORD_SEARCH_STRING_LEN + 1);
    227  nsAutoCString gnomeSearchTitle;
    228  if (GetGnomeSearchTitle(searchTerm.get(), gnomeSearchTitle)) {
    229    g_variant_builder_add(&b, "{sv}", "name",
    230                          g_variant_new_string(gnomeSearchTitle.get()));
    231    // TODO: When running on flatpak/snap we may need to use
    232    // icon like org.mozilla.Firefox or so.
    233    g_variant_builder_add(&b, "{sv}", "gicon", g_variant_new_string("firefox"));
    234  }
    235 
    236  return dont_AddRef(g_variant_ref_sink(g_variant_builder_end(&b)));
    237 }
    238 
    239 // GetResultMetas :: (as) → (aa{sv})
    240 void DBusHandleResultMetas(
    241    RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
    242    GVariant* aParameters, GDBusMethodInvocation* aReply) {
    243  RefPtr<GVariant> variant =
    244      dont_AddRef(g_variant_get_child_value(aParameters, 0));
    245  gsize elements;
    246  const char** stringArray = g_variant_get_strv(variant, &elements);
    247  if (!stringArray) {
    248    g_dbus_method_invocation_return_error(
    249        aReply, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Wrong params!");
    250    return;
    251  }
    252 
    253  GVariantBuilder b;
    254  g_variant_builder_init(&b, G_VARIANT_TYPE("aa{sv}"));
    255  for (gsize i = 0; i < elements; i++) {
    256    RefPtr<GVariant> value;
    257    if (strncmp(stringArray[i], KEYWORD_SEARCH_STRING,
    258                KEYWORD_SEARCH_STRING_LEN) == 0) {
    259      value = DBusAppendSearchID(stringArray[i]);
    260    } else {
    261      value = DBusAppendResultID(aSearchResult, stringArray[i]);
    262    }
    263    if (value) {
    264      g_variant_builder_add_value(&b, value);
    265    }
    266  }
    267 
    268  GVariant* v = g_variant_builder_end(&b);
    269  g_dbus_method_invocation_return_value(aReply, g_variant_new_tuple(&v, 1));
    270 
    271  g_free(stringArray);
    272 }  // namespace mozilla
    273 
    274 static void ActivateResultID(
    275    RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
    276    const char* aResultID, uint32_t aTimeStamp) {
    277  mozilla::UniquePtr<char[]> commandLine;
    278  int len;
    279 
    280  if (strncmp(aResultID, KEYWORD_SEARCH_STRING, KEYWORD_SEARCH_STRING_LEN) ==
    281      0) {
    282    const char* urlList[3] = {"unused", "--search",
    283                              aSearchResult->GetSearchTerm().get()};
    284    commandLine =
    285        ConstructCommandLine(std::size(urlList), urlList, nullptr, &len);
    286  } else {
    287    int keyIndex = atoi(aResultID);
    288    char state = DBusGetStateFromIDKey(aResultID);
    289    nsCOMPtr<nsINavHistoryResultNode> child;
    290    aSearchResult->GetSearchResultContainer()->GetChild(keyIndex,
    291                                                        getter_AddRefs(child));
    292    if (!child) {
    293      return;
    294    }
    295 
    296    nsAutoCString uri;
    297    nsresult rv = child->GetUri(uri);
    298    if (NS_FAILED(rv)) {
    299      return;
    300    }
    301 
    302    // If the state of the URI is 'o'pen, we send it along to JS and let
    303    // it switch the tab accordingly
    304    if (state == 'o') {
    305      // If we can't successfully switch to an open tab, use the existing
    306      // 'open in a new tab'-mechanism as a fallback.
    307      nsresult rv;
    308      nsCOMPtr<nsIOpenTabsProvider> provider = do_ImportESModule(
    309          "moz-src:///browser/components/shell/OpenTabsProvider.sys.mjs", &rv);
    310      if (NS_SUCCEEDED(rv)) {
    311        rv = provider->SwitchToOpenTab(uri);
    312        if (NS_SUCCEEDED(rv)) {
    313          return;
    314        }
    315      }
    316    }
    317 
    318    const char* urlList[2] = {"unused", uri.get()};
    319    commandLine =
    320        ConstructCommandLine(std::size(urlList), urlList, nullptr, &len);
    321  }
    322 
    323  if (commandLine) {
    324    aSearchResult->HandleCommandLine(mozilla::Span(commandLine.get(), len),
    325                                     aTimeStamp);
    326  }
    327 }
    328 
    329 static void DBusLaunchWithAllResults(
    330    RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
    331    uint32_t aTimeStamp) {
    332  uint32_t childCount = 0;
    333  nsresult rv =
    334      aSearchResult->GetSearchResultContainer()->GetChildCount(&childCount);
    335  if (NS_FAILED(rv) || childCount == 0) {
    336    return;
    337  }
    338 
    339  if (childCount > MAX_SEARCH_RESULTS_NUM) {
    340    childCount = MAX_SEARCH_RESULTS_NUM;
    341  }
    342 
    343  // Allocate space for all found results, "unused", "--search" and
    344  // potential search request.
    345  const char** urlList =
    346      (const char**)moz_xmalloc(sizeof(char*) * (childCount + 3));
    347  int urlListElements = 0;
    348 
    349  urlList[urlListElements++] = strdup("unused");
    350 
    351  for (uint32_t i = 0; i < childCount; i++) {
    352    nsCOMPtr<nsINavHistoryResultNode> child;
    353    aSearchResult->GetSearchResultContainer()->GetChild(i,
    354                                                        getter_AddRefs(child));
    355 
    356    if (!IsHistoryResultNodeURI(child)) {
    357      continue;
    358    }
    359 
    360    nsAutoCString uri;
    361    nsresult rv = child->GetUri(uri);
    362    if (NS_FAILED(rv)) {
    363      continue;
    364    }
    365    urlList[urlListElements++] = strdup(uri.get());
    366  }
    367 
    368  // When there isn't any uri to open pass search at least.
    369  if (!childCount) {
    370    urlList[urlListElements++] = strdup("--search");
    371    urlList[urlListElements++] = strdup(aSearchResult->GetSearchTerm().get());
    372  }
    373 
    374  int len;
    375  mozilla::UniquePtr<char[]> commandLine =
    376      ConstructCommandLine(urlListElements, urlList, nullptr, &len);
    377  if (commandLine) {
    378    aSearchResult->HandleCommandLine(mozilla::Span(commandLine.get(), len),
    379                                     aTimeStamp);
    380  }
    381 
    382  for (int i = 0; i < urlListElements; i++) {
    383    free((void*)urlList[i]);
    384  }
    385  free(urlList);
    386 }
    387 
    388 // ActivateResult :: (s,as,u) → ()
    389 void DBusActivateResult(RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
    390                        GVariant* aParameters, GDBusMethodInvocation* aReply) {
    391  const char* resultID;
    392 
    393  // aParameters is "(s,as,u)" type
    394  RefPtr<GVariant> r = dont_AddRef(g_variant_get_child_value(aParameters, 0));
    395  if (!(resultID = g_variant_get_string(r, nullptr))) {
    396    g_dbus_method_invocation_return_error(
    397        aReply, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Wrong params!");
    398    return;
    399  }
    400  RefPtr<GVariant> t = dont_AddRef(g_variant_get_child_value(aParameters, 2));
    401  uint32_t timestamp = g_variant_get_uint32(t);
    402 
    403  ActivateResultID(aSearchResult, resultID, timestamp);
    404  g_dbus_method_invocation_return_value(aReply, nullptr);
    405 }
    406 
    407 // LaunchSearch :: (as,u) → ()
    408 void DBusLaunchSearch(RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
    409                      GVariant* aParameters, GDBusMethodInvocation* aReply) {
    410  RefPtr<GVariant> variant =
    411      dont_AddRef(g_variant_get_child_value(aParameters, 1));
    412  if (!variant) {
    413    g_dbus_method_invocation_return_error(
    414        aReply, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Wrong params!");
    415    return;
    416  }
    417  DBusLaunchWithAllResults(aSearchResult, g_variant_get_uint32(variant));
    418  g_dbus_method_invocation_return_value(aReply, nullptr);
    419 }
    420 
    421 bool IsHistoryResultNodeURI(nsINavHistoryResultNode* aHistoryNode) {
    422  uint32_t type;
    423  nsresult rv = aHistoryNode->GetType(&type);
    424  if (NS_FAILED(rv) || type != nsINavHistoryResultNode::RESULT_TYPE_URI)
    425    return false;
    426 
    427  nsAutoCString title;
    428  rv = aHistoryNode->GetTitle(title);
    429  if (NS_SUCCEEDED(rv) && !title.IsEmpty()) {
    430    return true;
    431  }
    432 
    433  rv = aHistoryNode->GetUri(title);
    434  return NS_SUCCEEDED(rv) && !title.IsEmpty();
    435 }