tor-browser

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

nsGNOMEShellService.cpp (15132B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "mozilla/Preferences.h"
      7 #include "mozilla/widget/GSettings.h"
      8 
      9 #include "nsCOMPtr.h"
     10 #include "nsGNOMEShellService.h"
     11 #include "nsShellService.h"
     12 #include "nsIFile.h"
     13 #include "nsIProperties.h"
     14 #include "nsDirectoryServiceDefs.h"
     15 #include "prenv.h"
     16 #include "nsString.h"
     17 #include "nsIGIOService.h"
     18 #include "nsIStringBundle.h"
     19 #include "nsServiceManagerUtils.h"
     20 #include "nsIImageLoadingContent.h"
     21 #include "imgIRequest.h"
     22 #include "imgIContainer.h"
     23 #include "mozilla/Components.h"
     24 #include "mozilla/GRefPtr.h"
     25 #include "mozilla/GUniquePtr.h"
     26 #include "mozilla/WidgetUtilsGtk.h"
     27 #include "mozilla/dom/Element.h"
     28 #include "nsImageToPixbuf.h"
     29 #include "nsXULAppAPI.h"
     30 #include "gfxPlatform.h"
     31 
     32 #include <glib.h>
     33 #include <gdk/gdk.h>
     34 #include <gdk-pixbuf/gdk-pixbuf.h>
     35 #include <stdlib.h>
     36 
     37 using namespace mozilla;
     38 
     39 struct ProtocolAssociation {
     40  const char* name;
     41  bool essential;
     42 };
     43 
     44 struct MimeTypeAssociation {
     45  const char* mimeType;
     46  const char* extensions;
     47 };
     48 
     49 static const ProtocolAssociation appProtocols[] = {
     50    // clang-format off
     51  { "http",   true     },
     52  { "https",  true     },
     53  { "chrome", false }
     54    // clang-format on
     55 };
     56 
     57 static const MimeTypeAssociation appTypes[] = {
     58    // clang-format off
     59  { "text/html",             "htm html shtml" },
     60  { "application/xhtml+xml", "xhtml xht"      }
     61    // clang-format on
     62 };
     63 
     64 #define kDesktopBGSchema "org.gnome.desktop.background"_ns
     65 #define kDesktopColorGSKey "primary-color"_ns
     66 
     67 nsresult nsGNOMEShellService::Init() {
     68  nsresult rv;
     69 
     70  if (gfxPlatform::IsHeadless()) {
     71    return NS_ERROR_NOT_AVAILABLE;
     72  }
     73 
     74  // GSettings or GIO _must_ be available, or we do not allow
     75  // CreateInstance to succeed.
     76 
     77 #ifdef MOZ_ENABLE_DBUS
     78  if (widget::IsGnomeDesktopEnvironment() &&
     79      Preferences::GetBool("browser.gnome-search-provider.enabled", false)) {
     80    mSearchProvider.Startup();
     81  }
     82 #endif
     83 
     84  // Check G_BROKEN_FILENAMES.  If it's set, then filenames in glib use
     85  // the locale encoding.  If it's not set, they use UTF-8.
     86  mUseLocaleFilenames = PR_GetEnv("G_BROKEN_FILENAMES") != nullptr;
     87 
     88  if (GetAppPathFromLauncher()) return NS_OK;
     89 
     90  nsCOMPtr<nsIProperties> dirSvc(
     91      do_GetService("@mozilla.org/file/directory_service;1"));
     92  NS_ENSURE_TRUE(dirSvc, NS_ERROR_NOT_AVAILABLE);
     93 
     94  nsCOMPtr<nsIFile> appPath;
     95  rv = dirSvc->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
     96                   getter_AddRefs(appPath));
     97  NS_ENSURE_SUCCESS(rv, rv);
     98 
     99  return appPath->GetNativePath(mAppPath);
    100 }
    101 
    102 NS_IMPL_ISUPPORTS(nsGNOMEShellService, nsIGNOMEShellService, nsIShellService,
    103                  nsIToolkitShellService)
    104 
    105 bool nsGNOMEShellService::GetAppPathFromLauncher() {
    106  gchar* tmp;
    107 
    108  const char* launcher = PR_GetEnv("MOZ_APP_LAUNCHER");
    109  if (!launcher) return false;
    110 
    111  if (g_path_is_absolute(launcher)) {
    112    mAppPath = launcher;
    113    tmp = g_path_get_basename(launcher);
    114    gchar* fullpath = g_find_program_in_path(tmp);
    115    if (fullpath && mAppPath.Equals(fullpath)) mAppIsInPath = true;
    116    g_free(fullpath);
    117  } else {
    118    tmp = g_find_program_in_path(launcher);
    119    if (!tmp) return false;
    120    mAppPath = tmp;
    121    mAppIsInPath = true;
    122  }
    123 
    124  g_free(tmp);
    125  return true;
    126 }
    127 
    128 bool nsGNOMEShellService::KeyMatchesAppName(const char* aKeyValue) const {
    129  gchar* commandPath;
    130  if (mUseLocaleFilenames) {
    131    gchar* nativePath =
    132        g_filename_from_utf8(aKeyValue, -1, nullptr, nullptr, nullptr);
    133    if (!nativePath) {
    134      NS_ERROR("Error converting path to filesystem encoding");
    135      return false;
    136    }
    137 
    138    commandPath = g_find_program_in_path(nativePath);
    139    g_free(nativePath);
    140  } else {
    141    commandPath = g_find_program_in_path(aKeyValue);
    142  }
    143 
    144  if (!commandPath) return false;
    145 
    146  bool matches = mAppPath.Equals(commandPath);
    147  g_free(commandPath);
    148  return matches;
    149 }
    150 
    151 bool nsGNOMEShellService::CheckHandlerMatchesAppName(
    152    const nsACString& handler) const {
    153  gint argc;
    154  gchar** argv;
    155  nsAutoCString command(handler);
    156 
    157  // The string will be something of the form: [/path/to/]browser "%s"
    158  // We want to remove all of the parameters and get just the binary name.
    159 
    160  if (g_shell_parse_argv(command.get(), &argc, &argv, nullptr) && argc > 0) {
    161    command.Assign(argv[0]);
    162    g_strfreev(argv);
    163  }
    164 
    165  if (!KeyMatchesAppName(command.get()))
    166    return false;  // the handler is set to another app
    167 
    168  return true;
    169 }
    170 
    171 NS_IMETHODIMP
    172 nsGNOMEShellService::IsDefaultBrowser(bool aForAllTypes,
    173                                      bool* aIsDefaultBrowser) {
    174  *aIsDefaultBrowser = false;
    175 
    176  if (widget::IsRunningUnderSnap()) {
    177    const gchar* argv[] = {"xdg-settings", "check", "default-web-browser",
    178                           (MOZ_APP_NAME ".desktop"), nullptr};
    179    GSpawnFlags flags = static_cast<GSpawnFlags>(G_SPAWN_SEARCH_PATH |
    180                                                 G_SPAWN_STDERR_TO_DEV_NULL);
    181    gchar* output = nullptr;
    182    gint exit_status = 0;
    183    if (!g_spawn_sync(nullptr, (gchar**)argv, nullptr, flags, nullptr, nullptr,
    184                      &output, nullptr, &exit_status, nullptr)) {
    185      return NS_OK;
    186    }
    187    if (exit_status != 0) {
    188      g_free(output);
    189      return NS_OK;
    190    }
    191    if (strcmp(output, "yes\n") == 0) {
    192      *aIsDefaultBrowser = true;
    193    }
    194    g_free(output);
    195    return NS_OK;
    196  }
    197 
    198  nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
    199  nsAutoCString handler;
    200  nsCOMPtr<nsIGIOMimeApp> gioApp;
    201 
    202  for (unsigned int i = 0; i < std::size(appProtocols); ++i) {
    203    if (!appProtocols[i].essential) continue;
    204 
    205    if (!IsDefaultForSchemeHelper(nsDependentCString(appProtocols[i].name),
    206                                  giovfs)) {
    207      return NS_OK;
    208    }
    209  }
    210 
    211  *aIsDefaultBrowser = true;
    212 
    213  return NS_OK;
    214 }
    215 
    216 bool nsGNOMEShellService::IsDefaultForSchemeHelper(
    217    const nsACString& aScheme, nsIGIOService* giovfs) const {
    218  nsCOMPtr<nsIGIOService> gioService;
    219  if (!giovfs) {
    220    gioService = do_GetService(NS_GIOSERVICE_CONTRACTID);
    221    giovfs = gioService.get();
    222  }
    223 
    224  if (!giovfs) {
    225    return false;
    226  }
    227 
    228  nsCOMPtr<nsIGIOMimeApp> gioApp;
    229  nsCOMPtr<nsIHandlerApp> handlerApp;
    230  giovfs->GetAppForURIScheme(aScheme, getter_AddRefs(handlerApp));
    231  gioApp = do_QueryInterface(handlerApp);
    232  if (!gioApp) {
    233    return false;
    234  }
    235 
    236  nsAutoCString handler;
    237  gioApp->GetCommand(handler);
    238  return CheckHandlerMatchesAppName(handler);
    239 }
    240 
    241 NS_IMETHODIMP
    242 nsGNOMEShellService::IsDefaultForScheme(const nsACString& aScheme,
    243                                        bool* aIsDefaultBrowser) {
    244  *aIsDefaultBrowser = IsDefaultForSchemeHelper(aScheme, nullptr);
    245  return NS_OK;
    246 }
    247 
    248 NS_IMETHODIMP
    249 nsGNOMEShellService::SetDefaultBrowser(bool aForAllUsers) {
    250 #ifdef DEBUG
    251  if (aForAllUsers)
    252    NS_WARNING(
    253        "Setting the default browser for all users is not yet supported");
    254 #endif
    255 
    256  if (widget::IsRunningUnderSnap()) {
    257    const gchar* argv[] = {"xdg-settings", "set", "default-web-browser",
    258                           (MOZ_APP_NAME ".desktop"), nullptr};
    259    GSpawnFlags flags = static_cast<GSpawnFlags>(G_SPAWN_SEARCH_PATH |
    260                                                 G_SPAWN_STDOUT_TO_DEV_NULL |
    261                                                 G_SPAWN_STDERR_TO_DEV_NULL);
    262    g_spawn_sync(nullptr, (gchar**)argv, nullptr, flags, nullptr, nullptr,
    263                 nullptr, nullptr, nullptr, nullptr);
    264    return NS_OK;
    265  }
    266 
    267  nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
    268  if (giovfs) {
    269    nsresult rv;
    270    nsCOMPtr<nsIStringBundleService> bundleService =
    271        components::StringBundle::Service(&rv);
    272    NS_ENSURE_SUCCESS(rv, rv);
    273 
    274    nsCOMPtr<nsIStringBundle> brandBundle;
    275    rv = bundleService->CreateBundle(BRAND_PROPERTIES,
    276                                     getter_AddRefs(brandBundle));
    277    NS_ENSURE_SUCCESS(rv, rv);
    278 
    279    nsAutoString brandShortName;
    280    brandBundle->GetStringFromName("brandShortName", brandShortName);
    281 
    282    // use brandShortName as the application id.
    283    NS_ConvertUTF16toUTF8 id(brandShortName);
    284    nsCOMPtr<nsIGIOMimeApp> appInfo;
    285    rv = giovfs->FindAppFromCommand(mAppPath, getter_AddRefs(appInfo));
    286    if (NS_FAILED(rv)) {
    287      // Application was not found in the list of installed applications
    288      // provided by OS. Fallback to create appInfo from command and name.
    289      rv = giovfs->CreateAppFromCommand(mAppPath, id, getter_AddRefs(appInfo));
    290      NS_ENSURE_SUCCESS(rv, rv);
    291    }
    292 
    293    // set handler for the protocols
    294    for (unsigned int i = 0; i < std::size(appProtocols); ++i) {
    295      appInfo->SetAsDefaultForURIScheme(
    296          nsDependentCString(appProtocols[i].name));
    297    }
    298 
    299    // set handler for .html and xhtml files and MIME types:
    300    // Add mime types for html, xhtml extension and set app to just created
    301    // appinfo.
    302    for (unsigned int i = 0; i < std::size(appTypes); ++i) {
    303      appInfo->SetAsDefaultForMimeType(
    304          nsDependentCString(appTypes[i].mimeType));
    305      appInfo->SetAsDefaultForFileExtensions(
    306          nsDependentCString(appTypes[i].extensions));
    307    }
    308  }
    309 
    310  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
    311  if (prefs) {
    312    (void)prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
    313    // Reset the number of times the dialog should be shown
    314    // before it is silenced.
    315    (void)prefs->SetIntPref(PREF_DEFAULTBROWSERCHECKCOUNT, 0);
    316  }
    317 
    318  return NS_OK;
    319 }
    320 
    321 NS_IMETHODIMP
    322 nsGNOMEShellService::GetCanSetDesktopBackground(bool* aResult) {
    323  // setting desktop background is currently only supported
    324  // for Gnome or desktops using the same GSettings keys
    325  if (widget::IsGnomeDesktopEnvironment()) {
    326    *aResult = true;
    327    return NS_OK;
    328  }
    329 
    330  *aResult = !!getenv("GNOME_DESKTOP_SESSION_ID");
    331  return NS_OK;
    332 }
    333 
    334 static nsresult WriteImage(const nsCString& aPath, imgIContainer* aImage) {
    335  RefPtr<GdkPixbuf> pixbuf = nsImageToPixbuf::ImageToPixbuf(aImage);
    336  if (!pixbuf) {
    337    return NS_ERROR_NOT_AVAILABLE;
    338  }
    339 
    340  gboolean res = gdk_pixbuf_save(pixbuf, aPath.get(), "png", nullptr, nullptr);
    341  return res ? NS_OK : NS_ERROR_FAILURE;
    342 }
    343 
    344 NS_IMETHODIMP
    345 nsGNOMEShellService::SetDesktopBackground(dom::Element* aElement,
    346                                          int32_t aPosition,
    347                                          const nsACString& aImageName) {
    348  nsCOMPtr<nsIImageLoadingContent> imageContent = do_QueryInterface(aElement);
    349  if (!imageContent) {
    350    return NS_ERROR_FAILURE;
    351  }
    352 
    353  // get the image container
    354  nsCOMPtr<imgIRequest> request;
    355  imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
    356                           getter_AddRefs(request));
    357  if (!request) {
    358    return NS_ERROR_FAILURE;
    359  }
    360  nsCOMPtr<imgIContainer> container;
    361  request->GetImage(getter_AddRefs(container));
    362  if (!container) {
    363    return NS_ERROR_FAILURE;
    364  }
    365 
    366  // Set desktop wallpaper filling style
    367  nsAutoCString options;
    368  if (aPosition == BACKGROUND_TILE)
    369    options.AssignLiteral("wallpaper");
    370  else if (aPosition == BACKGROUND_STRETCH)
    371    options.AssignLiteral("stretched");
    372  else if (aPosition == BACKGROUND_FILL)
    373    options.AssignLiteral("zoom");
    374  else if (aPosition == BACKGROUND_FIT)
    375    options.AssignLiteral("scaled");
    376  else if (aPosition == BACKGROUND_SPAN)
    377    options.AssignLiteral("spanned");
    378  else
    379    options.AssignLiteral("centered");
    380 
    381  // Write the background file to the home directory.
    382  nsAutoCString filePath(PR_GetEnv("HOME"));
    383  nsAutoString brandName;
    384 
    385  // get the product brand name from localized strings
    386  if (nsCOMPtr<nsIStringBundleService> bundleService =
    387          components::StringBundle::Service()) {
    388    nsCOMPtr<nsIStringBundle> brandBundle;
    389    bundleService->CreateBundle(BRAND_PROPERTIES, getter_AddRefs(brandBundle));
    390    if (bundleService) {
    391      brandBundle->GetStringFromName("brandShortName", brandName);
    392    }
    393  }
    394 
    395  // build the file name
    396  filePath.Append('/');
    397  filePath.Append(NS_ConvertUTF16toUTF8(brandName));
    398  filePath.AppendLiteral("_wallpaper.png");
    399 
    400  // write the image to a file in the home dir
    401  MOZ_TRY(WriteImage(filePath, container));
    402 
    403  widget::GSettings::Collection bgSettings(kDesktopBGSchema);
    404  if (!bgSettings) {
    405    return NS_ERROR_FAILURE;
    406  }
    407 
    408  GUniquePtr<gchar> fileURI(
    409      g_filename_to_uri(filePath.get(), nullptr, nullptr));
    410  if (!fileURI) {
    411    return NS_ERROR_FAILURE;
    412  }
    413 
    414  bgSettings.SetString("picture-options"_ns, options);
    415  bgSettings.SetString("picture-uri"_ns, nsDependentCString(fileURI.get()));
    416  bgSettings.SetString("picture-uri-dark"_ns,
    417                       nsDependentCString(fileURI.get()));
    418  return NS_OK;
    419 }
    420 
    421 #define COLOR_16_TO_8_BIT(_c) ((_c) >> 8)
    422 #define COLOR_8_TO_16_BIT(_c) ((_c) << 8 | (_c))
    423 
    424 NS_IMETHODIMP
    425 nsGNOMEShellService::GetDesktopBackgroundColor(uint32_t* aColor) {
    426  nsAutoCString background;
    427  widget::GSettings::GetString(kDesktopBGSchema, kDesktopColorGSKey,
    428                               background);
    429  if (background.IsEmpty()) {
    430    *aColor = 0;
    431    return NS_OK;
    432  }
    433 
    434  GdkColor color;
    435  gboolean success = gdk_color_parse(background.get(), &color);
    436 
    437  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
    438 
    439  *aColor = COLOR_16_TO_8_BIT(color.red) << 16 |
    440            COLOR_16_TO_8_BIT(color.green) << 8 | COLOR_16_TO_8_BIT(color.blue);
    441  return NS_OK;
    442 }
    443 
    444 static void ColorToCString(uint32_t aColor, nsCString& aResult) {
    445  // The #rrrrggggbbbb format is used to match gdk_color_to_string()
    446  aResult.SetLength(13);
    447  char* buf = aResult.BeginWriting();
    448  if (!buf) return;
    449 
    450  uint16_t red = COLOR_8_TO_16_BIT((aColor >> 16) & 0xff);
    451  uint16_t green = COLOR_8_TO_16_BIT((aColor >> 8) & 0xff);
    452  uint16_t blue = COLOR_8_TO_16_BIT(aColor & 0xff);
    453 
    454  snprintf(buf, 14, "#%04x%04x%04x", red, green, blue);
    455 }
    456 
    457 NS_IMETHODIMP
    458 nsGNOMEShellService::SetDesktopBackgroundColor(uint32_t aColor) {
    459  NS_ASSERTION(aColor <= 0xffffff, "aColor has extra bits");
    460  nsAutoCString colorString;
    461  ColorToCString(aColor, colorString);
    462 
    463  widget::GSettings::Collection bgSettings(kDesktopBGSchema);
    464  if (bgSettings) {
    465    bgSettings.SetString(kDesktopColorGSKey, colorString);
    466    return NS_OK;
    467  }
    468 
    469  return NS_ERROR_FAILURE;
    470 }
    471 
    472 NS_IMETHODIMP
    473 nsGNOMEShellService::GetGSettingsString(const nsACString& aSchema,
    474                                        const nsACString& aKey,
    475                                        nsACString& aResult) {
    476  widget::GSettings::GetString(PromiseFlatCString(aSchema),
    477                               PromiseFlatCString(aKey), aResult);
    478  return NS_OK;
    479 }
    480 
    481 NS_IMETHODIMP
    482 nsGNOMEShellService::SetGSettingsString(const nsACString& aSchema,
    483                                        const nsACString& aKey,
    484                                        const nsACString& aValue) {
    485  widget::GSettings::Collection settings(PromiseFlatCString(aSchema));
    486  if (!settings) {
    487    return NS_ERROR_FAILURE;
    488  }
    489  if (!settings.SetString(PromiseFlatCString(aKey),
    490                          PromiseFlatCString(aValue))) {
    491    return NS_ERROR_FAILURE;
    492  }
    493  return NS_OK;
    494 }