tor-browser

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

nsWindowsShellService.cpp (103457B)


      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 #define UNICODE
      7 
      8 #include "nsWindowsShellService.h"
      9 #include "nsWindowsShellServiceInternal.h"
     10 
     11 #include "BinaryPath.h"
     12 #include "gfxUtils.h"
     13 #include "imgIContainer.h"
     14 #include "imgIRequest.h"
     15 #include "imgITools.h"
     16 #include "mozilla/dom/Element.h"
     17 #include "mozilla/dom/Promise.h"
     18 #include "mozilla/ErrorResult.h"
     19 #include "mozilla/FileUtils.h"
     20 #include "mozilla/gfx/2D.h"
     21 #include "mozilla/intl/Localization.h"
     22 #include "mozilla/RefPtr.h"
     23 #include "mozilla/widget/WinTaskbar.h"
     24 #include "mozilla/WindowsVersion.h"
     25 #include "mozilla/WinHeaderOnlyUtils.h"
     26 #include "nsAppDirectoryServiceDefs.h"
     27 #include "nsComponentManagerUtils.h"
     28 #include "nsDirectoryServiceDefs.h"
     29 #include "nsDirectoryServiceUtils.h"
     30 #include "nsIContent.h"
     31 #include "nsIFile.h"
     32 #include "nsIFileStreams.h"
     33 #include "nsIImageLoadingContent.h"
     34 #include "nsIMIMEService.h"
     35 #include "nsINIParser.h"
     36 #include "nsIOutputStream.h"
     37 #include "nsIPrefService.h"
     38 #include "nsIStringBundle.h"
     39 #include "nsIWindowsRegKey.h"
     40 #include "nsIXULAppInfo.h"
     41 #include "nsLocalFile.h"
     42 #include "nsNativeAppSupportWin.h"
     43 #include "nsNetUtil.h"
     44 #include "nsProxyRelease.h"
     45 #include "nsServiceManagerUtils.h"
     46 #include "nsShellService.h"
     47 #include "nsUnicharUtils.h"
     48 #include "nsWindowsHelpers.h"
     49 #include "nsXULAppAPI.h"
     50 #include "Windows11TaskbarPinning.h"
     51 #include "WindowsDefaultBrowser.h"
     52 #include "WindowsUserChoice.h"
     53 #include "WinUtils.h"
     54 
     55 #include <comutil.h>
     56 #include <knownfolders.h>
     57 #include <mbstring.h>
     58 #include <objbase.h>
     59 #include <propkey.h>
     60 #include <propvarutil.h>
     61 #include <shellapi.h>
     62 #include <strsafe.h>
     63 #include <windows.h>
     64 #include <windows.foundation.h>
     65 #include <wrl.h>
     66 #include <wrl/wrappers/corewrappers.h>
     67 
     68 using namespace ABI::Windows;
     69 using namespace ABI::Windows::Foundation;
     70 using namespace ABI::Windows::Foundation::Collections;
     71 using namespace Microsoft::WRL;
     72 using namespace Microsoft::WRL::Wrappers;
     73 
     74 #ifndef __MINGW32__
     75 #  include <windows.applicationmodel.h>
     76 #  include <windows.applicationmodel.activation.h>
     77 #  include <windows.applicationmodel.core.h>
     78 #  include <windows.ui.startscreen.h>
     79 using namespace ABI::Windows::ApplicationModel;
     80 using namespace ABI::Windows::ApplicationModel::Core;
     81 using namespace ABI::Windows::UI::StartScreen;
     82 #endif
     83 
     84 #define PRIVATE_BROWSING_BINARY L"private_browsing.exe"
     85 
     86 #undef ACCESS_READ
     87 
     88 #ifndef MAX_BUF
     89 #  define MAX_BUF 4096
     90 #endif
     91 
     92 #define REG_SUCCEEDED(val) (val == ERROR_SUCCESS)
     93 
     94 #define REG_FAILED(val) (val != ERROR_SUCCESS)
     95 
     96 #ifdef DEBUG
     97 #  define NS_ENSURE_HRESULT(hres, ret)                    \
     98    do {                                                  \
     99      HRESULT result = hres;                              \
    100      if (MOZ_UNLIKELY(FAILED(result))) {                 \
    101        mozilla::SmprintfPointer msg = mozilla::Smprintf( \
    102            "NS_ENSURE_HRESULT(%s, %s) failed with "      \
    103            "result 0x%" PRIX32,                          \
    104            #hres, #ret, static_cast<uint32_t>(result));  \
    105        NS_WARNING(msg.get());                            \
    106        return ret;                                       \
    107      }                                                   \
    108    } while (false)
    109 #else
    110 #  define NS_ENSURE_HRESULT(hres, ret) \
    111    if (MOZ_UNLIKELY(FAILED(hres))) return ret
    112 #endif
    113 
    114 using namespace mozilla;
    115 using mozilla::intl::Localization;
    116 
    117 struct SysFreeStringDeleter {
    118  void operator()(BSTR aPtr) { ::SysFreeString(aPtr); }
    119 };
    120 using BStrPtr = mozilla::UniquePtr<OLECHAR, SysFreeStringDeleter>;
    121 
    122 NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIToolkitShellService,
    123                  nsIShellService, nsIWindowsShellService)
    124 
    125 /* Enable logging by setting MOZ_LOG to "nsWindowsShellService:5" for debugging
    126 * purposes. */
    127 static LazyLogModule sLog("nsWindowsShellService");
    128 
    129 static bool PollAppsFolderForShortcut(const nsAString& aAppUserModelId,
    130                                      const TimeDuration aTimeout);
    131 static nsresult PinCurrentAppToTaskbarWin10(bool aCheckOnly,
    132                                            const nsAString& aAppUserModelId,
    133                                            const nsAString& aShortcutPath);
    134 static nsresult WriteBitmap(nsIFile* aFile, imgIContainer* aImage);
    135 static nsresult WriteIcon(nsIFile* aIcoFile, gfx::DataSourceSurface* aSurface);
    136 
    137 static nsresult OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName,
    138                                  HKEY* aKey) {
    139  const nsString& flatName = PromiseFlatString(aKeyName);
    140 
    141  DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey);
    142  switch (res) {
    143    case ERROR_SUCCESS:
    144      break;
    145    case ERROR_ACCESS_DENIED:
    146      return NS_ERROR_FILE_ACCESS_DENIED;
    147    case ERROR_FILE_NOT_FOUND:
    148      return NS_ERROR_NOT_AVAILABLE;
    149  }
    150 
    151  return NS_OK;
    152 }
    153 
    154 nsresult GetHelperPath(nsAutoString& aPath) {
    155  nsresult rv;
    156  nsCOMPtr<nsIProperties> directoryService =
    157      do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
    158  NS_ENSURE_SUCCESS(rv, rv);
    159 
    160  nsCOMPtr<nsIFile> appHelper;
    161  rv = directoryService->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
    162                             getter_AddRefs(appHelper));
    163  NS_ENSURE_SUCCESS(rv, rv);
    164 
    165  rv = appHelper->SetNativeLeafName("uninstall"_ns);
    166  NS_ENSURE_SUCCESS(rv, rv);
    167 
    168  rv = appHelper->AppendNative("helper.exe"_ns);
    169  NS_ENSURE_SUCCESS(rv, rv);
    170 
    171  rv = appHelper->GetPath(aPath);
    172 
    173  aPath.Insert(L'"', 0);
    174  aPath.Append(L'"');
    175  return rv;
    176 }
    177 
    178 nsresult LaunchHelper(nsAutoString& aPath) {
    179  STARTUPINFOW si = {sizeof(si), 0};
    180  PROCESS_INFORMATION pi = {0};
    181 
    182  if (!CreateProcessW(nullptr, (LPWSTR)aPath.get(), nullptr, nullptr, FALSE, 0,
    183                      nullptr, nullptr, &si, &pi)) {
    184    return NS_ERROR_FAILURE;
    185  }
    186 
    187  CloseHandle(pi.hProcess);
    188  CloseHandle(pi.hThread);
    189  return NS_OK;
    190 }
    191 
    192 static bool IsPathDefaultForClass(
    193    const RefPtr<IApplicationAssociationRegistration>& pAAR, wchar_t* exePath,
    194    LPCWSTR aClassName) {
    195  LPWSTR registeredApp;
    196  bool isProtocol = *aClassName != L'.';
    197  ASSOCIATIONTYPE queryType = isProtocol ? AT_URLPROTOCOL : AT_FILEEXTENSION;
    198  HRESULT hr = pAAR->QueryCurrentDefault(aClassName, queryType, AL_EFFECTIVE,
    199                                         &registeredApp);
    200  if (FAILED(hr)) {
    201    return false;
    202  }
    203 
    204  nsAutoString regAppName(registeredApp);
    205  CoTaskMemFree(registeredApp);
    206 
    207  // Make sure the application path for this progID is this installation.
    208  regAppName.AppendLiteral("\\shell\\open\\command");
    209  HKEY theKey;
    210  nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, regAppName, &theKey);
    211  if (NS_FAILED(rv)) {
    212    return false;
    213  }
    214 
    215  wchar_t cmdFromReg[MAX_BUF] = L"";
    216  DWORD len = sizeof(cmdFromReg);
    217  DWORD res = ::RegQueryValueExW(theKey, nullptr, nullptr, nullptr,
    218                                 (LPBYTE)cmdFromReg, &len);
    219  ::RegCloseKey(theKey);
    220  if (REG_FAILED(res)) {
    221    return false;
    222  }
    223 
    224  nsAutoString pathFromReg(cmdFromReg);
    225  nsLocalFile::CleanupCmdHandlerPath(pathFromReg);
    226 
    227  return _wcsicmp(exePath, pathFromReg.Data()) == 0;
    228 }
    229 
    230 NS_IMETHODIMP
    231 nsWindowsShellService::IsDefaultBrowser(bool aForAllTypes,
    232                                        bool* aIsDefaultBrowser) {
    233  *aIsDefaultBrowser = false;
    234 
    235  RefPtr<IApplicationAssociationRegistration> pAAR;
    236  HRESULT hr = CoCreateInstance(
    237      CLSID_ApplicationAssociationRegistration, nullptr, CLSCTX_INPROC,
    238      IID_IApplicationAssociationRegistration, getter_AddRefs(pAAR));
    239  if (FAILED(hr)) {
    240    return NS_OK;
    241  }
    242 
    243  wchar_t exePath[MAXPATHLEN] = L"";
    244  nsresult rv = BinaryPath::GetLong(exePath);
    245 
    246  if (NS_FAILED(rv)) {
    247    return NS_OK;
    248  }
    249 
    250  *aIsDefaultBrowser = IsPathDefaultForClass(pAAR, exePath, L"http");
    251  if (*aIsDefaultBrowser && aForAllTypes) {
    252    *aIsDefaultBrowser = IsPathDefaultForClass(pAAR, exePath, L".html");
    253  }
    254  return NS_OK;
    255 }
    256 
    257 NS_IMETHODIMP
    258 nsWindowsShellService::IsDefaultHandlerFor(
    259    const nsAString& aFileExtensionOrProtocol, bool* aIsDefaultHandlerFor) {
    260  *aIsDefaultHandlerFor = false;
    261 
    262  RefPtr<IApplicationAssociationRegistration> pAAR;
    263  HRESULT hr = CoCreateInstance(
    264      CLSID_ApplicationAssociationRegistration, nullptr, CLSCTX_INPROC,
    265      IID_IApplicationAssociationRegistration, getter_AddRefs(pAAR));
    266  if (FAILED(hr)) {
    267    return NS_OK;
    268  }
    269 
    270  wchar_t exePath[MAXPATHLEN] = L"";
    271  nsresult rv = BinaryPath::GetLong(exePath);
    272 
    273  if (NS_FAILED(rv)) {
    274    return NS_OK;
    275  }
    276 
    277  const nsString& flatClass = PromiseFlatString(aFileExtensionOrProtocol);
    278 
    279  *aIsDefaultHandlerFor = IsPathDefaultForClass(pAAR, exePath, flatClass.get());
    280  return NS_OK;
    281 }
    282 
    283 NS_IMETHODIMP
    284 nsWindowsShellService::QueryCurrentDefaultHandlerFor(
    285    const nsAString& aFileExtensionOrProtocol, nsAString& aResult) {
    286  aResult.Truncate();
    287 
    288  RefPtr<IApplicationAssociationRegistration> pAAR;
    289  HRESULT hr = CoCreateInstance(
    290      CLSID_ApplicationAssociationRegistration, nullptr, CLSCTX_INPROC,
    291      IID_IApplicationAssociationRegistration, getter_AddRefs(pAAR));
    292  if (FAILED(hr)) {
    293    return NS_OK;
    294  }
    295 
    296  const nsString& flatClass = PromiseFlatString(aFileExtensionOrProtocol);
    297 
    298  LPWSTR registeredApp;
    299  bool isProtocol = flatClass.First() != L'.';
    300  ASSOCIATIONTYPE queryType = isProtocol ? AT_URLPROTOCOL : AT_FILEEXTENSION;
    301  hr = pAAR->QueryCurrentDefault(flatClass.get(), queryType, AL_EFFECTIVE,
    302                                 &registeredApp);
    303  if (hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION)) {
    304    return NS_OK;
    305  }
    306  NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
    307 
    308  aResult = registeredApp;
    309  CoTaskMemFree(registeredApp);
    310 
    311  return NS_OK;
    312 }
    313 
    314 nsresult nsWindowsShellService::LaunchControlPanelDefaultsSelectionUI() {
    315  IApplicationAssociationRegistrationUI* pAARUI;
    316  HRESULT hr = CoCreateInstance(
    317      CLSID_ApplicationAssociationRegistrationUI, NULL, CLSCTX_INPROC,
    318      IID_IApplicationAssociationRegistrationUI, (void**)&pAARUI);
    319  if (SUCCEEDED(hr)) {
    320    mozilla::UniquePtr<wchar_t[]> appRegName;
    321    GetAppRegName(appRegName);
    322    hr = pAARUI->LaunchAdvancedAssociationUI(appRegName.get());
    323    pAARUI->Release();
    324  }
    325  return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
    326 }
    327 
    328 NS_IMETHODIMP
    329 nsWindowsShellService::CheckAllProgIDsExist(bool* aResult) {
    330  *aResult = false;
    331  nsAutoString aumid;
    332  if (!mozilla::widget::WinTaskbar::GetAppUserModelID(aumid)) {
    333    return NS_OK;
    334  }
    335 
    336  if (widget::WinUtils::HasPackageIdentity()) {
    337    UniquePtr<wchar_t[]> extraProgID;
    338    nsresult rv;
    339    bool result = true;
    340 
    341    // "FirefoxURL".
    342    rv = GetMsixProgId(L"https", extraProgID);
    343    if (NS_WARN_IF(NS_FAILED(rv))) {
    344      return rv;
    345    }
    346    result = result && CheckProgIDExists(extraProgID.get());
    347 
    348    // "FirefoxHTML".
    349    rv = GetMsixProgId(L".htm", extraProgID);
    350    if (NS_WARN_IF(NS_FAILED(rv))) {
    351      return rv;
    352    }
    353    result = result && CheckProgIDExists(extraProgID.get());
    354 
    355    // "FirefoxPDF".
    356    rv = GetMsixProgId(L".pdf", extraProgID);
    357    if (NS_WARN_IF(NS_FAILED(rv))) {
    358      return rv;
    359    }
    360    result = result && CheckProgIDExists(extraProgID.get());
    361 
    362    *aResult = result;
    363  } else {
    364    *aResult =
    365        CheckProgIDExists(FormatProgID(L"FirefoxURL", aumid.get()).get()) &&
    366        CheckProgIDExists(FormatProgID(L"FirefoxHTML", aumid.get()).get()) &&
    367        CheckProgIDExists(FormatProgID(L"FirefoxPDF", aumid.get()).get());
    368  }
    369 
    370  return NS_OK;
    371 }
    372 
    373 NS_IMETHODIMP
    374 nsWindowsShellService::CheckBrowserUserChoiceHashes(bool* aResult) {
    375  *aResult = ::CheckBrowserUserChoiceHashes();
    376  return NS_OK;
    377 }
    378 
    379 NS_IMETHODIMP
    380 nsWindowsShellService::CheckCurrentProcessAUMIDForTesting(
    381    nsAString& aRetAumid) {
    382  PWSTR id;
    383  HRESULT hr = GetCurrentProcessExplicitAppUserModelID(&id);
    384 
    385  if (FAILED(hr)) {
    386    // Process AUMID may not be set on MSIX builds,
    387    // if so we should return a dummy value
    388    if (widget::WinUtils::HasPackageIdentity()) {
    389      aRetAumid.Assign(u"MSIXAumidTestValue"_ns);
    390      return NS_OK;
    391    }
    392    return NS_ERROR_FAILURE;
    393  }
    394  aRetAumid.Assign(id);
    395  CoTaskMemFree(id);
    396 
    397  return NS_OK;
    398 }
    399 
    400 NS_IMETHODIMP
    401 nsWindowsShellService::CanSetDefaultBrowserUserChoice(bool* aResult) {
    402  *aResult = false;
    403 // If the WDBA is not available, this could never succeed.
    404 #ifdef MOZ_DEFAULT_BROWSER_AGENT
    405  bool progIDsExist = false;
    406  bool hashOk = false;
    407  *aResult = NS_SUCCEEDED(CheckAllProgIDsExist(&progIDsExist)) &&
    408             progIDsExist &&
    409             NS_SUCCEEDED(CheckBrowserUserChoiceHashes(&hashOk)) && hashOk;
    410 #endif
    411  return NS_OK;
    412 }
    413 
    414 nsresult nsWindowsShellService::LaunchModernSettingsDialogDefaultApps() {
    415  return ::LaunchModernSettingsDialogDefaultApps() ? NS_OK : NS_ERROR_FAILURE;
    416 }
    417 
    418 NS_IMETHODIMP
    419 nsWindowsShellService::SetDefaultBrowser(bool aForAllUsers) {
    420  // If running from within a package, don't attempt to set default with
    421  // the helper, as it will not work and will only confuse our package's
    422  // virtualized registry.
    423  nsresult rv = NS_OK;
    424  if (!widget::WinUtils::HasPackageIdentity()) {
    425    nsAutoString appHelperPath;
    426    if (NS_FAILED(GetHelperPath(appHelperPath))) return NS_ERROR_FAILURE;
    427 
    428    if (aForAllUsers) {
    429      appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal");
    430    } else {
    431      appHelperPath.AppendLiteral(" /SetAsDefaultAppUser");
    432    }
    433 
    434    rv = LaunchHelper(appHelperPath);
    435  }
    436 
    437  if (NS_SUCCEEDED(rv)) {
    438    rv = LaunchModernSettingsDialogDefaultApps();
    439    // The above call should never really fail, but just in case
    440    // fall back to showing control panel for all defaults
    441    if (NS_FAILED(rv)) {
    442      rv = LaunchControlPanelDefaultsSelectionUI();
    443    }
    444  }
    445 
    446  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
    447  if (prefs) {
    448    (void)prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
    449    // Reset the number of times the dialog should be shown
    450    // before it is silenced.
    451    (void)prefs->SetIntPref(PREF_DEFAULTBROWSERCHECKCOUNT, 0);
    452  }
    453 
    454  return rv;
    455 }
    456 
    457 /*
    458 * Asynchronous function to Write an ico file to the disk / in a nsIFile.
    459 * Limitation: Only square images are supported as of now.
    460 */
    461 NS_IMETHODIMP
    462 nsWindowsShellService::CreateWindowsIcon(nsIFile* aIcoFile,
    463                                         imgIContainer* aImage, JSContext* aCx,
    464                                         dom::Promise** aPromise) {
    465  NS_ENSURE_ARG_POINTER(aIcoFile);
    466  NS_ENSURE_ARG_POINTER(aImage);
    467  NS_ENSURE_ARG_POINTER(aCx);
    468  NS_ENSURE_ARG_POINTER(aPromise);
    469 
    470  if (!NS_IsMainThread()) {
    471    return NS_ERROR_NOT_SAME_THREAD;
    472  }
    473 
    474  ErrorResult rv;
    475  RefPtr<dom::Promise> promise =
    476      dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
    477 
    478  if (MOZ_UNLIKELY(rv.Failed())) {
    479    return rv.StealNSResult();
    480  }
    481 
    482  auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
    483      "CreateWindowsIcon promise", promise);
    484 
    485  MOZ_LOG(sLog, LogLevel::Debug,
    486          ("%s:%d - Reading input image...\n", __FILE__, __LINE__));
    487 
    488  // At present SVG frame retrieval defaults to 16x16, which will result in
    489  // small bordered icon in some contexts icons are used - e.g. pin to taskbar
    490  // notifications. To prevent this we retrieve the frame at 256x256. This only
    491  // works for SVGs, raster `imgIContainer` formats instead select the closest
    492  // matching size from existing frames.
    493  RefPtr<gfx::SourceSurface> surface =
    494      aImage->GetFrameAtSize(nsIntSize(256, 256), imgIContainer::FRAME_FIRST,
    495                             imgIContainer::FLAG_SYNC_DECODE |
    496                                 imgIContainer::FLAG_HIGH_QUALITY_SCALING);
    497  NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
    498 
    499  // At time of writing only `DataSourceSurface` was guaranteed thread safe. We
    500  // need this guarantee to write the icon file off the main thread.
    501  RefPtr<gfx::DataSourceSurface> dataSurface = surface->GetDataSurface();
    502  NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
    503 
    504  MOZ_LOG(sLog, LogLevel::Debug,
    505          ("%s:%d - Surface found, writing icon... \n", __FILE__, __LINE__));
    506 
    507  NS_DispatchBackgroundTask(
    508      NS_NewRunnableFunction(
    509          "CreateWindowsIcon",
    510          [icoFile = nsCOMPtr<nsIFile>(aIcoFile), dataSurface, promiseHolder] {
    511            nsresult rv = WriteIcon(icoFile, dataSurface);
    512 
    513            NS_DispatchToMainThread(NS_NewRunnableFunction(
    514                "CreateWindowsIcon callback", [rv, promiseHolder] {
    515                  dom::Promise* promise = promiseHolder.get()->get();
    516 
    517                  if (NS_SUCCEEDED(rv)) {
    518                    promise->MaybeResolveWithUndefined();
    519                  } else {
    520                    promise->MaybeReject(rv);
    521                  }
    522                }));
    523          }),
    524      NS_DISPATCH_EVENT_MAY_BLOCK);
    525 
    526  promise.forget(aPromise);
    527  return NS_OK;
    528 }
    529 
    530 static nsresult WriteIcon(nsIFile* aIcoFile, gfx::DataSourceSurface* aSurface) {
    531  NS_ENSURE_ARG(aIcoFile);
    532  NS_ENSURE_ARG(aSurface);
    533 
    534  const gfx::IntSize size = aSurface->GetSize();
    535  if (size.IsEmpty()) {
    536    MOZ_LOG(sLog, LogLevel::Debug,
    537            ("%s:%d - The input image looks empty :(\n", __FILE__, __LINE__));
    538    return NS_ERROR_FAILURE;
    539  }
    540 
    541  int32_t width = aSurface->GetSize().width;
    542  int32_t height = aSurface->GetSize().height;
    543 
    544  MOZ_LOG(sLog, LogLevel::Debug,
    545          ("%s:%d - Input image dimensions are: %dx%d pixels\n", __FILE__,
    546           __LINE__, width, height));
    547 
    548  NS_ENSURE_TRUE(height > 0, NS_ERROR_FAILURE);
    549  NS_ENSURE_TRUE(width > 0, NS_ERROR_FAILURE);
    550  NS_ENSURE_TRUE(width == height, NS_ERROR_FAILURE);
    551 
    552  MOZ_LOG(sLog, LogLevel::Debug,
    553          ("%s:%d - Opening file for writing...\n", __FILE__, __LINE__));
    554 
    555  ScopedCloseFile file;
    556  nsresult rv = aIcoFile->OpenANSIFileDesc("wb", getter_Transfers(file));
    557  NS_ENSURE_SUCCESS(rv, rv);
    558 
    559  MOZ_LOG(sLog, LogLevel::Debug,
    560          ("%s:%d - Writing icon...\n", __FILE__, __LINE__));
    561 
    562  rv = gfxUtils::EncodeSourceSurface(aSurface, ImageType::ICO, u""_ns,
    563                                     gfxUtils::eBinaryEncode, file.get());
    564 
    565  if (NS_FAILED(rv)) {
    566    MOZ_LOG(sLog, LogLevel::Debug,
    567            ("%s:%d - Could not write the icon!\n", __FILE__, __LINE__));
    568    return rv;
    569  }
    570 
    571  MOZ_LOG(sLog, LogLevel::Debug,
    572          ("%s:%d - Icon written!\n", __FILE__, __LINE__));
    573  return NS_OK;
    574 }
    575 
    576 static nsresult WriteBitmap(nsIFile* aFile, imgIContainer* aImage) {
    577  nsresult rv;
    578 
    579  RefPtr<gfx::SourceSurface> surface = aImage->GetFrame(
    580      imgIContainer::FRAME_FIRST, imgIContainer::FLAG_SYNC_DECODE);
    581  NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
    582 
    583  // For either of the following formats we want to set the biBitCount member
    584  // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
    585  // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
    586  // for the BI_RGB value we use for the biCompression member.
    587  MOZ_ASSERT(surface->GetFormat() == gfx::SurfaceFormat::B8G8R8A8 ||
    588             surface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
    589 
    590  RefPtr<gfx::DataSourceSurface> dataSurface = surface->GetDataSurface();
    591  NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
    592 
    593  int32_t width = dataSurface->GetSize().width;
    594  int32_t height = dataSurface->GetSize().height;
    595  int32_t bytesPerPixel = 4 * sizeof(uint8_t);
    596  uint32_t bytesPerRow = bytesPerPixel * width;
    597 
    598  // initialize these bitmap structs which we will later
    599  // serialize directly to the head of the bitmap file
    600  BITMAPINFOHEADER bmi;
    601  bmi.biSize = sizeof(BITMAPINFOHEADER);
    602  bmi.biWidth = width;
    603  bmi.biHeight = height;
    604  bmi.biPlanes = 1;
    605  bmi.biBitCount = (WORD)bytesPerPixel * 8;
    606  bmi.biCompression = BI_RGB;
    607  bmi.biSizeImage = bytesPerRow * height;
    608  bmi.biXPelsPerMeter = 0;
    609  bmi.biYPelsPerMeter = 0;
    610  bmi.biClrUsed = 0;
    611  bmi.biClrImportant = 0;
    612 
    613  BITMAPFILEHEADER bf;
    614  bf.bfType = 0x4D42;  // 'BM'
    615  bf.bfReserved1 = 0;
    616  bf.bfReserved2 = 0;
    617  bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    618  bf.bfSize = bf.bfOffBits + bmi.biSizeImage;
    619 
    620  // get a file output stream
    621  nsCOMPtr<nsIOutputStream> stream;
    622  rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
    623  NS_ENSURE_SUCCESS(rv, rv);
    624 
    625  // (redundant) guard clause to assert stream is initialized
    626  if (NS_WARN_IF(!stream)) {
    627    MOZ_ASSERT(stream, "rv should have failed when stream is not initialized.");
    628    return NS_ERROR_FAILURE;
    629  }
    630 
    631  gfx::DataSourceSurface::MappedSurface map;
    632  if (!dataSurface->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
    633    // removal of file created handled later
    634    rv = NS_ERROR_FAILURE;
    635  }
    636 
    637  // enter only if datasurface mapping succeeded
    638  if (NS_SUCCEEDED(rv)) {
    639    // write the bitmap headers and rgb pixel data to the file
    640    uint32_t written;
    641    rv = stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
    642    if (NS_SUCCEEDED(rv)) {
    643      rv = stream->Write((const char*)&bmi, sizeof(BITMAPINFOHEADER), &written);
    644      if (NS_SUCCEEDED(rv)) {
    645        // write out the image data backwards because the desktop won't
    646        // show bitmaps with negative heights for top-to-bottom
    647        uint32_t i = map.mStride * height;
    648        do {
    649          i -= map.mStride;
    650          rv = stream->Write(((const char*)map.mData) + i, bytesPerRow,
    651                             &written);
    652          if (NS_FAILED(rv)) {
    653            break;
    654          }
    655        } while (i != 0);
    656      }
    657    }
    658 
    659    dataSurface->Unmap();
    660  }
    661 
    662  stream->Close();
    663 
    664  // Obtaining the file output stream results in a newly created file or
    665  // truncates the file if it already exists. As such, it is necessary to
    666  // remove the file if the write fails for some reason.
    667  if (NS_FAILED(rv)) {
    668    if (NS_WARN_IF(NS_FAILED(aFile->Remove(PR_FALSE)))) {
    669      MOZ_LOG(sLog, LogLevel::Warning,
    670              ("Failed to remove empty bitmap file : %s",
    671               aFile->HumanReadablePath().get()));
    672    }
    673  }
    674 
    675  return rv;
    676 }
    677 
    678 NS_IMETHODIMP
    679 nsWindowsShellService::SetDesktopBackground(dom::Element* aElement,
    680                                            int32_t aPosition,
    681                                            const nsACString& aImageName) {
    682  if (!aElement || !aElement->IsHTMLElement(nsGkAtoms::img)) {
    683    // XXX write background loading stuff!
    684    return NS_ERROR_NOT_AVAILABLE;
    685  }
    686 
    687  nsresult rv;
    688  nsCOMPtr<nsIImageLoadingContent> imageContent =
    689      do_QueryInterface(aElement, &rv);
    690  if (!imageContent) return rv;
    691 
    692  // get the image container
    693  nsCOMPtr<imgIRequest> request;
    694  rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
    695                                getter_AddRefs(request));
    696  if (!request) return rv;
    697 
    698  nsCOMPtr<imgIContainer> container;
    699  rv = request->GetImage(getter_AddRefs(container));
    700  if (!container) return NS_ERROR_FAILURE;
    701 
    702  // get the file name from localized strings, e.g. "Desktop Background", then
    703  // append the extension (".bmp").
    704  nsTArray<nsCString> resIds = {
    705      "browser/setDesktopBackground.ftl"_ns,
    706  };
    707  RefPtr<Localization> l10n = Localization::Create(resIds, true);
    708  nsAutoCString fileLeafNameUtf8;
    709  IgnoredErrorResult locRv;
    710  l10n->FormatValueSync("set-desktop-background-filename"_ns, {},
    711                        fileLeafNameUtf8, locRv);
    712  nsAutoString fileLeafName = NS_ConvertUTF8toUTF16(fileLeafNameUtf8);
    713  fileLeafName.AppendLiteral(".bmp");
    714 
    715  // get the profile root directory
    716  nsCOMPtr<nsIFile> file;
    717  rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR,
    718                              getter_AddRefs(file));
    719  NS_ENSURE_SUCCESS(rv, rv);
    720 
    721  // eventually, the path is "%APPDATA%\Mozilla\Firefox\Desktop Background.bmp"
    722  rv = file->Append(fileLeafName);
    723  NS_ENSURE_SUCCESS(rv, rv);
    724 
    725  nsAutoString path;
    726  rv = file->GetPath(path);
    727  NS_ENSURE_SUCCESS(rv, rv);
    728 
    729  // write the bitmap to a file in the profile directory.
    730  // We have to write old bitmap format for Windows 7 wallpaper support.
    731  rv = WriteBitmap(file, container);
    732 
    733  // if the file was written successfully, set it as the system wallpaper
    734  if (NS_SUCCEEDED(rv)) {
    735    nsCOMPtr<nsIWindowsRegKey> regKey =
    736        do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
    737    NS_ENSURE_SUCCESS(rv, rv);
    738 
    739    rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
    740                        u"Control Panel\\Desktop"_ns,
    741                        nsIWindowsRegKey::ACCESS_SET_VALUE);
    742    NS_ENSURE_SUCCESS(rv, rv);
    743 
    744    nsAutoString tile;
    745    nsAutoString style;
    746    switch (aPosition) {
    747      case BACKGROUND_TILE:
    748        style.Assign('0');
    749        tile.Assign('1');
    750        break;
    751      case BACKGROUND_CENTER:
    752        style.Assign('0');
    753        tile.Assign('0');
    754        break;
    755      case BACKGROUND_STRETCH:
    756        style.Assign('2');
    757        tile.Assign('0');
    758        break;
    759      case BACKGROUND_FILL:
    760        style.AssignLiteral("10");
    761        tile.Assign('0');
    762        break;
    763      case BACKGROUND_FIT:
    764        style.Assign('6');
    765        tile.Assign('0');
    766        break;
    767      case BACKGROUND_SPAN:
    768        style.AssignLiteral("22");
    769        tile.Assign('0');
    770        break;
    771    }
    772 
    773    rv = regKey->WriteStringValue(u"TileWallpaper"_ns, tile);
    774    NS_ENSURE_SUCCESS(rv, rv);
    775    rv = regKey->WriteStringValue(u"WallpaperStyle"_ns, style);
    776    NS_ENSURE_SUCCESS(rv, rv);
    777    rv = regKey->Close();
    778    NS_ENSURE_SUCCESS(rv, rv);
    779 
    780    ::SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (PVOID)path.get(),
    781                            SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
    782  }
    783  return rv;
    784 }
    785 
    786 NS_IMETHODIMP
    787 nsWindowsShellService::GetDesktopBackgroundColor(uint32_t* aColor) {
    788  uint32_t color = ::GetSysColor(COLOR_DESKTOP);
    789  *aColor =
    790      (GetRValue(color) << 16) | (GetGValue(color) << 8) | GetBValue(color);
    791  return NS_OK;
    792 }
    793 
    794 NS_IMETHODIMP
    795 nsWindowsShellService::SetDesktopBackgroundColor(uint32_t aColor) {
    796  int aParameters[2] = {COLOR_BACKGROUND, COLOR_DESKTOP};
    797  BYTE r = (aColor >> 16);
    798  BYTE g = (aColor << 16) >> 24;
    799  BYTE b = (aColor << 24) >> 24;
    800  COLORREF colors[2] = {RGB(r, g, b), RGB(r, g, b)};
    801 
    802  ::SetSysColors(sizeof(aParameters) / sizeof(int), aParameters, colors);
    803 
    804  nsresult rv;
    805  nsCOMPtr<nsIWindowsRegKey> regKey =
    806      do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
    807  NS_ENSURE_SUCCESS(rv, rv);
    808 
    809  rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
    810                      u"Control Panel\\Colors"_ns,
    811                      nsIWindowsRegKey::ACCESS_SET_VALUE);
    812  NS_ENSURE_SUCCESS(rv, rv);
    813 
    814  wchar_t rgb[12];
    815  _snwprintf(rgb, 12, L"%u %u %u", r, g, b);
    816 
    817  rv = regKey->WriteStringValue(u"Background"_ns, nsDependentString(rgb));
    818  NS_ENSURE_SUCCESS(rv, rv);
    819 
    820  return regKey->Close();
    821 }
    822 
    823 enum class ShortcutsLogChange {
    824  Add,
    825  Remove,
    826 };
    827 
    828 /*
    829 * Updates information about a shortcut to a shortcuts log in
    830 * %PROGRAMDATA%\Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38.
    831 * (This is the same directory used for update staging.)
    832 * For more on the shortcuts log format and purpose, consult
    833 * /toolkit/mozapps/installer/windows/nsis/common.nsh.
    834 *
    835 * The shortcuts log modified here is named after the currently
    836 * running application and current user SID. For example:
    837 * Firefox_$SID_shortcuts.ini.
    838 *
    839 * A new file will be created when the first shortcut is added.
    840 * If a matching shortcut already exists, a new one will not
    841 * be appended. The file will not be deleted if the last one is
    842 * removed.
    843 *
    844 * In an ideal world this function would not need aShortcutsLogDir
    845 * passed to it, but it is called by at least one function that runs
    846 * asynchronously, and is therefore unable to use nsDirectoryService
    847 * to look it up itself.
    848 */
    849 static nsresult UpdateShortcutInLog(nsIFile* aShortcutsLogDir,
    850                                    KNOWNFOLDERID aFolderId,
    851                                    ShortcutsLogChange aChange,
    852                                    const nsAString& aShortcutRelativePath) {
    853  // the section inside the shortcuts log
    854  nsAutoCString section;
    855  // the shortcuts log wants "Programs" shortcuts in its "STARTMENU" section
    856  if (aFolderId == FOLDERID_CommonPrograms || aFolderId == FOLDERID_Programs) {
    857    section.Assign("STARTMENU");
    858  } else if (aFolderId == FOLDERID_PublicDesktop ||
    859             aFolderId == FOLDERID_Desktop) {
    860    section.Assign("DESKTOP");
    861  } else {
    862    return NS_ERROR_INVALID_ARG;
    863  }
    864 
    865  nsCOMPtr<nsIFile> shortcutsLog;
    866  nsresult rv = aShortcutsLogDir->GetParent(getter_AddRefs(shortcutsLog));
    867  NS_ENSURE_SUCCESS(rv, rv);
    868 
    869  nsAutoCString appName;
    870  nsCOMPtr<nsIXULAppInfo> appInfo =
    871      do_GetService("@mozilla.org/xre/app-info;1");
    872  rv = appInfo->GetName(appName);
    873  NS_ENSURE_SUCCESS(rv, rv);
    874 
    875  auto userSid = GetCurrentUserStringSid();
    876  if (!userSid) {
    877    return NS_ERROR_FILE_NOT_FOUND;
    878  }
    879 
    880  nsAutoString filename;
    881  filename.AppendPrintf("%s_%ls_shortcuts.ini", appName.get(), userSid.get());
    882  rv = shortcutsLog->Append(filename);
    883  NS_ENSURE_SUCCESS(rv, rv);
    884 
    885  nsINIParser parser;
    886  bool shortcutsLogEntryExists = false;
    887  nsAutoCString keyName, shortcutRelativePath, iniShortcut;
    888 
    889  shortcutRelativePath = NS_ConvertUTF16toUTF8(aShortcutRelativePath);
    890 
    891  // Last key that was valid.
    892  nsAutoCString lastValidKey;
    893  // Last key where the filename was found.
    894  nsAutoCString fileFoundAtKeyName;
    895 
    896  // If the shortcuts log exists, find either an existing matching
    897  // entry, or the next available shortcut index.
    898  rv = parser.Init(shortcutsLog);
    899  if (NS_SUCCEEDED(rv)) {
    900    for (int i = 0;; i++) {
    901      keyName.AssignLiteral("Shortcut");
    902      keyName.AppendInt(i);
    903      rv = parser.GetString(section.get(), keyName.get(), iniShortcut);
    904      if (NS_FAILED(rv) && rv != NS_ERROR_FAILURE) {
    905        return rv;
    906      }
    907 
    908      if (rv == NS_ERROR_FAILURE) {
    909        // This is the end of the file (as far as we're concerned.)
    910        break;
    911      } else if (iniShortcut.Equals(shortcutRelativePath)) {
    912        shortcutsLogEntryExists = true;
    913        fileFoundAtKeyName = keyName;
    914      }
    915 
    916      lastValidKey = keyName;
    917    }
    918  } else if (rv == NS_ERROR_FILE_NOT_FOUND) {
    919    // If the file doesn't exist, then start at Shortcut0.
    920    // When removing, this does nothing; when adding, this is always
    921    // a safe place to start.
    922    keyName.AssignLiteral("Shortcut0");
    923  } else {
    924    return rv;
    925  }
    926 
    927  bool changed = false;
    928  if (aChange == ShortcutsLogChange::Add && !shortcutsLogEntryExists) {
    929    parser.SetString(section.get(), keyName.get(), shortcutRelativePath.get());
    930    changed = true;
    931  } else if (aChange == ShortcutsLogChange::Remove && shortcutsLogEntryExists) {
    932    // Don't just remove it! The first missing index is considered
    933    // the end of the log. Instead, move the last one in, then delete
    934    // the last one, reducing the total length by one.
    935    parser.SetString(section.get(), fileFoundAtKeyName.get(),
    936                     iniShortcut.get());
    937    parser.DeleteString(section.get(), lastValidKey.get());
    938    changed = true;
    939  }
    940 
    941  if (changed) {
    942    // We write this ourselves instead of using parser->WriteToFile because
    943    // the INI parser in our uninstaller needs to read this, and only supports
    944    // UTF-16LE encoding. nsINIParser does not support UTF-16.
    945    nsAutoCString formatted;
    946    parser.WriteToString(formatted);
    947    FILE* writeFile;
    948    rv = shortcutsLog->OpenANSIFileDesc("w,ccs=UTF-16LE", &writeFile);
    949    NS_ENSURE_SUCCESS(rv, rv);
    950    NS_ConvertUTF8toUTF16 formattedUTF16(formatted);
    951    if (fwrite(formattedUTF16.get(), sizeof(wchar_t), formattedUTF16.Length(),
    952               writeFile) != formattedUTF16.Length()) {
    953      fclose(writeFile);
    954      return NS_ERROR_FAILURE;
    955    }
    956    fclose(writeFile);
    957  }
    958 
    959  return NS_OK;
    960 }
    961 
    962 nsresult CreateShellLinkObject(nsIFile* aBinary,
    963                               const CopyableTArray<nsString>& aArguments,
    964                               const nsAString& aDescription,
    965                               nsIFile* aIconFile, uint16_t aIconIndex,
    966                               const nsAString& aAppUserModelId,
    967                               IShellLinkW** aLink) {
    968  RefPtr<IShellLinkW> link;
    969  HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
    970                                IID_IShellLinkW, getter_AddRefs(link));
    971  NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
    972 
    973  nsString path(aBinary->NativePath());
    974  link->SetPath(path.get());
    975 
    976  wchar_t workingDir[MAX_PATH + 1];
    977  wcscpy_s(workingDir, MAX_PATH + 1, aBinary->NativePath().get());
    978  PathRemoveFileSpecW(workingDir);
    979  link->SetWorkingDirectory(workingDir);
    980 
    981  if (!aDescription.IsEmpty()) {
    982    link->SetDescription(PromiseFlatString(aDescription).get());
    983  }
    984 
    985  // TODO: Properly escape quotes in the string, see bug 1604287.
    986  nsString arguments;
    987  for (const auto& arg : aArguments) {
    988    arguments += u"\""_ns + arg + u"\" "_ns;
    989  }
    990 
    991  link->SetArguments(arguments.get());
    992 
    993  if (aIconFile) {
    994    nsString icon(aIconFile->NativePath());
    995    link->SetIconLocation(icon.get(), aIconIndex);
    996  }
    997 
    998  if (!aAppUserModelId.IsEmpty()) {
    999    RefPtr<IPropertyStore> propStore;
   1000    hr = link->QueryInterface(IID_IPropertyStore, getter_AddRefs(propStore));
   1001    NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
   1002 
   1003    PROPVARIANT pv;
   1004    if (FAILED(InitPropVariantFromString(
   1005            PromiseFlatString(aAppUserModelId).get(), &pv))) {
   1006      return NS_ERROR_FAILURE;
   1007    }
   1008 
   1009    hr = propStore->SetValue(PKEY_AppUserModel_ID, pv);
   1010    PropVariantClear(&pv);
   1011    NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
   1012 
   1013    hr = propStore->Commit();
   1014    NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
   1015  }
   1016 
   1017  link.forget(aLink);
   1018  return NS_OK;
   1019 }
   1020 
   1021 struct ShortcutLocations {
   1022  KNOWNFOLDERID folderId;
   1023  nsCOMPtr<nsIFile> shortcutsLogDir;
   1024  nsCOMPtr<nsIFile> shortcutFile;
   1025 };
   1026 
   1027 static nsresult CreateShortcutImpl(nsIFile* aBinary,
   1028                                   const CopyableTArray<nsString>& aArguments,
   1029                                   const nsAString& aDescription,
   1030                                   nsIFile* aIconFile, uint16_t aIconIndex,
   1031                                   const nsAString& aAppUserModelId,
   1032                                   const ShortcutLocations& location,
   1033                                   const nsAString& aShortcutRelativePath) {
   1034  NS_ENSURE_ARG(aBinary);
   1035  NS_ENSURE_ARG(aIconFile);
   1036 
   1037  nsresult rv =
   1038      UpdateShortcutInLog(location.shortcutsLogDir, location.folderId,
   1039                          ShortcutsLogChange::Add, aShortcutRelativePath);
   1040  NS_ENSURE_SUCCESS(rv, rv);
   1041 
   1042  RefPtr<IShellLinkW> link;
   1043  rv = CreateShellLinkObject(aBinary, aArguments, aDescription, aIconFile,
   1044                             aIconIndex, aAppUserModelId, getter_AddRefs(link));
   1045  NS_ENSURE_SUCCESS(rv, rv);
   1046 
   1047  RefPtr<IPersistFile> persist;
   1048  HRESULT hr = link->QueryInterface(IID_IPersistFile, getter_AddRefs(persist));
   1049  NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
   1050 
   1051  hr = persist->Save(location.shortcutFile->NativePath().get(), TRUE);
   1052  NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
   1053 
   1054  return NS_OK;
   1055 }
   1056 
   1057 static Result<ShortcutLocations, nsresult> GetShortcutPaths(
   1058    const nsAString& aShortcutFolder, const nsAString& aShortcutRelativePath) {
   1059  KNOWNFOLDERID folderId;
   1060  if (aShortcutFolder.Equals(L"Programs")) {
   1061    folderId = FOLDERID_Programs;
   1062  } else if (aShortcutFolder.Equals(L"Desktop")) {
   1063    folderId = FOLDERID_Desktop;
   1064  } else {
   1065    return Err(NS_ERROR_INVALID_ARG);
   1066  }
   1067 
   1068  nsCOMPtr<nsIFile> updRoot, shortcutsLogDir;
   1069  nsresult nsrv =
   1070      NS_GetSpecialDirectory(XRE_UPDATE_ROOT_DIR, getter_AddRefs(updRoot));
   1071  NS_ENSURE_SUCCESS(nsrv, Err(nsrv));
   1072  nsrv = updRoot->GetParent(getter_AddRefs(shortcutsLogDir));
   1073  NS_ENSURE_SUCCESS(nsrv, Err(nsrv));
   1074 
   1075  nsCOMPtr<nsIFile> shortcutFile;
   1076  if (folderId == FOLDERID_Programs) {
   1077    nsrv = NS_GetSpecialDirectory(NS_WIN_PROGRAMS_DIR,
   1078                                  getter_AddRefs(shortcutFile));
   1079  } else if (folderId == FOLDERID_Desktop) {
   1080    nsrv =
   1081        NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(shortcutFile));
   1082  } else {
   1083    return Err(NS_ERROR_FILE_NOT_FOUND);
   1084  }
   1085 
   1086  if (NS_FAILED(nsrv)) {
   1087    return Err(NS_ERROR_FILE_NOT_FOUND);
   1088  }
   1089 
   1090  nsrv = shortcutFile->AppendRelativePath(aShortcutRelativePath);
   1091  NS_ENSURE_SUCCESS(nsrv, Err(nsrv));
   1092 
   1093  ShortcutLocations result{};
   1094  result.folderId = folderId;
   1095  result.shortcutsLogDir = std::move(shortcutsLogDir);
   1096  result.shortcutFile = std::move(shortcutFile);
   1097  return result;
   1098 }
   1099 
   1100 NS_IMETHODIMP
   1101 nsWindowsShellService::CreateShortcut(nsIFile* aBinary,
   1102                                      const nsTArray<nsString>& aArguments,
   1103                                      const nsAString& aDescription,
   1104                                      nsIFile* aIconFile, uint16_t aIconIndex,
   1105                                      const nsAString& aAppUserModelId,
   1106                                      const nsAString& aShortcutFolder,
   1107                                      const nsAString& aShortcutRelativePath,
   1108                                      JSContext* aCx, dom::Promise** aPromise) {
   1109  if (!NS_IsMainThread()) {
   1110    return NS_ERROR_NOT_SAME_THREAD;
   1111  }
   1112 
   1113  ErrorResult rv;
   1114  RefPtr<dom::Promise> promise =
   1115      dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
   1116 
   1117  if (MOZ_UNLIKELY(rv.Failed())) {
   1118    return rv.StealNSResult();
   1119  }
   1120 
   1121  ShortcutLocations location =
   1122      MOZ_TRY(GetShortcutPaths(aShortcutFolder, aShortcutRelativePath));
   1123 
   1124  nsCOMPtr<nsIFile> parentDirectory;
   1125  nsresult nsrv;
   1126  nsrv = location.shortcutFile->GetParent(getter_AddRefs(parentDirectory));
   1127  NS_ENSURE_SUCCESS(nsrv, nsrv);
   1128  nsrv = parentDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
   1129  if (NS_FAILED(nsrv) && nsrv != NS_ERROR_FILE_ALREADY_EXISTS) {
   1130    return nsrv;
   1131  }
   1132 
   1133  auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
   1134      "CreateShortcut promise", promise);
   1135 
   1136  nsCOMPtr<nsIFile> binary(aBinary);
   1137  nsCOMPtr<nsIFile> iconFile(aIconFile);
   1138  NS_DispatchBackgroundTask(
   1139      NS_NewRunnableFunction(
   1140          "CreateShortcut",
   1141          [binary, aArguments = CopyableTArray<nsString>(aArguments),
   1142           aDescription = nsString{aDescription}, iconFile, aIconIndex,
   1143           aAppUserModelId = nsString{aAppUserModelId},
   1144           location = std::move(location),
   1145           aShortcutFolder = nsString{aShortcutFolder},
   1146           aShortcutRelativePath = nsString{aShortcutRelativePath},
   1147           promiseHolder = std::move(promiseHolder)] {
   1148            nsresult rv = CreateShortcutImpl(
   1149                binary.get(), aArguments, aDescription, iconFile.get(),
   1150                aIconIndex, aAppUserModelId, location, aShortcutRelativePath);
   1151 
   1152            NS_DispatchToMainThread(NS_NewRunnableFunction(
   1153                "CreateShortcut callback",
   1154                [rv, shortcutFile = location.shortcutFile,
   1155                 promiseHolder = std::move(promiseHolder)] {
   1156                  dom::Promise* promise = promiseHolder.get()->get();
   1157 
   1158                  if (NS_SUCCEEDED(rv)) {
   1159                    promise->MaybeResolve(shortcutFile->NativePath());
   1160                  } else {
   1161                    promise->MaybeReject(rv);
   1162                  }
   1163                }));
   1164          }),
   1165      NS_DISPATCH_EVENT_MAY_BLOCK);
   1166 
   1167  promise.forget(aPromise);
   1168  return NS_OK;
   1169 }
   1170 
   1171 static nsresult DeleteShortcutImpl(const ShortcutLocations& aLocation,
   1172                                   const nsAString& aShortcutRelativePath) {
   1173  // Do the removal first so an error keeps it in the log.
   1174  nsresult rv = aLocation.shortcutFile->Remove(false);
   1175  NS_ENSURE_SUCCESS(rv, rv);
   1176 
   1177  rv = UpdateShortcutInLog(aLocation.shortcutsLogDir, aLocation.folderId,
   1178                           ShortcutsLogChange::Remove, aShortcutRelativePath);
   1179  NS_ENSURE_SUCCESS(rv, rv);
   1180 
   1181  return NS_OK;
   1182 }
   1183 
   1184 NS_IMETHODIMP
   1185 nsWindowsShellService::DeleteShortcut(const nsAString& aShortcutFolder,
   1186                                      const nsAString& aShortcutRelativePath,
   1187                                      JSContext* aCx, dom::Promise** aPromise) {
   1188  if (!NS_IsMainThread()) {
   1189    return NS_ERROR_NOT_SAME_THREAD;
   1190  }
   1191 
   1192  ErrorResult rv;
   1193  RefPtr<dom::Promise> promise =
   1194      dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
   1195 
   1196  if (MOZ_UNLIKELY(rv.Failed())) {
   1197    return rv.StealNSResult();
   1198  }
   1199 
   1200  ShortcutLocations location =
   1201      MOZ_TRY(GetShortcutPaths(aShortcutFolder, aShortcutRelativePath));
   1202 
   1203  auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
   1204      "DeleteShortcut promise", promise);
   1205 
   1206  NS_DispatchBackgroundTask(
   1207      NS_NewRunnableFunction(
   1208          "DeleteShortcut",
   1209          [aShortcutFolder = nsString{aShortcutFolder},
   1210           aShortcutRelativePath = nsString{aShortcutRelativePath},
   1211           location = std::move(location),
   1212           promiseHolder = std::move(promiseHolder)] {
   1213            nsresult rv = DeleteShortcutImpl(location, aShortcutRelativePath);
   1214 
   1215            NS_DispatchToMainThread(NS_NewRunnableFunction(
   1216                "DeleteShortcut callback",
   1217                [rv, shortcutFile = location.shortcutFile,
   1218                 promiseHolder = std::move(promiseHolder)] {
   1219                  dom::Promise* promise = promiseHolder.get()->get();
   1220 
   1221                  if (NS_SUCCEEDED(rv)) {
   1222                    promise->MaybeResolve(shortcutFile->NativePath());
   1223                  } else {
   1224                    promise->MaybeReject(rv);
   1225                  }
   1226                }));
   1227          }),
   1228      NS_DISPATCH_EVENT_MAY_BLOCK);
   1229 
   1230  promise.forget(aPromise);
   1231  return NS_OK;
   1232 }
   1233 
   1234 NS_IMETHODIMP
   1235 nsWindowsShellService::GetLaunchOnLoginShortcuts(
   1236    nsTArray<nsString>& aShortcutPaths) {
   1237  aShortcutPaths.Clear();
   1238 
   1239  // Get AppData\\Roaming folder using a known folder ID
   1240  RefPtr<IKnownFolderManager> fManager;
   1241  RefPtr<IKnownFolder> roamingAppData;
   1242  LPWSTR roamingAppDataW;
   1243  nsString roamingAppDataNS;
   1244  HRESULT hr =
   1245      CoCreateInstance(CLSID_KnownFolderManager, nullptr, CLSCTX_INPROC_SERVER,
   1246                       IID_IKnownFolderManager, getter_AddRefs(fManager));
   1247  if (FAILED(hr)) {
   1248    return NS_ERROR_ABORT;
   1249  }
   1250  fManager->GetFolder(FOLDERID_RoamingAppData,
   1251                      roamingAppData.StartAssignment());
   1252  hr = roamingAppData->GetPath(0, &roamingAppDataW);
   1253  if (FAILED(hr)) {
   1254    return NS_ERROR_FILE_NOT_FOUND;
   1255  }
   1256 
   1257  // Append startup folder to AppData\\Roaming
   1258  roamingAppDataNS.Assign(roamingAppDataW);
   1259  CoTaskMemFree(roamingAppDataW);
   1260  nsString startupFolder =
   1261      roamingAppDataNS +
   1262      u"\\Microsoft\\Windows\\Start Menu\\Programs\\Startup"_ns;
   1263  nsString startupFolderWildcard = startupFolder + u"\\*.lnk"_ns;
   1264 
   1265  // Get known path for binary file for later comparison with shortcuts.
   1266  // Returns lowercase file path which should be fine for Windows as all
   1267  // directories and files are case-insensitive by default.
   1268  RefPtr<nsIFile> binFile;
   1269  nsString binPath;
   1270  nsresult rv = XRE_GetBinaryPath(binFile.StartAssignment());
   1271  if (FAILED(rv)) {
   1272    return NS_ERROR_FAILURE;
   1273  }
   1274  rv = binFile->GetPath(binPath);
   1275  if (FAILED(rv)) {
   1276    return NS_ERROR_FILE_UNRECOGNIZED_PATH;
   1277  }
   1278 
   1279  // Check for if first file exists with a shortcut extension (.lnk)
   1280  WIN32_FIND_DATAW ffd;
   1281  HANDLE fileHandle = INVALID_HANDLE_VALUE;
   1282  fileHandle = FindFirstFileW(startupFolderWildcard.get(), &ffd);
   1283  if (fileHandle == INVALID_HANDLE_VALUE) {
   1284    // This means that no files were found in the folder which
   1285    // doesn't imply an error. Most of the time the user won't
   1286    // have any shortcuts here.
   1287    return NS_OK;
   1288  }
   1289 
   1290  do {
   1291    // Extract shortcut target path from every
   1292    // shortcut in the startup folder.
   1293    nsString fileName(ffd.cFileName);
   1294    RefPtr<IShellLinkW> link;
   1295    RefPtr<IPersistFile> ppf;
   1296    nsString target;
   1297    target.SetLength(MAX_PATH);
   1298    CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
   1299                     IID_IShellLinkW, getter_AddRefs(link));
   1300    hr = link->QueryInterface(IID_IPersistFile, getter_AddRefs(ppf));
   1301    if (NS_WARN_IF(FAILED(hr))) {
   1302      continue;
   1303    }
   1304    nsString filePath = startupFolder + u"\\"_ns + fileName;
   1305    hr = ppf->Load(filePath.get(), STGM_READ);
   1306    if (NS_WARN_IF(FAILED(hr))) {
   1307      continue;
   1308    }
   1309    hr = link->GetPath(target.get(), MAX_PATH, nullptr, 0);
   1310    if (NS_WARN_IF(FAILED(hr))) {
   1311      continue;
   1312    }
   1313 
   1314    // If shortcut target matches known binary file value
   1315    // then add the path to the shortcut as a valid
   1316    // startup shortcut. This has to be a substring search as
   1317    // the user could have added unknown command line arguments
   1318    // to the shortcut.
   1319    if (_wcsnicmp(target.get(), binPath.get(), binPath.Length()) == 0) {
   1320      aShortcutPaths.AppendElement(filePath);
   1321    }
   1322  } while (FindNextFile(fileHandle, &ffd) != 0);
   1323  FindClose(fileHandle);
   1324  return NS_OK;
   1325 }
   1326 
   1327 // Look for any installer-created shortcuts in the given location that match
   1328 // the given AUMID and EXE Path. If one is found, output its path.
   1329 //
   1330 // NOTE: DO NOT USE if a false negative (mismatch) is unacceptable.
   1331 // aExePath is compared directly to the path retrieved from the shortcut.
   1332 // Due to the presence of symlinks or other filesystem issues, it's possible
   1333 // for different paths to refer to the same file, which would cause the check
   1334 // to fail.
   1335 // This should rarely be an issue as we are most likely to be run from a path
   1336 // written by the installer (shortcut, association, launch from installer),
   1337 // which also wrote the shortcuts. But it is possible.
   1338 //
   1339 // aCSIDL   the CSIDL of the directory to look for matching shortcuts in
   1340 // aAUMID   the AUMID to check for
   1341 // aExePath the target exe path to check for, should be a long path where
   1342 //          possible
   1343 // aShortcutSubstring a substring to limit which shortcuts in aCSIDL are
   1344 //                    inspected for a match. Only shortcuts whose filename
   1345 //                    contains this substring will be considered
   1346 // aShortcutPath outparam, set to matching shortcut path if NS_OK is returned.
   1347 //
   1348 // Returns
   1349 //   NS_ERROR_FAILURE on errors before any shortcuts were loaded
   1350 //   NS_ERROR_FILE_NOT_FOUND if no shortcuts matching aShortcutSubstring exist
   1351 //   NS_ERROR_FILE_ALREADY_EXISTS if shortcuts were found but did not match
   1352 //                                aAUMID or aExePath
   1353 //   NS_OK if a matching shortcut is found
   1354 static nsresult GetMatchingShortcut(int aCSIDL, const nsAString& aAUMID,
   1355                                    const wchar_t aExePath[MAXPATHLEN],
   1356                                    const nsAString& aShortcutSubstring,
   1357                                    /* out */ nsAutoString& aShortcutPath) {
   1358  nsresult result = NS_ERROR_FAILURE;
   1359 
   1360  wchar_t folderPath[MAX_PATH] = {};
   1361  HRESULT hr = SHGetFolderPathW(nullptr, aCSIDL, nullptr, SHGFP_TYPE_CURRENT,
   1362                                folderPath);
   1363  if (NS_WARN_IF(FAILED(hr))) {
   1364    return NS_ERROR_FAILURE;
   1365  }
   1366  if (wcscat_s(folderPath, MAX_PATH, L"\\") != 0) {
   1367    return NS_ERROR_FAILURE;
   1368  }
   1369 
   1370  // Get list of shortcuts in aCSIDL
   1371  nsAutoString pattern(folderPath);
   1372  pattern.AppendLiteral("*.lnk");
   1373 
   1374  WIN32_FIND_DATAW findData = {};
   1375  HANDLE hFindFile = FindFirstFileW(pattern.get(), &findData);
   1376  if (hFindFile == INVALID_HANDLE_VALUE) {
   1377    (void)NS_WARN_IF(GetLastError() != ERROR_FILE_NOT_FOUND);
   1378    return NS_ERROR_FILE_NOT_FOUND;
   1379  }
   1380  // Past this point we don't return until the end of the function,
   1381  // when FindClose() is called.
   1382 
   1383  // todo: improve return values here
   1384  do {
   1385    // Skip any that don't contain aShortcutSubstring
   1386    // This is a case sensitive comparison, but that's probably fine for
   1387    // the vast majority of cases -- and certainly for all the ones where
   1388    // a shortcut was created by the installer.
   1389    if (StrStrIW(findData.cFileName, aShortcutSubstring.Data()) == NULL) {
   1390      continue;
   1391    }
   1392 
   1393    nsAutoString path(folderPath);
   1394    path.Append(findData.cFileName);
   1395 
   1396    // Create a shell link object for loading the shortcut
   1397    RefPtr<IShellLinkW> link;
   1398    HRESULT hr =
   1399        CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
   1400                         IID_IShellLinkW, getter_AddRefs(link));
   1401    if (NS_WARN_IF(FAILED(hr))) {
   1402      continue;
   1403    }
   1404 
   1405    // Load
   1406    RefPtr<IPersistFile> persist;
   1407    hr = link->QueryInterface(IID_IPersistFile, getter_AddRefs(persist));
   1408    if (NS_WARN_IF(FAILED(hr))) {
   1409      continue;
   1410    }
   1411 
   1412    hr = persist->Load(path.get(), STGM_READ);
   1413    if (FAILED(hr)) {
   1414      if (NS_WARN_IF(hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))) {
   1415        // empty branch, result unchanged but warning issued
   1416      } else {
   1417        // If we've ever gotten past this block, result will already be
   1418        // NS_ERROR_FILE_ALREADY_EXISTS, which is a more accurate error
   1419        // than NS_ERROR_FILE_NOT_FOUND.
   1420        if (result != NS_ERROR_FILE_ALREADY_EXISTS) {
   1421          result = NS_ERROR_FILE_NOT_FOUND;
   1422        }
   1423      }
   1424      continue;
   1425    }
   1426    result = NS_ERROR_FILE_ALREADY_EXISTS;
   1427 
   1428    // Check the AUMID
   1429    RefPtr<IPropertyStore> propStore;
   1430    hr = link->QueryInterface(IID_IPropertyStore, getter_AddRefs(propStore));
   1431    if (NS_WARN_IF(FAILED(hr))) {
   1432      continue;
   1433    }
   1434 
   1435    PROPVARIANT pv;
   1436    hr = propStore->GetValue(PKEY_AppUserModel_ID, &pv);
   1437    if (NS_WARN_IF(FAILED(hr))) {
   1438      continue;
   1439    }
   1440 
   1441    wchar_t storedAUMID[MAX_PATH];
   1442    hr = PropVariantToString(pv, storedAUMID, MAX_PATH);
   1443    PropVariantClear(&pv);
   1444    if (NS_WARN_IF(FAILED(hr))) {
   1445      continue;
   1446    }
   1447 
   1448    if (!aAUMID.Equals(storedAUMID)) {
   1449      continue;
   1450    }
   1451 
   1452    // Check the exe path
   1453    static_assert(MAXPATHLEN == MAX_PATH);
   1454    wchar_t storedExePath[MAX_PATH] = {};
   1455    // With no flags GetPath gets a long path
   1456    hr = link->GetPath(storedExePath, std::size(storedExePath), nullptr, 0);
   1457    if (FAILED(hr) || hr == S_FALSE) {
   1458      continue;
   1459    }
   1460    // Case insensitive path comparison
   1461    if (wcsnicmp(storedExePath, aExePath, MAXPATHLEN) == 0) {
   1462      aShortcutPath.Assign(path);
   1463      result = NS_OK;
   1464      break;
   1465    }
   1466  } while (FindNextFileW(hFindFile, &findData));
   1467 
   1468  FindClose(hFindFile);
   1469 
   1470  return result;
   1471 }
   1472 
   1473 static nsresult FindPinnableShortcut(const nsAString& aAppUserModelId,
   1474                                     const nsAString& aShortcutSubstring,
   1475                                     const bool aPrivateBrowsing,
   1476                                     nsAutoString& aShortcutPath) {
   1477  wchar_t exePath[MAXPATHLEN] = {};
   1478  if (NS_WARN_IF(NS_FAILED(BinaryPath::GetLong(exePath)))) {
   1479    return NS_ERROR_FAILURE;
   1480  }
   1481 
   1482  if (aPrivateBrowsing) {
   1483    if (!PathRemoveFileSpecW(exePath)) {
   1484      return NS_ERROR_FAILURE;
   1485    }
   1486    if (!PathAppendW(exePath, L"private_browsing.exe")) {
   1487      return NS_ERROR_FAILURE;
   1488    }
   1489  }
   1490 
   1491  int shortcutCSIDLs[] = {CSIDL_COMMON_PROGRAMS, CSIDL_PROGRAMS};
   1492  for (int shortcutCSIDL : shortcutCSIDLs) {
   1493    // GetMatchingShortcut may fail when the exe path doesn't match, even
   1494    // if it refers to the same file. This should be rare, and the worst
   1495    // outcome would be failure to pin, so the risk is acceptable.
   1496    nsresult rv = GetMatchingShortcut(shortcutCSIDL, aAppUserModelId, exePath,
   1497                                      aShortcutSubstring, aShortcutPath);
   1498    if (NS_SUCCEEDED(rv)) {
   1499      return NS_OK;
   1500    }
   1501  }
   1502 
   1503  return NS_ERROR_FILE_NOT_FOUND;
   1504 }
   1505 
   1506 static bool HasPinnableShortcutImpl(const nsAString& aAppUserModelId,
   1507                                    const bool aPrivateBrowsing,
   1508                                    const nsAutoString& aShortcutSubstring) {
   1509  // unused by us, but required
   1510  nsAutoString shortcutPath;
   1511  nsresult rv = FindPinnableShortcut(aAppUserModelId, aShortcutSubstring,
   1512                                     aPrivateBrowsing, shortcutPath);
   1513  if (SUCCEEDED(rv)) {
   1514    return true;
   1515  }
   1516 
   1517  return false;
   1518 }
   1519 
   1520 NS_IMETHODIMP nsWindowsShellService::HasPinnableShortcut(
   1521    const nsAString& aAppUserModelId, const bool aPrivateBrowsing,
   1522    JSContext* aCx, dom::Promise** aPromise) {
   1523  if (!NS_IsMainThread()) {
   1524    return NS_ERROR_NOT_SAME_THREAD;
   1525  }
   1526 
   1527  ErrorResult rv;
   1528  RefPtr<dom::Promise> promise =
   1529      dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
   1530 
   1531  if (MOZ_UNLIKELY(rv.Failed())) {
   1532    return rv.StealNSResult();
   1533  }
   1534 
   1535  auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
   1536      "HasPinnableShortcut promise", promise);
   1537 
   1538  NS_DispatchBackgroundTask(
   1539      NS_NewRunnableFunction(
   1540          "HasPinnableShortcut",
   1541          [aAppUserModelId = nsString{aAppUserModelId}, aPrivateBrowsing,
   1542           promiseHolder = std::move(promiseHolder)] {
   1543            bool rv = false;
   1544            HRESULT hr = CoInitialize(nullptr);
   1545 
   1546            if (SUCCEEDED(hr)) {
   1547              nsAutoString shortcutSubstring;
   1548              shortcutSubstring.AssignLiteral(MOZ_APP_DISPLAYNAME);
   1549              rv = HasPinnableShortcutImpl(aAppUserModelId, aPrivateBrowsing,
   1550                                           shortcutSubstring);
   1551              CoUninitialize();
   1552            }
   1553 
   1554            NS_DispatchToMainThread(NS_NewRunnableFunction(
   1555                "HasPinnableShortcut callback",
   1556                [rv, promiseHolder = std::move(promiseHolder)] {
   1557                  dom::Promise* promise = promiseHolder.get()->get();
   1558 
   1559                  promise->MaybeResolve(rv);
   1560                }));
   1561          }),
   1562      NS_DISPATCH_EVENT_MAY_BLOCK);
   1563 
   1564  promise.forget(aPromise);
   1565  return NS_OK;
   1566 }
   1567 
   1568 static bool IsCurrentAppPinnedToTaskbarSync(const nsAString& aumid) {
   1569  // Use new Windows pinning APIs to determine whether or not we're pinned.
   1570  // If these fail we can safely fall back to the old method for regular
   1571  // installs however MSIX will always return false.
   1572 
   1573  // Bug 1911343: Add a check for whether we're looking for a regular pin
   1574  // or PB pin based on the AUMID value once private browser pinning
   1575  // is supported on MSIX.
   1576  // Right now only run this check on MSIX to avoid
   1577  // false positives when only private browsing is pinned.
   1578  if (widget::WinUtils::HasPackageIdentity()) {
   1579    auto pinWithWin11TaskbarAPIResults =
   1580        IsCurrentAppPinnedToTaskbarWin11(false);
   1581    switch (pinWithWin11TaskbarAPIResults.result) {
   1582      case Win11PinToTaskBarResultStatus::NotPinned:
   1583        return false;
   1584        break;
   1585      case Win11PinToTaskBarResultStatus::AlreadyPinned:
   1586        return true;
   1587        break;
   1588      default:
   1589        // Fall through to the old mechanism.
   1590        // The old mechanism should continue working for non-MSIX
   1591        // builds.
   1592        break;
   1593    }
   1594  }
   1595 
   1596  // There are two shortcut targets that we created. One always matches the
   1597  // binary we're running as (eg: firefox.exe). The other is the wrapper
   1598  // for launching in Private Browsing mode. We need to inspect shortcuts
   1599  // that point at either of these to accurately judge whether or not
   1600  // the app is pinned with the given AUMID.
   1601  wchar_t exePath[MAXPATHLEN] = {};
   1602  wchar_t pbExePath[MAXPATHLEN] = {};
   1603 
   1604  if (NS_WARN_IF(NS_FAILED(BinaryPath::GetLong(exePath)))) {
   1605    return false;
   1606  }
   1607 
   1608  wcscpy_s(pbExePath, MAXPATHLEN, exePath);
   1609  if (!PathRemoveFileSpecW(pbExePath)) {
   1610    return false;
   1611  }
   1612  if (!PathAppendW(pbExePath, L"private_browsing.exe")) {
   1613    return false;
   1614  }
   1615 
   1616  wchar_t folderChars[MAX_PATH] = {};
   1617  HRESULT hr = SHGetFolderPathW(nullptr, CSIDL_APPDATA, nullptr,
   1618                                SHGFP_TYPE_CURRENT, folderChars);
   1619  if (NS_WARN_IF(FAILED(hr))) {
   1620    return false;
   1621  }
   1622 
   1623  nsAutoString folder;
   1624  folder.Assign(folderChars);
   1625  if (NS_WARN_IF(folder.IsEmpty())) {
   1626    return false;
   1627  }
   1628  if (folder[folder.Length() - 1] != '\\') {
   1629    folder.AppendLiteral("\\");
   1630  }
   1631  folder.AppendLiteral(
   1632      "Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar");
   1633  nsAutoString pattern;
   1634  pattern.Assign(folder);
   1635  pattern.AppendLiteral("\\*.lnk");
   1636 
   1637  WIN32_FIND_DATAW findData = {};
   1638  HANDLE hFindFile = FindFirstFileW(pattern.get(), &findData);
   1639  if (hFindFile == INVALID_HANDLE_VALUE) {
   1640    (void)NS_WARN_IF(GetLastError() != ERROR_FILE_NOT_FOUND);
   1641    return false;
   1642  }
   1643  // Past this point we don't return until the end of the function,
   1644  // when FindClose() is called.
   1645 
   1646  // Check all shortcuts until a match is found
   1647  bool isPinned = false;
   1648  do {
   1649    nsAutoString fileName;
   1650    fileName.Assign(folder);
   1651    fileName.AppendLiteral("\\");
   1652    fileName.Append(findData.cFileName);
   1653 
   1654    // Create a shell link object for loading the shortcut
   1655    RefPtr<IShellLinkW> link;
   1656    HRESULT hr =
   1657        CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
   1658                         IID_IShellLinkW, getter_AddRefs(link));
   1659    if (NS_WARN_IF(FAILED(hr))) {
   1660      continue;
   1661    }
   1662 
   1663    // Load
   1664    RefPtr<IPersistFile> persist;
   1665    hr = link->QueryInterface(IID_IPersistFile, getter_AddRefs(persist));
   1666    if (NS_WARN_IF(FAILED(hr))) {
   1667      continue;
   1668    }
   1669 
   1670    hr = persist->Load(fileName.get(), STGM_READ);
   1671    if (NS_WARN_IF(FAILED(hr))) {
   1672      continue;
   1673    }
   1674 
   1675    // Check the exe path
   1676    static_assert(MAXPATHLEN == MAX_PATH);
   1677    wchar_t storedExePath[MAX_PATH] = {};
   1678    // With no flags GetPath gets a long path
   1679    hr = link->GetPath(storedExePath, std::size(storedExePath), nullptr, 0);
   1680    if (FAILED(hr) || hr == S_FALSE) {
   1681      continue;
   1682    }
   1683    // Case insensitive path comparison
   1684    // NOTE: Because this compares the path directly, it is possible to
   1685    // have a false negative mismatch.
   1686    if (wcsnicmp(storedExePath, exePath, MAXPATHLEN) == 0 ||
   1687        wcsnicmp(storedExePath, pbExePath, MAXPATHLEN) == 0) {
   1688      RefPtr<IPropertyStore> propStore;
   1689      hr = link->QueryInterface(IID_IPropertyStore, getter_AddRefs(propStore));
   1690      if (NS_WARN_IF(FAILED(hr))) {
   1691        continue;
   1692      }
   1693 
   1694      PROPVARIANT pv;
   1695      hr = propStore->GetValue(PKEY_AppUserModel_ID, &pv);
   1696      if (NS_WARN_IF(FAILED(hr))) {
   1697        continue;
   1698      }
   1699 
   1700      wchar_t storedAUMID[MAX_PATH];
   1701      hr = PropVariantToString(pv, storedAUMID, MAX_PATH);
   1702      PropVariantClear(&pv);
   1703      if (NS_WARN_IF(FAILED(hr))) {
   1704        continue;
   1705      }
   1706 
   1707      if (aumid.Equals(storedAUMID)) {
   1708        isPinned = true;
   1709        break;
   1710      }
   1711    }
   1712  } while (FindNextFileW(hFindFile, &findData));
   1713 
   1714  FindClose(hFindFile);
   1715 
   1716  return isPinned;
   1717 }
   1718 
   1719 static nsresult ManageShortcutTaskbarPins(bool aCheckOnly, bool aPinType,
   1720                                          const nsAString& aShortcutPath) {
   1721  // This enum is likely only used for Windows telemetry, INT_MAX is chosen to
   1722  // avoid confusion with existing uses.
   1723  enum PINNEDLISTMODIFYCALLER { PLMC_INT_MAX = INT_MAX };
   1724 
   1725  // The types below, and the idea of using IPinnedList3::Modify,
   1726  // are thanks to Gee Law <https://geelaw.blog/entries/msedge-pins/>
   1727  static constexpr GUID CLSID_TaskbandPin = {
   1728      0x90aa3a4e,
   1729      0x1cba,
   1730      0x4233,
   1731      {0xb8, 0xbb, 0x53, 0x57, 0x73, 0xd4, 0x84, 0x49}};
   1732 
   1733  static constexpr GUID IID_IPinnedList3 = {
   1734      0x0dd79ae2,
   1735      0xd156,
   1736      0x45d4,
   1737      {0x9e, 0xeb, 0x3b, 0x54, 0x97, 0x69, 0xe9, 0x40}};
   1738 
   1739  struct IPinnedList3Vtbl;
   1740  struct IPinnedList3 {
   1741    IPinnedList3Vtbl* vtbl;
   1742  };
   1743 
   1744  typedef ULONG STDMETHODCALLTYPE ReleaseFunc(IPinnedList3 * that);
   1745  typedef HRESULT STDMETHODCALLTYPE ModifyFunc(
   1746      IPinnedList3 * that, PCIDLIST_ABSOLUTE unpin, PCIDLIST_ABSOLUTE pin,
   1747      PINNEDLISTMODIFYCALLER caller);
   1748 
   1749  struct IPinnedList3Vtbl {
   1750    void* QueryInterface;  // 0
   1751    void* AddRef;          // 1
   1752    ReleaseFunc* Release;  // 2
   1753    void* Other[13];       // 3-15
   1754    ModifyFunc* Modify;    // 16
   1755  };
   1756 
   1757  struct ILFreeDeleter {
   1758    void operator()(LPITEMIDLIST aPtr) {
   1759      if (aPtr) {
   1760        ILFree(aPtr);
   1761      }
   1762    }
   1763  };
   1764 
   1765  mozilla::UniquePtr<__unaligned ITEMIDLIST, ILFreeDeleter> path(
   1766      ILCreateFromPathW(nsString(aShortcutPath).get()));
   1767  if (NS_WARN_IF(!path)) {
   1768    return NS_ERROR_FILE_NOT_FOUND;
   1769  }
   1770 
   1771  IPinnedList3* pinnedList = nullptr;
   1772  HRESULT hr = CoCreateInstance(CLSID_TaskbandPin, NULL, CLSCTX_INPROC_SERVER,
   1773                                IID_IPinnedList3, (void**)&pinnedList);
   1774  if (FAILED(hr) || !pinnedList) {
   1775    return NS_ERROR_NOT_AVAILABLE;
   1776  }
   1777 
   1778  if (!aCheckOnly) {
   1779    hr = pinnedList->vtbl->Modify(pinnedList, aPinType ? NULL : path.get(),
   1780                                  aPinType ? path.get() : NULL, PLMC_INT_MAX);
   1781  }
   1782 
   1783  pinnedList->vtbl->Release(pinnedList);
   1784 
   1785  if (FAILED(hr)) {
   1786    return NS_ERROR_FILE_ACCESS_DENIED;
   1787  }
   1788  return NS_OK;
   1789 }
   1790 
   1791 static nsresult PinShortcutToTaskbarImpl(bool aCheckOnly,
   1792                                         const nsAString& aAppUserModelId,
   1793                                         const nsAString& aShortcutPath) {
   1794  // Verify shortcut is visible to `shell:appsfolder`. Shortcut creation -
   1795  // during install or runtime - causes a race between it propagating to the
   1796  // virtual `shell:appsfolder` and attempts to pin via `ITaskbarManager`,
   1797  // resulting in pin failures when the latter occurs before the former. We can
   1798  // skip this when we're in a MSIX build or only checking whether we're pinned.
   1799  if (!widget::WinUtils::HasPackageIdentity() && !aCheckOnly &&
   1800      !PollAppsFolderForShortcut(aAppUserModelId,
   1801                                 TimeDuration::FromSeconds(15))) {
   1802    return NS_ERROR_FILE_NOT_FOUND;
   1803  }
   1804 
   1805  auto pinWithWin11TaskbarAPIResults =
   1806      PinCurrentAppToTaskbarWin11(aCheckOnly, aAppUserModelId);
   1807  switch (pinWithWin11TaskbarAPIResults.result) {
   1808    case Win11PinToTaskBarResultStatus::NotSupported:
   1809      // Fall through to the win 10 mechanism
   1810      break;
   1811 
   1812    case Win11PinToTaskBarResultStatus::Success:
   1813    case Win11PinToTaskBarResultStatus::AlreadyPinned:
   1814      return NS_OK;
   1815 
   1816    case Win11PinToTaskBarResultStatus::NotPinned:
   1817    case Win11PinToTaskBarResultStatus::NotCurrentlyAllowed:
   1818    case Win11PinToTaskBarResultStatus::Failed:
   1819      // return NS_ERROR_FAILURE;
   1820 
   1821      // Fall through to the old mechanism for now
   1822      // In future, we should be sending telemetry for when
   1823      // an error occurs or for when pinning is not allowed
   1824      // with the Win 11 APIs.
   1825      break;
   1826  }
   1827 
   1828  return PinCurrentAppToTaskbarWin10(aCheckOnly, aAppUserModelId,
   1829                                     aShortcutPath);
   1830 }
   1831 
   1832 /* This function pins a shortcut to the taskbar based on its location. While
   1833 * Windows 11 only needs the `aAppUserModelId`, `aShortcutPath` is required
   1834 * for pinning in Windows 10.
   1835 * @param aAppUserModelId
   1836 *        The same string used to create an lnk file.
   1837 * @param aShortcutPaths
   1838 *        Path for existing shortcuts (e.g., start menu)
   1839 */
   1840 NS_IMETHODIMP
   1841 nsWindowsShellService::PinShortcutToTaskbar(
   1842    const nsAString& aAppUserModelId, const nsAString& aShortcutFolder,
   1843    const nsAString& aShortcutRelativePath, JSContext* aCx,
   1844    dom::Promise** aPromise) {
   1845  NS_ENSURE_ARG_POINTER(aCx);
   1846  NS_ENSURE_ARG_POINTER(aPromise);
   1847 
   1848  if (!NS_IsMainThread()) {
   1849    return NS_ERROR_NOT_SAME_THREAD;
   1850  }
   1851 
   1852  // First available on 1809
   1853  if (!IsWin10Sep2018UpdateOrLater()) {
   1854    return NS_ERROR_NOT_AVAILABLE;
   1855  }
   1856 
   1857  ErrorResult rv;
   1858  RefPtr<dom::Promise> promise =
   1859      dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
   1860 
   1861  if (MOZ_UNLIKELY(rv.Failed())) {
   1862    return rv.StealNSResult();
   1863  }
   1864 
   1865  ShortcutLocations location =
   1866      MOZ_TRY(GetShortcutPaths(aShortcutFolder, aShortcutRelativePath));
   1867 
   1868  auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
   1869      "pinShortcutToTaskbar promise", promise);
   1870 
   1871  NS_DispatchBackgroundTask(
   1872      NS_NewRunnableFunction(
   1873          "pinShortcutToTaskbar",
   1874          [aumid = nsString{aAppUserModelId}, location = std::move(location),
   1875           promiseHolder = std::move(promiseHolder)] {
   1876            nsresult rv = NS_ERROR_FAILURE;
   1877            HRESULT hr = CoInitialize(nullptr);
   1878 
   1879            if (SUCCEEDED(hr)) {
   1880              rv = PinShortcutToTaskbarImpl(
   1881                  false, aumid, location.shortcutFile->NativePath());
   1882              CoUninitialize();
   1883            }
   1884 
   1885            NS_DispatchToMainThread(NS_NewRunnableFunction(
   1886                "pinShortcutToTaskbar callback",
   1887                [rv, promiseHolder = std::move(promiseHolder)] {
   1888                  dom::Promise* promise = promiseHolder.get()->get();
   1889 
   1890                  if (NS_SUCCEEDED(rv)) {
   1891                    promise->MaybeResolveWithUndefined();
   1892                  } else {
   1893                    promise->MaybeReject(rv);
   1894                  }
   1895                }));
   1896          }),
   1897      NS_DISPATCH_EVENT_MAY_BLOCK);
   1898 
   1899  promise.forget(aPromise);
   1900  return NS_OK;
   1901 }
   1902 
   1903 NS_IMETHODIMP
   1904 nsWindowsShellService::UnpinShortcutFromTaskbar(
   1905    const nsAString& aShortcutFolder, const nsAString& aShortcutRelativePath) {
   1906  const bool pinType = false;  // false means unpin
   1907  const bool runInTestMode = false;
   1908 
   1909  ShortcutLocations location =
   1910      MOZ_TRY(GetShortcutPaths(aShortcutFolder, aShortcutRelativePath));
   1911 
   1912  return ManageShortcutTaskbarPins(runInTestMode, pinType,
   1913                                   location.shortcutFile->NativePath());
   1914 }
   1915 
   1916 static nsresult PinCurrentAppToTaskbarWin10(bool aCheckOnly,
   1917                                            const nsAString& aAppUserModelId,
   1918                                            const nsAString& aShortcutPath) {
   1919  // The behavior here is identical if we're only checking or if we try to pin
   1920  // but the app is already pinned so we update the variable accordingly.
   1921  if (!aCheckOnly) {
   1922    aCheckOnly = IsCurrentAppPinnedToTaskbarSync(aAppUserModelId);
   1923  }
   1924  const bool pinType = true;  // true means pin
   1925  return ManageShortcutTaskbarPins(aCheckOnly, pinType, aShortcutPath);
   1926 }
   1927 
   1928 // There's a delay between shortcuts being created in locations visible to
   1929 // `shell:appsfolder` and that information being propogated to
   1930 // `shell:appsfolder`. APIs like `ITaskbarManager` pinning rely on said
   1931 // shortcuts being visible to `shell:appsfolder`, so we have to introduce a wait
   1932 // until they're visible when creating these shortcuts at runtime.
   1933 static bool PollAppsFolderForShortcut(const nsAString& aAppUserModelId,
   1934                                      const TimeDuration aTimeout) {
   1935  MOZ_DIAGNOSTIC_ASSERT(!NS_IsMainThread(),
   1936                        "PollAppsFolderForShortcut blocks and should be called "
   1937                        "off main thread only");
   1938 
   1939  // Implementation note: it was taken into consideration at the time of writing
   1940  // to implement this with `SHChangeNotifyRegister` and a `HWND_MESSAGE`
   1941  // window. This added significant complexity in terms of resource management
   1942  // and control flow that was deemed excessive for a function that is rarely
   1943  // run. Absent evidence that we're consuming excessive system resources, this
   1944  // simple, poll-based approach seemed more appropriate.
   1945  //
   1946  // If in the future it seems appropriate to modify this to be event based,
   1947  // here are some of the lessons learned during the investigation:
   1948  //   - `shell:appsfolder` is a virtual directory, composed of shortcut files
   1949  //     with unique AUMIDs from
   1950  //     `[%PROGRAMDATA%|%APPDATA%]\Microsoft\Windows\Start Menu\Programs`.
   1951  //   - `shell:appsfolder` does not have a full path in the filesystem,
   1952  //     therefore does not work with most file watching APIs.
   1953  //   - `SHChangeNotifyRegister` should listen for `SHCNE_UPDATEDIR` on
   1954  //     `FOLDERID_AppsFolder`. `SHCNE_CREATE` events are not issued for
   1955  //     shortcuts added to `FOLDERID_AppsFolder` likely due to it's virtual
   1956  //     nature.
   1957  //   - The mechanism for inspecting the `shell:appsfolder` for a shortcut with
   1958  //     matching AUMID is the same in an event-based implementation due to
   1959  //     `SHCNE_UPDATEDIR` events include the modified folder, but not the
   1960  //     modified file.
   1961 
   1962  TimeStamp start = TimeStamp::Now();
   1963 
   1964  ComPtr<IShellItem> appsFolder;
   1965  HRESULT hr = SHGetKnownFolderItem(FOLDERID_AppsFolder, KF_FLAG_DEFAULT,
   1966                                    nullptr, IID_PPV_ARGS(&appsFolder));
   1967  if (FAILED(hr)) {
   1968    return false;
   1969  }
   1970 
   1971  do {
   1972    // It's possible to have identically named files in `shell:appsfolder` as
   1973    // it's disambiguated by AUMID instead of file name, so we have to iterate
   1974    // over all items instead of querying the specific shortcut.
   1975    ComPtr<IEnumShellItems> shortcutIter;
   1976    hr = appsFolder->BindToHandler(nullptr, BHID_EnumItems,
   1977                                   IID_PPV_ARGS(&shortcutIter));
   1978    if (FAILED(hr)) {
   1979      return false;
   1980    }
   1981 
   1982    ComPtr<IShellItem> shortcut;
   1983    while (shortcutIter->Next(1, &shortcut, nullptr) == S_OK) {
   1984      ComPtr<IShellItem2> shortcut2;
   1985      hr = shortcut.As(&shortcut2);
   1986      if (FAILED(hr)) {
   1987        return false;
   1988      }
   1989 
   1990      mozilla::UniquePtr<WCHAR, mozilla::CoTaskMemFreeDeleter> shortcutAumid;
   1991      hr = shortcut2->GetString(PKEY_AppUserModel_ID,
   1992                                getter_Transfers(shortcutAumid));
   1993      if (FAILED(hr)) {
   1994        // `shell:appsfolder` is populated by unique shortcut AUMID; if this is
   1995        // absent something has gone wrong and we should exit.
   1996        return false;
   1997      }
   1998 
   1999      if (aAppUserModelId == nsDependentString(shortcutAumid.get())) {
   2000        return true;
   2001      }
   2002    }
   2003 
   2004    // Sleep for a quarter of a second to avoid pinning the CPU while waiting.
   2005    ::Sleep(250);
   2006  } while ((TimeStamp::Now() - start) < aTimeout);
   2007 
   2008  return false;
   2009 }
   2010 
   2011 static nsresult PinCurrentAppToTaskbarImpl(
   2012    bool aCheckOnly, bool aPrivateBrowsing, const nsAString& aAppUserModelId,
   2013    const nsAString& aShortcutName, const nsAString& aShortcutSubstring,
   2014    nsIFile* aGreDir, const ShortcutLocations& location) {
   2015  MOZ_DIAGNOSTIC_ASSERT(
   2016      !NS_IsMainThread(),
   2017      "PinCurrentAppToTaskbarImpl should be called off main thread only");
   2018 
   2019  nsAutoString shortcutPath;
   2020  nsresult rv = FindPinnableShortcut(aAppUserModelId, aShortcutSubstring,
   2021                                     aPrivateBrowsing, shortcutPath);
   2022  if (NS_FAILED(rv)) {
   2023    shortcutPath.Truncate();
   2024  }
   2025  if (shortcutPath.IsEmpty()) {
   2026    if (aCheckOnly) {
   2027      // Later checks rely on a shortcut already existing.
   2028      // We don't want to create a shortcut in check only mode
   2029      // so the best we can do is assume those parts will work.
   2030      return NS_OK;
   2031    }
   2032 
   2033    nsAutoString linkName(aShortcutName);
   2034 
   2035    nsCOMPtr<nsIFile> exeFile(aGreDir);
   2036    if (aPrivateBrowsing) {
   2037      nsAutoString pbExeStr(PRIVATE_BROWSING_BINARY);
   2038      nsresult rv = exeFile->Append(pbExeStr);
   2039      if (!NS_SUCCEEDED(rv)) {
   2040        return NS_ERROR_FAILURE;
   2041      }
   2042    } else {
   2043      wchar_t exePath[MAXPATHLEN] = {};
   2044      if (NS_WARN_IF(NS_FAILED(BinaryPath::GetLong(exePath)))) {
   2045        return NS_ERROR_FAILURE;
   2046      }
   2047      nsAutoString exeStr(exePath);
   2048      nsresult rv = NS_NewLocalFile(exeStr, getter_AddRefs(exeFile));
   2049      if (!NS_SUCCEEDED(rv)) {
   2050        return NS_ERROR_FILE_NOT_FOUND;
   2051      }
   2052    }
   2053 
   2054    nsTArray<nsString> arguments;
   2055    rv = CreateShortcutImpl(exeFile, arguments, aShortcutName, exeFile,
   2056                            // Icon indexes are defined as Resource IDs, but
   2057                            // CreateShortcutImpl needs an index.
   2058                            IDI_APPICON - 1, aAppUserModelId, location,
   2059                            linkName);
   2060    if (!NS_SUCCEEDED(rv)) {
   2061      return NS_ERROR_FILE_NOT_FOUND;
   2062    }
   2063  }
   2064  return PinShortcutToTaskbarImpl(aCheckOnly, aAppUserModelId, shortcutPath);
   2065 }
   2066 
   2067 static nsresult PinCurrentAppToTaskbarAsyncImpl(bool aCheckOnly,
   2068                                                bool aPrivateBrowsing,
   2069                                                JSContext* aCx,
   2070                                                dom::Promise** aPromise) {
   2071  if (!NS_IsMainThread()) {
   2072    return NS_ERROR_NOT_SAME_THREAD;
   2073  }
   2074 
   2075  // First available on 1809
   2076  if (!IsWin10Sep2018UpdateOrLater()) {
   2077    return NS_ERROR_NOT_AVAILABLE;
   2078  }
   2079 
   2080  ErrorResult rv;
   2081  RefPtr<dom::Promise> promise =
   2082      dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
   2083 
   2084  if (MOZ_UNLIKELY(rv.Failed())) {
   2085    return rv.StealNSResult();
   2086  }
   2087 
   2088  nsAutoString aumid;
   2089  if (NS_WARN_IF(!mozilla::widget::WinTaskbar::GetAppUserModelID(
   2090          aumid, aPrivateBrowsing))) {
   2091    return NS_ERROR_FAILURE;
   2092  }
   2093 
   2094  // NOTE: In the installer, non-private shortcuts are named
   2095  // "${BrandShortName}.lnk". This is set from MOZ_APP_DISPLAYNAME in
   2096  // defines.nsi.in. (Except in dev edition where it's explicitly set to
   2097  // "Firefox Developer Edition" in branding.nsi, which matches
   2098  // MOZ_APP_DISPLAYNAME in aurora/configure.sh.)
   2099  //
   2100  // If this changes, we could expand this to check shortcuts_log.ini,
   2101  // which records the name of the shortcuts as created by the installer.
   2102  //
   2103  // Private shortcuts are not created by the installer (they're created
   2104  // upon user request, ultimately by CreateShortcutImpl, and recorded in
   2105  // a separate shortcuts log. As with non-private shortcuts they have a known
   2106  // name - so there's no need to look through logs to find them.
   2107  nsAutoString shortcutName;
   2108  if (aPrivateBrowsing) {
   2109    nsTArray<nsCString> resIds = {
   2110        "branding/brand.ftl"_ns,
   2111        "browser/browser.ftl"_ns,
   2112    };
   2113    RefPtr<Localization> l10n = Localization::Create(resIds, true);
   2114    nsAutoCString pbStr;
   2115    IgnoredErrorResult rv;
   2116    l10n->FormatValueSync("private-browsing-shortcut-text-2"_ns, {}, pbStr, rv);
   2117    shortcutName.Append(NS_ConvertUTF8toUTF16(pbStr));
   2118    shortcutName.AppendLiteral(".lnk");
   2119  } else {
   2120    shortcutName.AppendLiteral(MOZ_APP_DISPLAYNAME ".lnk");
   2121  }
   2122 
   2123  nsCOMPtr<nsIFile> greDir;
   2124  nsresult nsrv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir));
   2125  NS_ENSURE_SUCCESS(nsrv, nsrv);
   2126 
   2127  ShortcutLocations location =
   2128      MOZ_TRY(GetShortcutPaths(nsString(L"Programs"), shortcutName));
   2129 
   2130  auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
   2131      "CheckPinCurrentAppToTaskbarAsync promise", promise);
   2132 
   2133  NS_DispatchBackgroundTask(
   2134      NS_NewRunnableFunction(
   2135          "CheckPinCurrentAppToTaskbarAsync",
   2136          [aCheckOnly, aPrivateBrowsing, shortcutName, aumid = nsString{aumid},
   2137           greDir, location = std::move(location),
   2138           promiseHolder = std::move(promiseHolder)] {
   2139            nsresult rv = NS_ERROR_FAILURE;
   2140            HRESULT hr = CoInitialize(nullptr);
   2141 
   2142            if (SUCCEEDED(hr)) {
   2143              nsAutoString shortcutSubstring;
   2144              shortcutSubstring.AssignLiteral(MOZ_APP_DISPLAYNAME);
   2145              rv = PinCurrentAppToTaskbarImpl(
   2146                  aCheckOnly, aPrivateBrowsing, aumid, shortcutName,
   2147                  shortcutSubstring, greDir.get(), location);
   2148              CoUninitialize();
   2149            }
   2150 
   2151            NS_DispatchToMainThread(NS_NewRunnableFunction(
   2152                "CheckPinCurrentAppToTaskbarAsync callback",
   2153                [rv, promiseHolder = std::move(promiseHolder)] {
   2154                  dom::Promise* promise = promiseHolder.get()->get();
   2155 
   2156                  if (NS_SUCCEEDED(rv)) {
   2157                    promise->MaybeResolveWithUndefined();
   2158                  } else {
   2159                    promise->MaybeReject(rv);
   2160                  }
   2161                }));
   2162          }),
   2163      NS_DISPATCH_EVENT_MAY_BLOCK);
   2164 
   2165  promise.forget(aPromise);
   2166  return NS_OK;
   2167 }
   2168 
   2169 NS_IMETHODIMP
   2170 nsWindowsShellService::PinCurrentAppToTaskbarAsync(bool aPrivateBrowsing,
   2171                                                   JSContext* aCx,
   2172                                                   dom::Promise** aPromise) {
   2173  return PinCurrentAppToTaskbarAsyncImpl(
   2174      /* aCheckOnly */ false, aPrivateBrowsing, aCx, aPromise);
   2175 }
   2176 
   2177 NS_IMETHODIMP
   2178 nsWindowsShellService::CheckPinCurrentAppToTaskbarAsync(
   2179    bool aPrivateBrowsing, JSContext* aCx, dom::Promise** aPromise) {
   2180  return PinCurrentAppToTaskbarAsyncImpl(
   2181      /* aCheckOnly = */ true, aPrivateBrowsing, aCx, aPromise);
   2182 }
   2183 
   2184 NS_IMETHODIMP
   2185 nsWindowsShellService::IsCurrentAppPinnedToTaskbarAsync(
   2186    const nsAString& aumid, JSContext* aCx, /* out */ dom::Promise** aPromise) {
   2187  if (!NS_IsMainThread()) {
   2188    return NS_ERROR_NOT_SAME_THREAD;
   2189  }
   2190 
   2191  ErrorResult rv;
   2192  RefPtr<dom::Promise> promise =
   2193      dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
   2194  if (MOZ_UNLIKELY(rv.Failed())) {
   2195    return rv.StealNSResult();
   2196  }
   2197 
   2198  // A holder to pass the promise through the background task and back to
   2199  // the main thread when finished.
   2200  auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
   2201      "IsCurrentAppPinnedToTaskbarAsync promise", promise);
   2202 
   2203  // nsAString can't be captured by a lambda because it does not have a
   2204  // public copy constructor
   2205  nsAutoString capturedAumid(aumid);
   2206  NS_DispatchBackgroundTask(
   2207      NS_NewRunnableFunction(
   2208          "IsCurrentAppPinnedToTaskbarAsync",
   2209          [capturedAumid, promiseHolder = std::move(promiseHolder)] {
   2210            bool isPinned = false;
   2211 
   2212            HRESULT hr = CoInitialize(nullptr);
   2213            if (SUCCEEDED(hr)) {
   2214              isPinned = IsCurrentAppPinnedToTaskbarSync(capturedAumid);
   2215              CoUninitialize();
   2216            }
   2217 
   2218            // Dispatch back to the main thread to resolve the promise.
   2219            NS_DispatchToMainThread(NS_NewRunnableFunction(
   2220                "IsCurrentAppPinnedToTaskbarAsync callback",
   2221                [isPinned, promiseHolder = std::move(promiseHolder)] {
   2222                  promiseHolder.get()->get()->MaybeResolve(isPinned);
   2223                }));
   2224          }),
   2225      NS_DISPATCH_EVENT_MAY_BLOCK);
   2226 
   2227  promise.forget(aPromise);
   2228  return NS_OK;
   2229 }
   2230 
   2231 #ifndef __MINGW32__
   2232 #  define RESOLVE_AND_RETURN(HOLDER, RESOLVE, RETURN)                \
   2233    NS_DispatchToMainThread(NS_NewRunnableFunction(                  \
   2234        __func__, [resolveVal = (RESOLVE), promiseHolder = HOLDER] { \
   2235          promiseHolder.get()->get()->MaybeResolve(resolveVal);      \
   2236        }));                                                         \
   2237    return RETURN
   2238 
   2239 #  define REJECT_AND_RETURN(HOLDER, REJECT, RETURN)                 \
   2240    NS_DispatchToMainThread(                                        \
   2241        NS_NewRunnableFunction(__func__, [promiseHolder = HOLDER] { \
   2242          promiseHolder.get()->get()->MaybeReject(REJECT);          \
   2243        }));                                                        \
   2244    return RETURN
   2245 
   2246 static void EnableLaunchOnLoginMSIXAsyncImpl(
   2247    const nsString& capturedTaskId,
   2248    const RefPtr<nsMainThreadPtrHolder<dom::Promise>> promiseHolder) {
   2249  ComPtr<IStartupTaskStatics> startupTaskStatics;
   2250  HRESULT hr = GetActivationFactory(
   2251      HStringReference(RuntimeClass_Windows_ApplicationModel_StartupTask).Get(),
   2252      &startupTaskStatics);
   2253  if (FAILED(hr)) {
   2254    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2255  }
   2256  ComPtr<IAsyncOperation<StartupTask*>> getTaskOperation = nullptr;
   2257  hr = startupTaskStatics->GetAsync(
   2258      HStringReference(capturedTaskId.get()).Get(), &getTaskOperation);
   2259  if (FAILED(hr)) {
   2260    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2261  }
   2262  auto getTaskCallback =
   2263      Callback<IAsyncOperationCompletedHandler<StartupTask*>>(
   2264          [promiseHolder](IAsyncOperation<StartupTask*>* operation,
   2265                          AsyncStatus status) -> HRESULT {
   2266            if (status != AsyncStatus::Completed) {
   2267              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2268            }
   2269            ComPtr<IStartupTask> startupTask;
   2270            HRESULT hr = operation->GetResults(&startupTask);
   2271            if (FAILED(hr)) {
   2272              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2273            }
   2274            ComPtr<IAsyncOperation<StartupTaskState>> enableOperation;
   2275            hr = startupTask->RequestEnableAsync(&enableOperation);
   2276            if (FAILED(hr)) {
   2277              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2278            }
   2279            // Set another callback for enabling the startup task
   2280            auto enableHandler =
   2281                Callback<IAsyncOperationCompletedHandler<StartupTaskState>>(
   2282                    [promiseHolder](
   2283                        IAsyncOperation<StartupTaskState>* operation,
   2284                        AsyncStatus status) -> HRESULT {
   2285                      StartupTaskState resultState;
   2286                      HRESULT hr = operation->GetResults(&resultState);
   2287                      if (SUCCEEDED(hr) && status == AsyncStatus::Completed) {
   2288                        RESOLVE_AND_RETURN(promiseHolder, true, S_OK);
   2289                      }
   2290                      RESOLVE_AND_RETURN(promiseHolder, false, S_OK);
   2291                    });
   2292            hr = enableOperation->put_Completed(enableHandler.Get());
   2293            if (FAILED(hr)) {
   2294              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, hr);
   2295            }
   2296            return hr;
   2297          });
   2298  hr = getTaskOperation->put_Completed(getTaskCallback.Get());
   2299  if (FAILED(hr)) {
   2300    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2301  }
   2302 }
   2303 
   2304 static void DisableLaunchOnLoginMSIXAsyncImpl(
   2305    const nsString& capturedTaskId,
   2306    const RefPtr<nsMainThreadPtrHolder<dom::Promise>> promiseHolder) {
   2307  ComPtr<IStartupTaskStatics> startupTaskStatics;
   2308  HRESULT hr = GetActivationFactory(
   2309      HStringReference(RuntimeClass_Windows_ApplicationModel_StartupTask).Get(),
   2310      &startupTaskStatics);
   2311  if (FAILED(hr)) {
   2312    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2313  }
   2314  ComPtr<IAsyncOperation<StartupTask*>> getTaskOperation = nullptr;
   2315  hr = startupTaskStatics->GetAsync(
   2316      HStringReference(capturedTaskId.get()).Get(), &getTaskOperation);
   2317  if (FAILED(hr)) {
   2318    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2319  }
   2320  auto getTaskCallback =
   2321      Callback<IAsyncOperationCompletedHandler<StartupTask*>>(
   2322          [promiseHolder](IAsyncOperation<StartupTask*>* operation,
   2323                          AsyncStatus status) -> HRESULT {
   2324            if (status != AsyncStatus::Completed) {
   2325              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2326            }
   2327            ComPtr<IStartupTask> startupTask;
   2328            HRESULT hr = operation->GetResults(&startupTask);
   2329            if (FAILED(hr)) {
   2330              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2331            }
   2332            hr = startupTask->Disable();
   2333            if (FAILED(hr)) {
   2334              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2335            }
   2336            RESOLVE_AND_RETURN(promiseHolder, true, S_OK);
   2337          });
   2338  hr = getTaskOperation->put_Completed(getTaskCallback.Get());
   2339  if (FAILED(hr)) {
   2340    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2341  }
   2342 }
   2343 
   2344 static void GetLaunchOnLoginEnabledMSIXAsyncImpl(
   2345    const nsString& capturedTaskId,
   2346    const RefPtr<nsMainThreadPtrHolder<dom::Promise>> promiseHolder) {
   2347  ComPtr<IStartupTaskStatics> startupTaskStatics;
   2348  HRESULT hr = GetActivationFactory(
   2349      HStringReference(RuntimeClass_Windows_ApplicationModel_StartupTask).Get(),
   2350      &startupTaskStatics);
   2351  if (FAILED(hr)) {
   2352    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2353  }
   2354  ComPtr<IAsyncOperation<StartupTask*>> getTaskOperation = nullptr;
   2355  hr = startupTaskStatics->GetAsync(
   2356      HStringReference(capturedTaskId.get()).Get(), &getTaskOperation);
   2357  if (FAILED(hr)) {
   2358    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2359  }
   2360  auto getTaskCallback =
   2361      Callback<IAsyncOperationCompletedHandler<StartupTask*>>(
   2362          [promiseHolder](IAsyncOperation<StartupTask*>* operation,
   2363                          AsyncStatus status) -> HRESULT {
   2364            if (status != AsyncStatus::Completed) {
   2365              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2366            }
   2367            ComPtr<IStartupTask> startupTask;
   2368            HRESULT hr = operation->GetResults(&startupTask);
   2369            if (FAILED(hr)) {
   2370              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2371            }
   2372            StartupTaskState state;
   2373            hr = startupTask->get_State(&state);
   2374            if (FAILED(hr)) {
   2375              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2376            }
   2377            switch (state) {
   2378              case StartupTaskState_EnabledByPolicy:
   2379                RESOLVE_AND_RETURN(
   2380                    promiseHolder,
   2381                    nsIWindowsShellService::LaunchOnLoginEnabledEnumerator::
   2382                        LAUNCH_ON_LOGIN_ENABLED_BY_POLICY,
   2383                    S_OK);
   2384                break;
   2385              case StartupTaskState_Enabled:
   2386                RESOLVE_AND_RETURN(
   2387                    promiseHolder,
   2388                    nsIWindowsShellService::LaunchOnLoginEnabledEnumerator::
   2389                        LAUNCH_ON_LOGIN_ENABLED,
   2390                    S_OK);
   2391                break;
   2392              case StartupTaskState_DisabledByUser:
   2393              case StartupTaskState_DisabledByPolicy:
   2394                RESOLVE_AND_RETURN(
   2395                    promiseHolder,
   2396                    nsIWindowsShellService::LaunchOnLoginEnabledEnumerator::
   2397                        LAUNCH_ON_LOGIN_DISABLED_BY_SETTINGS,
   2398                    S_OK);
   2399                break;
   2400              default:
   2401                RESOLVE_AND_RETURN(
   2402                    promiseHolder,
   2403                    nsIWindowsShellService::LaunchOnLoginEnabledEnumerator::
   2404                        LAUNCH_ON_LOGIN_DISABLED,
   2405                    S_OK);
   2406            }
   2407          });
   2408  hr = getTaskOperation->put_Completed(getTaskCallback.Get());
   2409  if (FAILED(hr)) {
   2410    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2411  }
   2412 }
   2413 
   2414 NS_IMETHODIMP
   2415 nsWindowsShellService::EnableLaunchOnLoginMSIXAsync(
   2416    const nsAString& aTaskId, JSContext* aCx,
   2417    /* out */ dom::Promise** aPromise) {
   2418  if (!widget::WinUtils::HasPackageIdentity()) {
   2419    return NS_ERROR_NOT_AVAILABLE;
   2420  }
   2421 
   2422  if (!NS_IsMainThread()) {
   2423    return NS_ERROR_NOT_SAME_THREAD;
   2424  }
   2425  ErrorResult rv;
   2426  RefPtr<dom::Promise> promise =
   2427      dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
   2428  if (MOZ_UNLIKELY(rv.Failed())) {
   2429    return rv.StealNSResult();
   2430  }
   2431 
   2432  // A holder to pass the promise through the background task and back to
   2433  // the main thread when finished.
   2434  auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
   2435      "EnableLaunchOnLoginMSIXAsync promise", promise);
   2436 
   2437  NS_DispatchBackgroundTask(NS_NewRunnableFunction(
   2438      "EnableLaunchOnLoginMSIXAsync",
   2439      [taskId = nsString(aTaskId), promiseHolder] {
   2440        EnableLaunchOnLoginMSIXAsyncImpl(taskId, promiseHolder);
   2441      }));
   2442 
   2443  promise.forget(aPromise);
   2444  return NS_OK;
   2445 }
   2446 
   2447 NS_IMETHODIMP
   2448 nsWindowsShellService::DisableLaunchOnLoginMSIXAsync(
   2449    const nsAString& aTaskId, JSContext* aCx,
   2450    /* out */ dom::Promise** aPromise) {
   2451  if (!widget::WinUtils::HasPackageIdentity()) {
   2452    return NS_ERROR_NOT_AVAILABLE;
   2453  }
   2454 
   2455  if (!NS_IsMainThread()) {
   2456    return NS_ERROR_NOT_SAME_THREAD;
   2457  }
   2458  ErrorResult rv;
   2459  RefPtr<dom::Promise> promise =
   2460      dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
   2461  if (MOZ_UNLIKELY(rv.Failed())) {
   2462    return rv.StealNSResult();
   2463  }
   2464 
   2465  // A holder to pass the promise through the background task and back to
   2466  // the main thread when finished.
   2467  auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
   2468      "DisableLaunchOnLoginMSIXAsync promise", promise);
   2469 
   2470  NS_DispatchBackgroundTask(NS_NewRunnableFunction(
   2471      "DisableLaunchOnLoginMSIXAsync",
   2472      [taskId = nsString(aTaskId), promiseHolder] {
   2473        DisableLaunchOnLoginMSIXAsyncImpl(taskId, promiseHolder);
   2474      }));
   2475 
   2476  promise.forget(aPromise);
   2477  return NS_OK;
   2478 }
   2479 
   2480 NS_IMETHODIMP
   2481 nsWindowsShellService::GetLaunchOnLoginEnabledMSIXAsync(
   2482    const nsAString& aTaskId, JSContext* aCx,
   2483    /* out */ dom::Promise** aPromise) {
   2484  if (!widget::WinUtils::HasPackageIdentity()) {
   2485    return NS_ERROR_NOT_AVAILABLE;
   2486  }
   2487 
   2488  if (!NS_IsMainThread()) {
   2489    return NS_ERROR_NOT_SAME_THREAD;
   2490  }
   2491  ErrorResult rv;
   2492  RefPtr<dom::Promise> promise =
   2493      dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
   2494  if (MOZ_UNLIKELY(rv.Failed())) {
   2495    return rv.StealNSResult();
   2496  }
   2497 
   2498  // A holder to pass the promise through the background task and back to
   2499  // the main thread when finished.
   2500  auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
   2501      "GetLaunchOnLoginEnabledMSIXAsync promise", promise);
   2502 
   2503  NS_DispatchBackgroundTask(NS_NewRunnableFunction(
   2504      "GetLaunchOnLoginEnabledMSIXAsync",
   2505      [taskId = nsString(aTaskId), promiseHolder] {
   2506        GetLaunchOnLoginEnabledMSIXAsyncImpl(taskId, promiseHolder);
   2507      }));
   2508 
   2509  promise.forget(aPromise);
   2510  return NS_OK;
   2511 }
   2512 
   2513 static HRESULT GetPackage3(ComPtr<IPackage3>& package3) {
   2514  // Get the current package and cast it to IPackage3 so we can
   2515  // check for AppListEntries
   2516  ComPtr<IPackageStatics> packageStatics;
   2517  HRESULT hr = GetActivationFactory(
   2518      HStringReference(RuntimeClass_Windows_ApplicationModel_Package).Get(),
   2519      &packageStatics);
   2520 
   2521  if (FAILED(hr)) {
   2522    return hr;
   2523  }
   2524  ComPtr<IPackage> package;
   2525  hr = packageStatics->get_Current(&package);
   2526  if (FAILED(hr)) {
   2527    return hr;
   2528  }
   2529  hr = package.As(&package3);
   2530  return hr;
   2531 }
   2532 
   2533 static HRESULT GetStartScreenManager(
   2534    ComPtr<IVectorView<AppListEntry*>>& appListEntries,
   2535    ComPtr<IAppListEntry>& entry,
   2536    ComPtr<IStartScreenManager>& startScreenManager) {
   2537  unsigned int numEntries = 0;
   2538  HRESULT hr = appListEntries->get_Size(&numEntries);
   2539  if (FAILED(hr) || numEntries == 0) {
   2540    return E_FAIL;
   2541  }
   2542  // There's only one AppListEntry in the Firefox package and by
   2543  // convention our main executable should be the first in the
   2544  // list.
   2545  hr = appListEntries->GetAt(0, &entry);
   2546 
   2547  // Create and init a StartScreenManager and check if we're already
   2548  // pinned.
   2549  ComPtr<IStartScreenManagerStatics> startScreenManagerStatics;
   2550  hr = GetActivationFactory(
   2551      HStringReference(RuntimeClass_Windows_UI_StartScreen_StartScreenManager)
   2552          .Get(),
   2553      &startScreenManagerStatics);
   2554  if (FAILED(hr)) {
   2555    return hr;
   2556  }
   2557 
   2558  hr = startScreenManagerStatics->GetDefault(&startScreenManager);
   2559  return hr;
   2560 }
   2561 
   2562 static void PinCurrentAppToStartMenuAsyncImpl(
   2563    bool aCheckOnly,
   2564    const RefPtr<nsMainThreadPtrHolder<dom::Promise>> promiseHolder) {
   2565  ComPtr<IPackage3> package3;
   2566  HRESULT hr = GetPackage3(package3);
   2567  if (FAILED(hr)) {
   2568    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2569  }
   2570 
   2571  // Get the AppList entries
   2572  ComPtr<IVectorView<AppListEntry*>> appListEntries;
   2573  ComPtr<IAsyncOperation<IVectorView<AppListEntry*>*>>
   2574      getAppListEntriesOperation;
   2575  hr = package3->GetAppListEntriesAsync(&getAppListEntriesOperation);
   2576  if (FAILED(hr)) {
   2577    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2578  }
   2579  auto getAppListEntriesCallback =
   2580      Callback<IAsyncOperationCompletedHandler<IVectorView<AppListEntry*>*>>(
   2581          [promiseHolder, aCheckOnly](
   2582              IAsyncOperation<IVectorView<AppListEntry*>*>* operation,
   2583              AsyncStatus status) -> HRESULT {
   2584            if (status != AsyncStatus::Completed) {
   2585              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2586            }
   2587            ComPtr<IVectorView<AppListEntry*>> appListEntries;
   2588            HRESULT hr = operation->GetResults(&appListEntries);
   2589            if (FAILED(hr)) {
   2590              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2591            }
   2592            ComPtr<IStartScreenManager> startScreenManager;
   2593            ComPtr<IAppListEntry> entry;
   2594            hr = GetStartScreenManager(appListEntries, entry,
   2595                                       startScreenManager);
   2596            if (FAILED(hr)) {
   2597              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2598            }
   2599            ComPtr<IAsyncOperation<bool>> getPinnedOperation;
   2600            hr = startScreenManager->ContainsAppListEntryAsync(
   2601                entry.Get(), &getPinnedOperation);
   2602            if (FAILED(hr)) {
   2603              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2604            }
   2605            auto getPinnedCallback =
   2606                Callback<IAsyncOperationCompletedHandler<bool>>(
   2607                    [promiseHolder, entry, startScreenManager, aCheckOnly](
   2608                        IAsyncOperation<bool>* operation,
   2609                        AsyncStatus status) -> HRESULT {
   2610                      if (status != AsyncStatus::Completed) {
   2611                        REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE,
   2612                                          E_FAIL);
   2613                      }
   2614                      boolean isAlreadyPinned;
   2615                      HRESULT hr = operation->GetResults(&isAlreadyPinned);
   2616                      if (FAILED(hr)) {
   2617                        REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE,
   2618                                          E_FAIL);
   2619                      }
   2620                      // If we're already pinned we can return early
   2621                      // Ditto if we're just checking whether we *can* pin
   2622                      if (isAlreadyPinned || aCheckOnly) {
   2623                        RESOLVE_AND_RETURN(promiseHolder, true, S_OK);
   2624                      }
   2625                      ComPtr<IAsyncOperation<bool>> pinOperation;
   2626                      startScreenManager->RequestAddAppListEntryAsync(
   2627                          entry.Get(), &pinOperation);
   2628                      // Set another callback for pinning to the start menu
   2629                      auto pinOperationCallback =
   2630                          Callback<IAsyncOperationCompletedHandler<bool>>(
   2631                              [promiseHolder](IAsyncOperation<bool>* operation,
   2632                                              AsyncStatus status) -> HRESULT {
   2633                                if (status != AsyncStatus::Completed) {
   2634                                  REJECT_AND_RETURN(promiseHolder,
   2635                                                    NS_ERROR_FAILURE, E_FAIL);
   2636                                };
   2637                                boolean pinSuccess;
   2638                                HRESULT hr = operation->GetResults(&pinSuccess);
   2639                                if (FAILED(hr)) {
   2640                                  REJECT_AND_RETURN(promiseHolder,
   2641                                                    NS_ERROR_FAILURE, E_FAIL);
   2642                                }
   2643                                RESOLVE_AND_RETURN(promiseHolder,
   2644                                                   pinSuccess ? true : false,
   2645                                                   S_OK);
   2646                              });
   2647                      hr = pinOperation->put_Completed(
   2648                          pinOperationCallback.Get());
   2649                      if (FAILED(hr)) {
   2650                        REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, hr);
   2651                      }
   2652                      return hr;
   2653                    });
   2654            hr = getPinnedOperation->put_Completed(getPinnedCallback.Get());
   2655            if (FAILED(hr)) {
   2656              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, hr);
   2657            }
   2658            return hr;
   2659          });
   2660  hr = getAppListEntriesOperation->put_Completed(
   2661      getAppListEntriesCallback.Get());
   2662  if (FAILED(hr)) {
   2663    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2664  }
   2665 }
   2666 
   2667 NS_IMETHODIMP
   2668 nsWindowsShellService::PinCurrentAppToStartMenuAsync(bool aCheckOnly,
   2669                                                     JSContext* aCx,
   2670                                                     dom::Promise** aPromise) {
   2671  if (!NS_IsMainThread()) {
   2672    return NS_ERROR_NOT_SAME_THREAD;
   2673  }
   2674  // Unfortunately pinning to the Start Menu requires IAppListEntry
   2675  // which is only implemented for packaged applications.
   2676  if (!widget::WinUtils::HasPackageIdentity()) {
   2677    return NS_ERROR_NOT_AVAILABLE;
   2678  }
   2679  ErrorResult rv;
   2680  RefPtr<dom::Promise> promise =
   2681      dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
   2682  if (MOZ_UNLIKELY(rv.Failed())) {
   2683    return rv.StealNSResult();
   2684  }
   2685 
   2686  // A holder to pass the promise through the background task and back to
   2687  // the main thread when finished.
   2688  auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
   2689      "PinCurrentAppToStartMenuAsync promise", promise);
   2690  NS_DispatchBackgroundTask(NS_NewRunnableFunction(
   2691      "PinCurrentAppToStartMenuAsync", [aCheckOnly, promiseHolder] {
   2692        PinCurrentAppToStartMenuAsyncImpl(aCheckOnly, promiseHolder);
   2693      }));
   2694  promise.forget(aPromise);
   2695  return NS_OK;
   2696 }
   2697 
   2698 static void IsCurrentAppPinnedToStartMenuAsyncImpl(
   2699    const RefPtr<nsMainThreadPtrHolder<dom::Promise>> promiseHolder) {
   2700  ComPtr<IPackage3> package3;
   2701  HRESULT hr = GetPackage3(package3);
   2702  if (FAILED(hr)) {
   2703    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2704  }
   2705 
   2706  // Get the AppList entries
   2707  ComPtr<IVectorView<AppListEntry*>> appListEntries;
   2708  ComPtr<IAsyncOperation<IVectorView<AppListEntry*>*>>
   2709      getAppListEntriesOperation;
   2710  hr = package3->GetAppListEntriesAsync(&getAppListEntriesOperation);
   2711  if (FAILED(hr)) {
   2712    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2713  }
   2714  auto getAppListEntriesCallback =
   2715      Callback<IAsyncOperationCompletedHandler<IVectorView<AppListEntry*>*>>(
   2716          [promiseHolder](
   2717              IAsyncOperation<IVectorView<AppListEntry*>*>* operation,
   2718              AsyncStatus status) -> HRESULT {
   2719            if (status != AsyncStatus::Completed) {
   2720              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2721            }
   2722            ComPtr<IVectorView<AppListEntry*>> appListEntries;
   2723            HRESULT hr = operation->GetResults(&appListEntries);
   2724            if (FAILED(hr)) {
   2725              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2726            }
   2727            ComPtr<IStartScreenManager> startScreenManager;
   2728            ComPtr<IAppListEntry> entry;
   2729            hr = GetStartScreenManager(appListEntries, entry,
   2730                                       startScreenManager);
   2731            if (FAILED(hr)) {
   2732              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2733            }
   2734            ComPtr<IAsyncOperation<bool>> getPinnedOperation;
   2735            hr = startScreenManager->ContainsAppListEntryAsync(
   2736                entry.Get(), &getPinnedOperation);
   2737            if (FAILED(hr)) {
   2738              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
   2739            }
   2740            auto getPinnedCallback =
   2741                Callback<IAsyncOperationCompletedHandler<bool>>(
   2742                    [promiseHolder, entry, startScreenManager](
   2743                        IAsyncOperation<bool>* operation,
   2744                        AsyncStatus status) -> HRESULT {
   2745                      if (status != AsyncStatus::Completed) {
   2746                        REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE,
   2747                                          E_FAIL);
   2748                      }
   2749                      boolean isAlreadyPinned;
   2750                      HRESULT hr = operation->GetResults(&isAlreadyPinned);
   2751                      if (FAILED(hr)) {
   2752                        REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE,
   2753                                          E_FAIL);
   2754                      }
   2755                      RESOLVE_AND_RETURN(promiseHolder,
   2756                                         isAlreadyPinned ? true : false, S_OK);
   2757                    });
   2758            hr = getPinnedOperation->put_Completed(getPinnedCallback.Get());
   2759            if (FAILED(hr)) {
   2760              REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, hr);
   2761            }
   2762            return hr;
   2763          });
   2764  hr = getAppListEntriesOperation->put_Completed(
   2765      getAppListEntriesCallback.Get());
   2766  if (FAILED(hr)) {
   2767    REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
   2768  }
   2769 }
   2770 
   2771 NS_IMETHODIMP
   2772 nsWindowsShellService::IsCurrentAppPinnedToStartMenuAsync(
   2773    JSContext* aCx, dom::Promise** aPromise) {
   2774  if (!NS_IsMainThread()) {
   2775    return NS_ERROR_NOT_SAME_THREAD;
   2776  }
   2777  // Unfortunately pinning to the Start Menu requires IAppListEntry
   2778  // which is only implemented for packaged applications.
   2779  if (!widget::WinUtils::HasPackageIdentity()) {
   2780    return NS_ERROR_NOT_AVAILABLE;
   2781  }
   2782  ErrorResult rv;
   2783  RefPtr<dom::Promise> promise =
   2784      dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
   2785  if (MOZ_UNLIKELY(rv.Failed())) {
   2786    return rv.StealNSResult();
   2787  }
   2788 
   2789  // A holder to pass the promise through the background task and back to
   2790  // the main thread when finished.
   2791  auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
   2792      "IsCurrentAppPinnedToStartMenuAsync promise", promise);
   2793  NS_DispatchBackgroundTask(NS_NewRunnableFunction(
   2794      "IsCurrentAppPinnedToStartMenuAsync", [promiseHolder] {
   2795        IsCurrentAppPinnedToStartMenuAsyncImpl(promiseHolder);
   2796      }));
   2797  promise.forget(aPromise);
   2798  return NS_OK;
   2799 }
   2800 
   2801 #else
   2802 NS_IMETHODIMP
   2803 nsWindowsShellService::EnableLaunchOnLoginMSIXAsync(
   2804    const nsAString& aTaskId, JSContext* aCx,
   2805    /* out */ dom::Promise** aPromise) {
   2806  return NS_ERROR_NOT_IMPLEMENTED;
   2807 }
   2808 
   2809 NS_IMETHODIMP
   2810 nsWindowsShellService::DisableLaunchOnLoginMSIXAsync(
   2811    const nsAString& aTaskId, JSContext* aCx,
   2812    /* out */ dom::Promise** aPromise) {
   2813  return NS_ERROR_NOT_IMPLEMENTED;
   2814 }
   2815 
   2816 NS_IMETHODIMP
   2817 nsWindowsShellService::GetLaunchOnLoginEnabledMSIXAsync(
   2818    const nsAString& aTaskId, JSContext* aCx,
   2819    /* out */ dom::Promise** aPromise) {
   2820  return NS_ERROR_NOT_IMPLEMENTED;
   2821 }
   2822 
   2823 NS_IMETHODIMP
   2824 nsWindowsShellService::PinCurrentAppToStartMenuAsync(bool aCheckOnly,
   2825                                                     JSContext* aCx,
   2826                                                     dom::Promise** aPromise) {
   2827  return NS_ERROR_NOT_IMPLEMENTED;
   2828 }
   2829 
   2830 NS_IMETHODIMP
   2831 nsWindowsShellService::IsCurrentAppPinnedToStartMenuAsync(
   2832    JSContext* aCx, dom::Promise** aPromise) {
   2833  return NS_ERROR_NOT_IMPLEMENTED;
   2834 }
   2835 #endif
   2836 
   2837 NS_IMETHODIMP
   2838 nsWindowsShellService::ClassifyShortcut(const nsAString& aPath,
   2839                                        nsAString& aResult) {
   2840  aResult.Truncate();
   2841 
   2842  nsAutoString shortcutPath(PromiseFlatString(aPath));
   2843 
   2844  // NOTE: On Windows 7, Start Menu pin shortcuts are stored under
   2845  // "<FOLDERID_User Pinned>\StartMenu", but on Windows 10 they are just normal
   2846  // Start Menu shortcuts. These both map to "StartMenu" for consistency,
   2847  // rather than having a separate "StartMenuPins" which would only apply on
   2848  // Win7.
   2849  struct {
   2850    KNOWNFOLDERID folderId;
   2851    const char16_t* postfix;
   2852    const char16_t* classification;
   2853  } folders[] = {{FOLDERID_CommonStartMenu, u"\\", u"StartMenu"},
   2854                 {FOLDERID_StartMenu, u"\\", u"StartMenu"},
   2855                 {FOLDERID_PublicDesktop, u"\\", u"Desktop"},
   2856                 {FOLDERID_Desktop, u"\\", u"Desktop"},
   2857                 {FOLDERID_UserPinned, u"\\TaskBar\\", u"Taskbar"},
   2858                 {FOLDERID_UserPinned, u"\\StartMenu\\", u"StartMenu"}};
   2859 
   2860  for (size_t i = 0; i < std::size(folders); ++i) {
   2861    nsAutoString knownPath;
   2862 
   2863    // These flags are chosen to avoid I/O, see bug 1363398.
   2864    DWORD flags =
   2865        KF_FLAG_SIMPLE_IDLIST | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_ALIAS;
   2866    PWSTR rawPath = nullptr;
   2867 
   2868    if (FAILED(SHGetKnownFolderPath(folders[i].folderId, flags, nullptr,
   2869                                    &rawPath))) {
   2870      continue;
   2871    }
   2872 
   2873    knownPath = nsDependentString(rawPath);
   2874    CoTaskMemFree(rawPath);
   2875 
   2876    knownPath.Append(folders[i].postfix);
   2877    // Check if the shortcut path starts with the shell folder path.
   2878    if (wcsnicmp(shortcutPath.get(), knownPath.get(), knownPath.Length()) ==
   2879        0) {
   2880      aResult.Assign(folders[i].classification);
   2881      nsTArray<nsCString> resIds = {
   2882          "branding/brand.ftl"_ns,
   2883          "browser/browser.ftl"_ns,
   2884      };
   2885      RefPtr<Localization> l10n = Localization::Create(resIds, true);
   2886      nsAutoCString pbStr;
   2887      IgnoredErrorResult rv;
   2888      l10n->FormatValueSync("private-browsing-shortcut-text-2"_ns, {}, pbStr,
   2889                            rv);
   2890      NS_ConvertUTF8toUTF16 widePbStr(pbStr);
   2891      if (wcsstr(shortcutPath.get(), widePbStr.get())) {
   2892        aResult.AppendLiteral("Private");
   2893      }
   2894      return NS_OK;
   2895    }
   2896  }
   2897 
   2898  // Nothing found, aResult is already "".
   2899  return NS_OK;
   2900 }
   2901 
   2902 nsWindowsShellService::nsWindowsShellService() {}
   2903 
   2904 nsWindowsShellService::~nsWindowsShellService() {}