summaryrefslogtreecommitdiffstats
path: root/browser/components/shell/nsWindowsShellService.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--browser/components/shell/nsWindowsShellService.cpp626
1 files changed, 626 insertions, 0 deletions
diff --git a/browser/components/shell/nsWindowsShellService.cpp b/browser/components/shell/nsWindowsShellService.cpp
new file mode 100644
index 0000000000..87f10cb80f
--- /dev/null
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -0,0 +1,626 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsWindowsShellService.h"
+
+#include "BinaryPath.h"
+#include "imgIContainer.h"
+#include "imgIRequest.h"
+#include "mozilla/RefPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIContent.h"
+#include "nsIImageLoadingContent.h"
+#include "nsIOutputStream.h"
+#include "nsIPrefService.h"
+#include "nsIStringBundle.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "nsShellService.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIWindowsRegKey.h"
+#include "nsUnicharUtils.h"
+#include "nsIURLFormatter.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/gfx/2D.h"
+#include "WindowsDefaultBrowser.h"
+
+#include "windows.h"
+#include "shellapi.h"
+#include <propvarutil.h>
+#include <propkey.h>
+
+#include <shlobj.h>
+#include "WinUtils.h"
+
+#include <mbstring.h>
+
+#undef ACCESS_READ
+
+#ifndef MAX_BUF
+# define MAX_BUF 4096
+#endif
+
+#define REG_SUCCEEDED(val) (val == ERROR_SUCCESS)
+
+#define REG_FAILED(val) (val != ERROR_SUCCESS)
+
+#ifdef DEBUG
+# define NS_ENSURE_HRESULT(hres, ret) \
+ do { \
+ HRESULT result = hres; \
+ if (MOZ_UNLIKELY(FAILED(result))) { \
+ mozilla::SmprintfPointer msg = mozilla::Smprintf( \
+ "NS_ENSURE_HRESULT(%s, %s) failed with " \
+ "result 0x%" PRIX32, \
+ #hres, #ret, static_cast<uint32_t>(result)); \
+ NS_WARNING(msg.get()); \
+ return ret; \
+ } \
+ } while (false)
+#else
+# define NS_ENSURE_HRESULT(hres, ret) \
+ if (MOZ_UNLIKELY(FAILED(hres))) return ret
+#endif
+
+using mozilla::IsWin8OrLater;
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIToolkitShellService,
+ nsIShellService, nsIWindowsShellService)
+
+static nsresult OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName,
+ HKEY* aKey) {
+ const nsString& flatName = PromiseFlatString(aKeyName);
+
+ DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey);
+ switch (res) {
+ case ERROR_SUCCESS:
+ break;
+ case ERROR_ACCESS_DENIED:
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ case ERROR_FILE_NOT_FOUND:
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return NS_OK;
+}
+
+nsresult GetHelperPath(nsAutoString& aPath) {
+ nsresult rv;
+ nsCOMPtr<nsIProperties> directoryService =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> appHelper;
+ rv = directoryService->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
+ getter_AddRefs(appHelper));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = appHelper->SetNativeLeafName("uninstall"_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = appHelper->AppendNative("helper.exe"_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = appHelper->GetPath(aPath);
+
+ aPath.Insert(L'"', 0);
+ aPath.Append(L'"');
+ return rv;
+}
+
+nsresult LaunchHelper(nsAutoString& aPath) {
+ STARTUPINFOW si = {sizeof(si), 0};
+ PROCESS_INFORMATION pi = {0};
+
+ if (!CreateProcessW(nullptr, (LPWSTR)aPath.get(), nullptr, nullptr, FALSE, 0,
+ nullptr, nullptr, &si, &pi)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return NS_OK;
+}
+
+static bool IsPathDefaultForClass(
+ const RefPtr<IApplicationAssociationRegistration>& pAAR, wchar_t* exePath,
+ LPCWSTR aClassName) {
+ // Make sure the Prog ID matches what we have
+ LPWSTR registeredApp;
+ bool isProtocol = *aClassName != L'.';
+ ASSOCIATIONTYPE queryType = isProtocol ? AT_URLPROTOCOL : AT_FILEEXTENSION;
+ HRESULT hr = pAAR->QueryCurrentDefault(aClassName, queryType, AL_EFFECTIVE,
+ &registeredApp);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ LPCWSTR progID = isProtocol ? L"FirefoxURL" : L"FirefoxHTML";
+ bool isDefault = !wcsnicmp(registeredApp, progID, wcslen(progID));
+
+ nsAutoString regAppName(registeredApp);
+ CoTaskMemFree(registeredApp);
+
+ if (isDefault) {
+ // Make sure the application path for this progID is this installation.
+ regAppName.AppendLiteral("\\shell\\open\\command");
+ HKEY theKey;
+ nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, regAppName, &theKey);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ wchar_t cmdFromReg[MAX_BUF] = L"";
+ DWORD len = sizeof(cmdFromReg);
+ DWORD res = ::RegQueryValueExW(theKey, nullptr, nullptr, nullptr,
+ (LPBYTE)cmdFromReg, &len);
+ ::RegCloseKey(theKey);
+ if (REG_FAILED(res)) {
+ return false;
+ }
+
+ wchar_t fullCmd[MAX_BUF] = L"";
+ _snwprintf(fullCmd, MAX_BUF, L"\"%s\" -osint -url \"%%1\"", exePath);
+
+ isDefault = _wcsicmp(fullCmd, cmdFromReg) == 0;
+ }
+
+ return isDefault;
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::IsDefaultBrowser(bool aForAllTypes,
+ bool* aIsDefaultBrowser) {
+ *aIsDefaultBrowser = false;
+
+ RefPtr<IApplicationAssociationRegistration> pAAR;
+ HRESULT hr = CoCreateInstance(
+ CLSID_ApplicationAssociationRegistration, nullptr, CLSCTX_INPROC,
+ IID_IApplicationAssociationRegistration, getter_AddRefs(pAAR));
+ if (FAILED(hr)) {
+ return NS_OK;
+ }
+
+ wchar_t exePath[MAXPATHLEN] = L"";
+ nsresult rv = BinaryPath::GetLong(exePath);
+
+ if (NS_FAILED(rv)) {
+ return NS_OK;
+ }
+
+ *aIsDefaultBrowser = IsPathDefaultForClass(pAAR, exePath, L"http");
+ if (*aIsDefaultBrowser && aForAllTypes) {
+ *aIsDefaultBrowser = IsPathDefaultForClass(pAAR, exePath, L".html");
+ }
+ return NS_OK;
+}
+
+nsresult nsWindowsShellService::LaunchControlPanelDefaultsSelectionUI() {
+ IApplicationAssociationRegistrationUI* pAARUI;
+ HRESULT hr = CoCreateInstance(
+ CLSID_ApplicationAssociationRegistrationUI, NULL, CLSCTX_INPROC,
+ IID_IApplicationAssociationRegistrationUI, (void**)&pAARUI);
+ if (SUCCEEDED(hr)) {
+ mozilla::UniquePtr<wchar_t[]> appRegName;
+ GetAppRegName(appRegName);
+ hr = pAARUI->LaunchAdvancedAssociationUI(appRegName.get());
+ pAARUI->Release();
+ }
+ return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsWindowsShellService::LaunchControlPanelDefaultPrograms() {
+ return ::LaunchControlPanelDefaultPrograms() ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsWindowsShellService::LaunchModernSettingsDialogDefaultApps() {
+ return ::LaunchModernSettingsDialogDefaultApps() ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsWindowsShellService::InvokeHTTPOpenAsVerb() {
+ nsCOMPtr<nsIURLFormatter> formatter(
+ do_GetService("@mozilla.org/toolkit/URLFormatterService;1"));
+ if (!formatter) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsString urlStr;
+ nsresult rv = formatter->FormatURLPref(u"app.support.baseURL"_ns, urlStr);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!StringBeginsWith(urlStr, u"https://"_ns)) {
+ return NS_ERROR_FAILURE;
+ }
+ urlStr.AppendLiteral("win10-default-browser");
+
+ SHELLEXECUTEINFOW seinfo = {sizeof(SHELLEXECUTEINFOW)};
+ seinfo.lpVerb = L"openas";
+ seinfo.lpFile = urlStr.get();
+ seinfo.nShow = SW_SHOWNORMAL;
+ if (!ShellExecuteExW(&seinfo)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+nsresult nsWindowsShellService::LaunchHTTPHandlerPane() {
+ OPENASINFO info;
+ info.pcszFile = L"http";
+ info.pcszClass = nullptr;
+ info.oaifInFlags =
+ OAIF_FORCE_REGISTRATION | OAIF_URL_PROTOCOL | OAIF_REGISTER_EXT;
+
+ HRESULT hr = SHOpenWithDialog(nullptr, &info);
+ if (SUCCEEDED(hr) || (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))) {
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::SetDefaultBrowser(bool aClaimAllTypes,
+ bool aForAllUsers) {
+ nsAutoString appHelperPath;
+ if (NS_FAILED(GetHelperPath(appHelperPath))) return NS_ERROR_FAILURE;
+
+ if (aForAllUsers) {
+ appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal");
+ } else {
+ appHelperPath.AppendLiteral(" /SetAsDefaultAppUser");
+ }
+
+ nsresult rv = LaunchHelper(appHelperPath);
+ if (NS_SUCCEEDED(rv) && IsWin8OrLater()) {
+ if (aClaimAllTypes) {
+ if (IsWin10OrLater()) {
+ rv = LaunchModernSettingsDialogDefaultApps();
+ } else {
+ rv = LaunchControlPanelDefaultsSelectionUI();
+ }
+ // The above call should never really fail, but just in case
+ // fall back to showing the HTTP association screen only.
+ if (NS_FAILED(rv)) {
+ if (IsWin10OrLater()) {
+ rv = InvokeHTTPOpenAsVerb();
+ } else {
+ rv = LaunchHTTPHandlerPane();
+ }
+ }
+ } else {
+ // Windows 10 blocks attempts to load the
+ // HTTP Handler association dialog.
+ if (IsWin10OrLater()) {
+ rv = LaunchModernSettingsDialogDefaultApps();
+ } else {
+ rv = LaunchHTTPHandlerPane();
+ }
+
+ // The above call should never really fail, but just in case
+ // fall back to showing control panel for all defaults
+ if (NS_FAILED(rv)) {
+ rv = LaunchControlPanelDefaultsSelectionUI();
+ }
+ }
+ }
+
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (prefs) {
+ (void)prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
+ // Reset the number of times the dialog should be shown
+ // before it is silenced.
+ (void)prefs->SetIntPref(PREF_DEFAULTBROWSERCHECKCOUNT, 0);
+ }
+
+ return rv;
+}
+
+static nsresult WriteBitmap(nsIFile* aFile, imgIContainer* aImage) {
+ nsresult rv;
+
+ RefPtr<gfx::SourceSurface> surface = aImage->GetFrame(
+ imgIContainer::FRAME_FIRST, imgIContainer::FLAG_SYNC_DECODE);
+ NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
+
+ // For either of the following formats we want to set the biBitCount member
+ // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
+ // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
+ // for the BI_RGB value we use for the biCompression member.
+ MOZ_ASSERT(surface->GetFormat() == gfx::SurfaceFormat::B8G8R8A8 ||
+ surface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
+
+ RefPtr<gfx::DataSourceSurface> dataSurface = surface->GetDataSurface();
+ NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
+
+ int32_t width = dataSurface->GetSize().width;
+ int32_t height = dataSurface->GetSize().height;
+ int32_t bytesPerPixel = 4 * sizeof(uint8_t);
+ uint32_t bytesPerRow = bytesPerPixel * width;
+
+ // initialize these bitmap structs which we will later
+ // serialize directly to the head of the bitmap file
+ BITMAPINFOHEADER bmi;
+ bmi.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.biWidth = width;
+ bmi.biHeight = height;
+ bmi.biPlanes = 1;
+ bmi.biBitCount = (WORD)bytesPerPixel * 8;
+ bmi.biCompression = BI_RGB;
+ bmi.biSizeImage = bytesPerRow * height;
+ bmi.biXPelsPerMeter = 0;
+ bmi.biYPelsPerMeter = 0;
+ bmi.biClrUsed = 0;
+ bmi.biClrImportant = 0;
+
+ BITMAPFILEHEADER bf;
+ bf.bfType = 0x4D42; // 'BM'
+ bf.bfReserved1 = 0;
+ bf.bfReserved2 = 0;
+ bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
+ bf.bfSize = bf.bfOffBits + bmi.biSizeImage;
+
+ // get a file output stream
+ nsCOMPtr<nsIOutputStream> stream;
+ rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ gfx::DataSourceSurface::MappedSurface map;
+ if (!dataSurface->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // write the bitmap headers and rgb pixel data to the file
+ rv = NS_ERROR_FAILURE;
+ if (stream) {
+ uint32_t written;
+ stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
+ if (written == sizeof(BITMAPFILEHEADER)) {
+ stream->Write((const char*)&bmi, sizeof(BITMAPINFOHEADER), &written);
+ if (written == sizeof(BITMAPINFOHEADER)) {
+ // write out the image data backwards because the desktop won't
+ // show bitmaps with negative heights for top-to-bottom
+ uint32_t i = map.mStride * height;
+ do {
+ i -= map.mStride;
+ stream->Write(((const char*)map.mData) + i, bytesPerRow, &written);
+ if (written == bytesPerRow) {
+ rv = NS_OK;
+ } else {
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ } while (i != 0);
+ }
+ }
+
+ stream->Close();
+ }
+
+ dataSurface->Unmap();
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::SetDesktopBackground(dom::Element* aElement,
+ int32_t aPosition,
+ const nsACString& aImageName) {
+ if (!aElement || !aElement->IsHTMLElement(nsGkAtoms::img)) {
+ // XXX write background loading stuff!
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIImageLoadingContent> imageContent =
+ do_QueryInterface(aElement, &rv);
+ if (!imageContent) return rv;
+
+ // get the image container
+ nsCOMPtr<imgIRequest> request;
+ rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
+ getter_AddRefs(request));
+ if (!request) return rv;
+
+ nsCOMPtr<imgIContainer> container;
+ rv = request->GetImage(getter_AddRefs(container));
+ if (!container) return NS_ERROR_FAILURE;
+
+ // get the file name from localized strings
+ nsCOMPtr<nsIStringBundleService> bundleService(
+ do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIStringBundle> shellBundle;
+ rv = bundleService->CreateBundle(SHELLSERVICE_PROPERTIES,
+ getter_AddRefs(shellBundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // e.g. "Desktop Background.bmp"
+ nsAutoString fileLeafName;
+ rv = shellBundle->GetStringFromName("desktopBackgroundLeafNameWin",
+ fileLeafName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // get the profile root directory
+ nsCOMPtr<nsIFile> file;
+ rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR,
+ getter_AddRefs(file));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // eventually, the path is "%APPDATA%\Mozilla\Firefox\Desktop Background.bmp"
+ rv = file->Append(fileLeafName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString path;
+ rv = file->GetPath(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // write the bitmap to a file in the profile directory.
+ // We have to write old bitmap format for Windows 7 wallpapar support.
+ rv = WriteBitmap(file, container);
+
+ // if the file was written successfully, set it as the system wallpaper
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIWindowsRegKey> regKey =
+ do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ u"Control Panel\\Desktop"_ns,
+ nsIWindowsRegKey::ACCESS_SET_VALUE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString tile;
+ nsAutoString style;
+ switch (aPosition) {
+ case BACKGROUND_TILE:
+ style.Assign('0');
+ tile.Assign('1');
+ break;
+ case BACKGROUND_CENTER:
+ style.Assign('0');
+ tile.Assign('0');
+ break;
+ case BACKGROUND_STRETCH:
+ style.Assign('2');
+ tile.Assign('0');
+ break;
+ case BACKGROUND_FILL:
+ style.AssignLiteral("10");
+ tile.Assign('0');
+ break;
+ case BACKGROUND_FIT:
+ style.Assign('6');
+ tile.Assign('0');
+ break;
+ case BACKGROUND_SPAN:
+ style.AssignLiteral("22");
+ tile.Assign('0');
+ break;
+ }
+
+ rv = regKey->WriteStringValue(u"TileWallpaper"_ns, tile);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = regKey->WriteStringValue(u"WallpaperStyle"_ns, style);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = regKey->Close();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ ::SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (PVOID)path.get(),
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::GetDesktopBackgroundColor(uint32_t* aColor) {
+ uint32_t color = ::GetSysColor(COLOR_DESKTOP);
+ *aColor =
+ (GetRValue(color) << 16) | (GetGValue(color) << 8) | GetBValue(color);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::SetDesktopBackgroundColor(uint32_t aColor) {
+ int aParameters[2] = {COLOR_BACKGROUND, COLOR_DESKTOP};
+ BYTE r = (aColor >> 16);
+ BYTE g = (aColor << 16) >> 24;
+ BYTE b = (aColor << 24) >> 24;
+ COLORREF colors[2] = {RGB(r, g, b), RGB(r, g, b)};
+
+ ::SetSysColors(sizeof(aParameters) / sizeof(int), aParameters, colors);
+
+ nsresult rv;
+ nsCOMPtr<nsIWindowsRegKey> regKey =
+ do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ u"Control Panel\\Colors"_ns,
+ nsIWindowsRegKey::ACCESS_SET_VALUE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ wchar_t rgb[12];
+ _snwprintf(rgb, 12, L"%u %u %u", r, g, b);
+
+ rv = regKey->WriteStringValue(u"Background"_ns, nsDependentString(rgb));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return regKey->Close();
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::CreateShortcut(nsIFile* aBinary,
+ const nsTArray<nsString>& aArguments,
+ const nsAString& aDescription,
+ nsIFile* aIconFile,
+ const nsAString& aAppUserModelId,
+ nsIFile* aTarget) {
+ NS_ENSURE_ARG(aBinary);
+ NS_ENSURE_ARG(aTarget);
+
+ RefPtr<IShellLinkW> link;
+ HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IShellLinkW, getter_AddRefs(link));
+ NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
+
+ nsString path(aBinary->NativePath());
+ link->SetPath(path.get());
+
+ if (!aDescription.IsEmpty()) {
+ link->SetDescription(PromiseFlatString(aDescription).get());
+ }
+
+ // TODO: Properly escape quotes in the string, see bug 1604287.
+ nsString arguments;
+ for (auto& arg : aArguments) {
+ arguments.AppendPrintf("\"%S\" ", arg.get());
+ }
+
+ link->SetArguments(arguments.get());
+
+ if (aIconFile) {
+ nsString icon(aIconFile->NativePath());
+ link->SetIconLocation(icon.get(), 0);
+ }
+
+ if (!aAppUserModelId.IsEmpty()) {
+ RefPtr<IPropertyStore> propStore;
+ hr = link->QueryInterface(IID_IPropertyStore, getter_AddRefs(propStore));
+ NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
+
+ PROPVARIANT pv;
+ if (FAILED(InitPropVariantFromString(
+ PromiseFlatString(aAppUserModelId).get(), &pv))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ hr = propStore->SetValue(PKEY_AppUserModel_ID, pv);
+ PropVariantClear(&pv);
+ NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
+
+ hr = propStore->Commit();
+ NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
+ }
+
+ RefPtr<IPersistFile> persist;
+ hr = link->QueryInterface(IID_IPersistFile, getter_AddRefs(persist));
+ NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
+
+ nsString target(aTarget->NativePath());
+ hr = persist->Save(target.get(), TRUE);
+ NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
+
+ return NS_OK;
+}
+
+nsWindowsShellService::nsWindowsShellService() {}
+
+nsWindowsShellService::~nsWindowsShellService() {}