summaryrefslogtreecommitdiffstats
path: root/browser/components/shell/nsWindowsShellService.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/shell/nsWindowsShellService.cpp')
-rw-r--r--browser/components/shell/nsWindowsShellService.cpp1280
1 files changed, 1280 insertions, 0 deletions
diff --git a/browser/components/shell/nsWindowsShellService.cpp b/browser/components/shell/nsWindowsShellService.cpp
new file mode 100644
index 000000000..416e00cbc
--- /dev/null
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -0,0 +1,1280 @@
+/* -*- 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 "imgIContainer.h"
+#include "imgIRequest.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMHTMLImageElement.h"
+#include "nsIImageLoadingContent.h"
+#include "nsIOutputStream.h"
+#include "nsIPrefService.h"
+#include "nsIPrefLocalizedString.h"
+#include "nsIServiceManager.h"
+#include "nsIStringBundle.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "nsShellService.h"
+#include "nsIProcess.h"
+#include "nsICategoryManager.h"
+#include "nsBrowserCompsCID.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIWindowsRegKey.h"
+#include "nsUnicharUtils.h"
+#include "nsIWinTaskbar.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIURLFormatter.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/WindowsVersion.h"
+
+#include "windows.h"
+#include "shellapi.h"
+
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0600
+#define INITGUID
+#undef NTDDI_VERSION
+#define NTDDI_VERSION NTDDI_WIN8
+// Needed for access to IApplicationActivationManager
+#include <shlobj.h>
+
+#include <mbstring.h>
+#include <shlwapi.h>
+
+#include <lm.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)
+
+#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
+
+using mozilla::IsWin8OrLater;
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIWindowsShellService, nsIShellService)
+
+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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Default Browser Registry Settings
+//
+// The setting of these values are made by an external binary since writing
+// these values may require elevation.
+//
+// - File Extension Mappings
+// -----------------------
+// The following file extensions:
+// .htm .html .shtml .xht .xhtml
+// are mapped like so:
+//
+// HKCU\SOFTWARE\Classes\.<ext>\ (default) REG_SZ FirefoxHTML
+//
+// as aliases to the class:
+//
+// HKCU\SOFTWARE\Classes\FirefoxHTML\
+// DefaultIcon (default) REG_SZ <apppath>,1
+// shell\open\command (default) REG_SZ <apppath> -osint -url "%1"
+// shell\open\ddeexec (default) REG_SZ <empty string>
+//
+// - Windows Vista and above Protocol Handler
+//
+// HKCU\SOFTWARE\Classes\FirefoxURL\ (default) REG_SZ <appname> URL
+// EditFlags REG_DWORD 2
+// FriendlyTypeName REG_SZ <appname> URL
+// DefaultIcon (default) REG_SZ <apppath>,1
+// shell\open\command (default) REG_SZ <apppath> -osint -url "%1"
+// shell\open\ddeexec (default) REG_SZ <empty string>
+//
+// - Protocol Mappings
+// -----------------
+// The following protocols:
+// HTTP, HTTPS, FTP
+// are mapped like so:
+//
+// HKCU\SOFTWARE\Classes\<protocol>\
+// DefaultIcon (default) REG_SZ <apppath>,1
+// shell\open\command (default) REG_SZ <apppath> -osint -url "%1"
+// shell\open\ddeexec (default) REG_SZ <empty string>
+//
+// - Windows Start Menu (XP SP1 and newer)
+// -------------------------------------------------
+// The following keys are set to make Firefox appear in the Start Menu as the
+// browser:
+//
+// HKCU\SOFTWARE\Clients\StartMenuInternet\FIREFOX.EXE\
+// (default) REG_SZ <appname>
+// DefaultIcon (default) REG_SZ <apppath>,0
+// InstallInfo HideIconsCommand REG_SZ <uninstpath> /HideShortcuts
+// InstallInfo IconsVisible REG_DWORD 1
+// InstallInfo ReinstallCommand REG_SZ <uninstpath> /SetAsDefaultAppGlobal
+// InstallInfo ShowIconsCommand REG_SZ <uninstpath> /ShowShortcuts
+// shell\open\command (default) REG_SZ <apppath>
+// shell\properties (default) REG_SZ <appname> &Options
+// shell\properties\command (default) REG_SZ <apppath> -preferences
+// shell\safemode (default) REG_SZ <appname> &Safe Mode
+// shell\safemode\command (default) REG_SZ <apppath> -safe-mode
+//
+
+// The values checked are all default values so the value name is not needed.
+typedef struct {
+ const char* keyName;
+ const char* valueData;
+ const char* oldValueData;
+} SETTING;
+
+#define APP_REG_NAME L"Firefox"
+#define VAL_FILE_ICON "%APPPATH%,1"
+#define VAL_OPEN "\"%APPPATH%\" -osint -url \"%1\""
+#define OLD_VAL_OPEN "\"%APPPATH%\" -requestPending -osint -url \"%1\""
+#define DI "\\DefaultIcon"
+#define SOC "\\shell\\open\\command"
+#define SOD "\\shell\\open\\ddeexec"
+// Used for updating the FTP protocol handler's shell open command under HKCU.
+#define FTP_SOC L"Software\\Classes\\ftp\\shell\\open\\command"
+
+#define MAKE_KEY_NAME1(PREFIX, MID) \
+ PREFIX MID
+
+// The DefaultIcon registry key value should never be used when checking if
+// Firefox is the default browser for file handlers since other applications
+// (e.g. MS Office) may modify the DefaultIcon registry key value to add Icon
+// Handlers. see http://msdn2.microsoft.com/en-us/library/aa969357.aspx for
+// more info. The FTP protocol is not checked so advanced users can set the FTP
+// handler to another application and still have Firefox check if it is the
+// default HTTP and HTTPS handler.
+// *** Do not add additional checks here unless you skip them when aForAllTypes
+// is false below***.
+static SETTING gSettings[] = {
+ // File Handler Class
+ // ***keep this as the first entry because when aForAllTypes is not set below
+ // it will skip over this check.***
+ { MAKE_KEY_NAME1("FirefoxHTML", SOC), VAL_OPEN, OLD_VAL_OPEN },
+
+ // Protocol Handler Class - for Vista and above
+ { MAKE_KEY_NAME1("FirefoxURL", SOC), VAL_OPEN, OLD_VAL_OPEN },
+
+ // Protocol Handlers
+ { MAKE_KEY_NAME1("HTTP", DI), VAL_FILE_ICON },
+ { MAKE_KEY_NAME1("HTTP", SOC), VAL_OPEN, OLD_VAL_OPEN },
+ { MAKE_KEY_NAME1("HTTPS", DI), VAL_FILE_ICON },
+ { MAKE_KEY_NAME1("HTTPS", SOC), VAL_OPEN, OLD_VAL_OPEN }
+};
+
+// The settings to disable DDE are separate from the default browser settings
+// since they are only checked when Firefox is the default browser and if they
+// are incorrect they are fixed without notifying the user.
+static SETTING gDDESettings[] = {
+ // File Handler Class
+ { MAKE_KEY_NAME1("Software\\Classes\\FirefoxHTML", SOD) },
+
+ // Protocol Handler Class - for Vista and above
+ { MAKE_KEY_NAME1("Software\\Classes\\FirefoxURL", SOD) },
+
+ // Protocol Handlers
+ { MAKE_KEY_NAME1("Software\\Classes\\FTP", SOD) },
+ { MAKE_KEY_NAME1("Software\\Classes\\HTTP", SOD) },
+ { MAKE_KEY_NAME1("Software\\Classes\\HTTPS", SOD) }
+};
+
+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(NS_LITERAL_CSTRING("uninstall"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe"));
+ 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;
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::ShortcutMaintenance()
+{
+ nsresult rv;
+
+ // XXX App ids were updated to a constant install path hash,
+ // XXX this code can be removed after a few upgrade cycles.
+
+ // Launch helper.exe so it can update the application user model ids on
+ // shortcuts in the user's taskbar and start menu. This keeps older pinned
+ // shortcuts grouped correctly after major updates. Note, we also do this
+ // through the upgrade installer script, however, this is the only place we
+ // have a chance to trap links created by users who do control the install/
+ // update process of the browser.
+
+ nsCOMPtr<nsIWinTaskbar> taskbarInfo =
+ do_GetService(NS_TASKBAR_CONTRACTID);
+ if (!taskbarInfo) // If we haven't built with win7 sdk features, this fails.
+ return NS_OK;
+
+ // Avoid if this isn't Win7+
+ bool isSupported = false;
+ taskbarInfo->GetAvailable(&isSupported);
+ if (!isSupported)
+ return NS_OK;
+
+ nsAutoString appId;
+ if (NS_FAILED(taskbarInfo->GetDefaultGroupId(appId)))
+ return NS_ERROR_UNEXPECTED;
+
+ NS_NAMED_LITERAL_CSTRING(prefName, "browser.taskbar.lastgroupid");
+ nsCOMPtr<nsIPrefBranch> prefs =
+ do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (!prefs)
+ return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsISupportsString> prefString;
+ rv = prefs->GetComplexValue(prefName.get(),
+ NS_GET_IID(nsISupportsString),
+ getter_AddRefs(prefString));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoString version;
+ prefString->GetData(version);
+ if (!version.IsEmpty() && version.Equals(appId)) {
+ // We're all good, get out of here.
+ return NS_OK;
+ }
+ }
+ // Update the version in prefs
+ prefString =
+ do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ prefString->SetData(appId);
+ rv = prefs->SetComplexValue(prefName.get(),
+ NS_GET_IID(nsISupportsString),
+ prefString);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Couldn't set last user model id!");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsAutoString appHelperPath;
+ if (NS_FAILED(GetHelperPath(appHelperPath)))
+ return NS_ERROR_UNEXPECTED;
+
+ appHelperPath.AppendLiteral(" /UpdateShortcutAppUserModelIds");
+
+ return LaunchHelper(appHelperPath);
+}
+
+static bool
+IsAARDefault(const RefPtr<IApplicationAssociationRegistration>& pAAR,
+ 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 = !wcsicmp(registeredApp, progID);
+ CoTaskMemFree(registeredApp);
+
+ return isDefault;
+}
+
+static void
+IsDefaultBrowserWin8(bool aCheckAllTypes, bool* aIsDefaultBrowser)
+{
+ RefPtr<IApplicationAssociationRegistration> pAAR;
+ HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
+ nullptr,
+ CLSCTX_INPROC,
+ IID_IApplicationAssociationRegistration,
+ getter_AddRefs(pAAR));
+ if (FAILED(hr)) {
+ return;
+ }
+
+ bool res = IsAARDefault(pAAR, L"http");
+ if (*aIsDefaultBrowser) {
+ *aIsDefaultBrowser = res;
+ }
+ res = IsAARDefault(pAAR, L".html");
+ if (*aIsDefaultBrowser && aCheckAllTypes) {
+ *aIsDefaultBrowser = res;
+ }
+}
+
+/*
+ * Query's the AAR for the default status.
+ * This only checks for FirefoxURL and if aCheckAllTypes is set, then
+ * it also checks for FirefoxHTML. Note that those ProgIDs are shared
+ * by all Firefox browsers.
+*/
+bool
+nsWindowsShellService::IsDefaultBrowserVista(bool aCheckAllTypes,
+ bool* aIsDefaultBrowser)
+{
+ RefPtr<IApplicationAssociationRegistration> pAAR;
+ HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
+ nullptr,
+ CLSCTX_INPROC,
+ IID_IApplicationAssociationRegistration,
+ getter_AddRefs(pAAR));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ if (aCheckAllTypes) {
+ BOOL res;
+ hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE,
+ APP_REG_NAME,
+ &res);
+ *aIsDefaultBrowser = res;
+ } else if (!IsWin8OrLater()) {
+ *aIsDefaultBrowser = IsAARDefault(pAAR, L"http");
+ }
+
+ return true;
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
+ bool aForAllTypes,
+ bool* aIsDefaultBrowser)
+{
+ // Assume we're the default unless one of the several checks below tell us
+ // otherwise.
+ *aIsDefaultBrowser = true;
+
+ wchar_t exePath[MAX_BUF];
+ if (!::GetModuleFileNameW(0, exePath, MAX_BUF))
+ return NS_ERROR_FAILURE;
+
+ // Convert the path to a long path since GetModuleFileNameW returns the path
+ // that was used to launch Firefox which is not necessarily a long path.
+ if (!::GetLongPathNameW(exePath, exePath, MAX_BUF))
+ return NS_ERROR_FAILURE;
+
+ nsAutoString appLongPath(exePath);
+
+ HKEY theKey;
+ DWORD res;
+ nsresult rv;
+ wchar_t currValue[MAX_BUF];
+
+ SETTING* settings = gSettings;
+ if (!aForAllTypes && IsWin8OrLater()) {
+ // Skip over the file handler check
+ settings++;
+ }
+
+ SETTING* end = gSettings + sizeof(gSettings) / sizeof(SETTING);
+
+ for (; settings < end; ++settings) {
+ NS_ConvertUTF8toUTF16 keyName(settings->keyName);
+ NS_ConvertUTF8toUTF16 valueData(settings->valueData);
+ int32_t offset = valueData.Find("%APPPATH%");
+ valueData.Replace(offset, 9, appLongPath);
+
+ rv = OpenKeyForReading(HKEY_CLASSES_ROOT, keyName, &theKey);
+ if (NS_FAILED(rv)) {
+ *aIsDefaultBrowser = false;
+ return NS_OK;
+ }
+
+ ::ZeroMemory(currValue, sizeof(currValue));
+ DWORD len = sizeof currValue;
+ res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr,
+ (LPBYTE)currValue, &len);
+ // Close the key that was opened.
+ ::RegCloseKey(theKey);
+ if (REG_FAILED(res) ||
+ _wcsicmp(valueData.get(), currValue)) {
+ // Key wasn't set or was set to something other than our registry entry.
+ NS_ConvertUTF8toUTF16 oldValueData(settings->oldValueData);
+ offset = oldValueData.Find("%APPPATH%");
+ oldValueData.Replace(offset, 9, appLongPath);
+ // The current registry value doesn't match the current or the old format.
+ if (_wcsicmp(oldValueData.get(), currValue)) {
+ *aIsDefaultBrowser = false;
+ return NS_OK;
+ }
+
+ res = ::RegOpenKeyExW(HKEY_CLASSES_ROOT, keyName.get(),
+ 0, KEY_SET_VALUE, &theKey);
+ if (REG_FAILED(res)) {
+ // If updating the open command fails try to update it using the helper
+ // application when setting Firefox as the default browser.
+ *aIsDefaultBrowser = false;
+ return NS_OK;
+ }
+
+ res = ::RegSetValueExW(theKey, L"", 0, REG_SZ,
+ (const BYTE *) valueData.get(),
+ (valueData.Length() + 1) * sizeof(char16_t));
+ // Close the key that was created.
+ ::RegCloseKey(theKey);
+ if (REG_FAILED(res)) {
+ // If updating the open command fails try to update it using the helper
+ // application when setting Firefox as the default browser.
+ *aIsDefaultBrowser = false;
+ return NS_OK;
+ }
+ }
+ }
+
+ // Only check if Firefox is the default browser on Vista and above if the
+ // previous checks show that Firefox is the default browser.
+ if (*aIsDefaultBrowser) {
+ IsDefaultBrowserVista(aForAllTypes, aIsDefaultBrowser);
+ if (IsWin8OrLater()) {
+ IsDefaultBrowserWin8(aForAllTypes, aIsDefaultBrowser);
+ }
+ }
+
+ // To handle the case where DDE isn't disabled due for a user because there
+ // account didn't perform a Firefox update this will check if Firefox is the
+ // default browser and if dde is disabled for each handler
+ // and if it isn't disable it. When Firefox is not the default browser the
+ // helper application will disable dde for each handler.
+ if (*aIsDefaultBrowser && aForAllTypes) {
+ // Check ftp settings
+
+ end = gDDESettings + sizeof(gDDESettings) / sizeof(SETTING);
+
+ for (settings = gDDESettings; settings < end; ++settings) {
+ NS_ConvertUTF8toUTF16 keyName(settings->keyName);
+
+ rv = OpenKeyForReading(HKEY_CURRENT_USER, keyName, &theKey);
+ if (NS_FAILED(rv)) {
+ ::RegCloseKey(theKey);
+ // If disabling DDE fails try to disable it using the helper
+ // application when setting Firefox as the default browser.
+ *aIsDefaultBrowser = false;
+ return NS_OK;
+ }
+
+ ::ZeroMemory(currValue, sizeof(currValue));
+ DWORD len = sizeof currValue;
+ res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr,
+ (LPBYTE)currValue, &len);
+ // Close the key that was opened.
+ ::RegCloseKey(theKey);
+ if (REG_FAILED(res) || char16_t('\0') != *currValue) {
+ // Key wasn't set or was set to something other than our registry entry.
+ // Delete the key along with all of its childrean and then recreate it.
+ ::SHDeleteKeyW(HKEY_CURRENT_USER, keyName.get());
+ res = ::RegCreateKeyExW(HKEY_CURRENT_USER, keyName.get(), 0, nullptr,
+ REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,
+ nullptr, &theKey, nullptr);
+ if (REG_FAILED(res)) {
+ // If disabling DDE fails try to disable it using the helper
+ // application when setting Firefox as the default browser.
+ *aIsDefaultBrowser = false;
+ return NS_OK;
+ }
+
+ res = ::RegSetValueExW(theKey, L"", 0, REG_SZ, (const BYTE *) L"",
+ sizeof(char16_t));
+ // Close the key that was created.
+ ::RegCloseKey(theKey);
+ if (REG_FAILED(res)) {
+ // If disabling DDE fails try to disable it using the helper
+ // application when setting Firefox as the default browser.
+ *aIsDefaultBrowser = false;
+ return NS_OK;
+ }
+ }
+ }
+
+ // Update the FTP protocol handler's shell open command if it is the old
+ // format.
+ res = ::RegOpenKeyExW(HKEY_CURRENT_USER, FTP_SOC, 0, KEY_ALL_ACCESS,
+ &theKey);
+ // Don't update the FTP protocol handler's shell open command when opening
+ // its registry key fails under HKCU since it most likely doesn't exist.
+ if (NS_FAILED(rv)) {
+ return NS_OK;
+ }
+
+ NS_ConvertUTF8toUTF16 oldValueOpen(OLD_VAL_OPEN);
+ int32_t offset = oldValueOpen.Find("%APPPATH%");
+ oldValueOpen.Replace(offset, 9, appLongPath);
+
+ ::ZeroMemory(currValue, sizeof(currValue));
+ DWORD len = sizeof currValue;
+ res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, (LPBYTE)currValue,
+ &len);
+
+ // Don't update the FTP protocol handler's shell open command when the
+ // current registry value doesn't exist or matches the old format.
+ if (REG_FAILED(res) ||
+ _wcsicmp(oldValueOpen.get(), currValue)) {
+ ::RegCloseKey(theKey);
+ return NS_OK;
+ }
+
+ NS_ConvertUTF8toUTF16 valueData(VAL_OPEN);
+ valueData.Replace(offset, 9, appLongPath);
+ res = ::RegSetValueExW(theKey, L"", 0, REG_SZ,
+ (const BYTE *) valueData.get(),
+ (valueData.Length() + 1) * sizeof(char16_t));
+ // Close the key that was created.
+ ::RegCloseKey(theKey);
+ // If updating the FTP protocol handlers shell open command fails try to
+ // update it using the helper application when setting Firefox as the
+ // default browser.
+ if (REG_FAILED(res)) {
+ *aIsDefaultBrowser = false;
+ }
+ }
+
+ return NS_OK;
+}
+
+static nsresult
+DynSHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo)
+{
+ // shell32.dll is in the knownDLLs list so will always be loaded from the
+ // system32 directory.
+ static const wchar_t kSehllLibraryName[] = L"shell32.dll";
+ HMODULE shellDLL = ::LoadLibraryW(kSehllLibraryName);
+ if (!shellDLL) {
+ return NS_ERROR_FAILURE;
+ }
+
+ decltype(SHOpenWithDialog)* SHOpenWithDialogFn =
+ (decltype(SHOpenWithDialog)*) GetProcAddress(shellDLL, "SHOpenWithDialog");
+
+ if (!SHOpenWithDialogFn) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ HRESULT hr = SHOpenWithDialogFn(hwndParent, poainfo);
+ if (SUCCEEDED(hr) || (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))) {
+ rv = NS_OK;
+ } else {
+ rv = NS_ERROR_FAILURE;
+ }
+ FreeLibrary(shellDLL);
+ return rv;
+}
+
+nsresult
+nsWindowsShellService::LaunchControlPanelDefaultsSelectionUI()
+{
+ IApplicationAssociationRegistrationUI* pAARUI;
+ HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistrationUI,
+ NULL,
+ CLSCTX_INPROC,
+ IID_IApplicationAssociationRegistrationUI,
+ (void**)&pAARUI);
+ if (SUCCEEDED(hr)) {
+ hr = pAARUI->LaunchAdvancedAssociationUI(APP_REG_NAME);
+ pAARUI->Release();
+ }
+ return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult
+nsWindowsShellService::LaunchControlPanelDefaultPrograms()
+{
+ // This Default Programs feature is Win7+ only.
+ if (!IsWin7OrLater()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Build the path control.exe path safely
+ WCHAR controlEXEPath[MAX_PATH + 1] = { '\0' };
+ if (!GetSystemDirectoryW(controlEXEPath, MAX_PATH)) {
+ return NS_ERROR_FAILURE;
+ }
+ LPCWSTR controlEXE = L"control.exe";
+ if (wcslen(controlEXEPath) + wcslen(controlEXE) >= MAX_PATH) {
+ return NS_ERROR_FAILURE;
+ }
+ if (!PathAppendW(controlEXEPath, controlEXE)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ WCHAR params[] = L"control.exe /name Microsoft.DefaultPrograms /page "
+ "pageDefaultProgram\\pageAdvancedSettings?pszAppName=" APP_REG_NAME;
+ STARTUPINFOW si = {sizeof(si), 0};
+ si.dwFlags = STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_SHOWDEFAULT;
+ PROCESS_INFORMATION pi = {0};
+ if (!CreateProcessW(controlEXEPath, params, nullptr, nullptr, FALSE,
+ 0, nullptr, nullptr, &si, &pi)) {
+ return NS_ERROR_FAILURE;
+ }
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+
+ return NS_OK;
+}
+
+static bool
+IsWindowsLogonConnected()
+{
+ WCHAR userName[UNLEN + 1];
+ DWORD size = ArrayLength(userName);
+ if (!GetUserNameW(userName, &size)) {
+ return false;
+ }
+
+ LPUSER_INFO_24 info;
+ if (NetUserGetInfo(nullptr, userName, 24, (LPBYTE *)&info)
+ != NERR_Success) {
+ return false;
+ }
+ bool connected = info->usri24_internet_identity;
+ NetApiBufferFree(info);
+
+ return connected;
+}
+
+static bool
+SettingsAppBelievesConnected()
+{
+ nsresult rv;
+ nsCOMPtr<nsIWindowsRegKey> regKey =
+ do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ NS_LITERAL_STRING("SOFTWARE\\Microsoft\\Windows\\Shell\\Associations"),
+ nsIWindowsRegKey::ACCESS_READ);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ uint32_t value;
+ rv = regKey->ReadIntValue(NS_LITERAL_STRING("IsConnectedAtLogon"), &value);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ return !!value;
+}
+
+nsresult
+nsWindowsShellService::LaunchModernSettingsDialogDefaultApps()
+{
+ if (!IsWindowsBuildOrLater(14965) &&
+ !IsWindowsLogonConnected() && SettingsAppBelievesConnected()) {
+ // Use the classic Control Panel to work around a bug of older
+ // builds of Windows 10.
+ return LaunchControlPanelDefaultPrograms();
+ }
+
+ IApplicationActivationManager* pActivator;
+ HRESULT hr = CoCreateInstance(CLSID_ApplicationActivationManager,
+ nullptr,
+ CLSCTX_INPROC,
+ IID_IApplicationActivationManager,
+ (void**)&pActivator);
+
+ if (SUCCEEDED(hr)) {
+ DWORD pid;
+ hr = pActivator->ActivateApplication(
+ L"windows.immersivecontrolpanel_cw5n1h2txyewy"
+ L"!microsoft.windows.immersivecontrolpanel",
+ L"page=SettingsPageAppsDefaults", AO_NONE, &pid);
+ if (SUCCEEDED(hr)) {
+ // Do not check error because we could at least open
+ // the "Default apps" setting.
+ pActivator->ActivateApplication(
+ L"windows.immersivecontrolpanel_cw5n1h2txyewy"
+ L"!microsoft.windows.immersivecontrolpanel",
+ L"page=SettingsPageAppsDefaults"
+ L"&target=SystemSettings_DefaultApps_Browser", AO_NONE, &pid);
+ }
+ pActivator->Release();
+ return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+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(
+ NS_LITERAL_STRING("app.support.baseURL"), urlStr);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!StringBeginsWith(urlStr, NS_LITERAL_STRING("https://"))) {
+ 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;
+ return DynSHOpenWithDialog(nullptr, &info);
+}
+
+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<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() == SurfaceFormat::B8G8R8A8 ||
+ surface->GetFormat() == SurfaceFormat::B8G8R8X8);
+
+ RefPtr<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);
+
+ DataSourceSurface::MappedSurface map;
+ if (!dataSurface->Map(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(nsIDOMElement* aElement,
+ int32_t aPosition)
+{
+ nsresult rv;
+
+ nsCOMPtr<imgIContainer> container;
+ nsCOMPtr<nsIDOMHTMLImageElement> imgElement(do_QueryInterface(aElement));
+ if (!imgElement) {
+ // XXX write background loading stuff!
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ else {
+ 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;
+ 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"
+ nsString fileLeafName;
+ rv = shellBundle->GetStringFromName
+ (u"desktopBackgroundLeafNameWin",
+ getter_Copies(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
+ 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,
+ NS_LITERAL_STRING("Control Panel\\Desktop"),
+ 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;
+ }
+
+ rv = regKey->WriteStringValue(NS_LITERAL_STRING("TileWallpaper"), tile);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = regKey->WriteStringValue(NS_LITERAL_STRING("WallpaperStyle"), 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::OpenApplication(int32_t aApplication)
+{
+ nsAutoString application;
+ switch (aApplication) {
+ case nsIShellService::APPLICATION_MAIL:
+ application.AssignLiteral("Mail");
+ break;
+ case nsIShellService::APPLICATION_NEWS:
+ application.AssignLiteral("News");
+ break;
+ }
+
+ // The Default Client section of the Windows Registry looks like this:
+ //
+ // Clients\aClient\
+ // e.g. aClient = "Mail"...
+ // \Mail\(default) = Client Subkey Name
+ // \Client Subkey Name
+ // \Client Subkey Name\shell\open\command\
+ // \Client Subkey Name\shell\open\command\(default) = path to exe
+ //
+
+ // Find the default application for this class.
+ HKEY theKey;
+ nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey);
+ if (NS_FAILED(rv))
+ return rv;
+
+ wchar_t buf[MAX_BUF];
+ DWORD type, len = sizeof buf;
+ DWORD res = ::RegQueryValueExW(theKey, EmptyString().get(), 0,
+ &type, (LPBYTE)&buf, &len);
+
+ if (REG_FAILED(res) || !*buf)
+ return NS_OK;
+
+ // Close the key we opened.
+ ::RegCloseKey(theKey);
+
+ // Find the "open" command
+ application.Append('\\');
+ application.Append(buf);
+ application.AppendLiteral("\\shell\\open\\command");
+
+ rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey);
+ if (NS_FAILED(rv))
+ return rv;
+
+ ::ZeroMemory(buf, sizeof(buf));
+ len = sizeof buf;
+ res = ::RegQueryValueExW(theKey, EmptyString().get(), 0,
+ &type, (LPBYTE)&buf, &len);
+ if (REG_FAILED(res) || !*buf)
+ return NS_ERROR_FAILURE;
+
+ // Close the key we opened.
+ ::RegCloseKey(theKey);
+
+ // Look for any embedded environment variables and substitute their
+ // values, as |::CreateProcessW| is unable to do this.
+ nsAutoString path(buf);
+ int32_t end = path.Length();
+ int32_t cursor = 0, temp = 0;
+ ::ZeroMemory(buf, sizeof(buf));
+ do {
+ cursor = path.FindChar('%', cursor);
+ if (cursor < 0)
+ break;
+
+ temp = path.FindChar('%', cursor + 1);
+ ++cursor;
+
+ ::ZeroMemory(&buf, sizeof(buf));
+
+ ::GetEnvironmentVariableW(nsAutoString(Substring(path, cursor, temp - cursor)).get(),
+ buf, sizeof(buf));
+
+ // "+ 2" is to subtract the extra characters used to delimit the environment
+ // variable ('%').
+ path.Replace((cursor - 1), temp - cursor + 2, nsDependentString(buf));
+
+ ++cursor;
+ }
+ while (cursor < end);
+
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+
+ ::ZeroMemory(&si, sizeof(STARTUPINFOW));
+ ::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
+
+ BOOL success = ::CreateProcessW(nullptr, (LPWSTR)path.get(), nullptr,
+ nullptr, FALSE, 0, nullptr, nullptr,
+ &si, &pi);
+ if (!success)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+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,
+ NS_LITERAL_STRING("Control Panel\\Colors"),
+ 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(NS_LITERAL_STRING("Background"),
+ nsDependentString(rgb));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return regKey->Close();
+}
+
+nsWindowsShellService::nsWindowsShellService()
+{
+}
+
+nsWindowsShellService::~nsWindowsShellService()
+{
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::OpenApplicationWithURI(nsIFile* aApplication,
+ const nsACString& aURI)
+{
+ nsresult rv;
+ nsCOMPtr<nsIProcess> process =
+ do_CreateInstance("@mozilla.org/process/util;1", &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = process->Init(aApplication);
+ if (NS_FAILED(rv))
+ return rv;
+
+ const nsCString spec(aURI);
+ const char* specStr = spec.get();
+ return process->Run(false, &specStr, 1);
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::GetDefaultFeedReader(nsIFile** _retval)
+{
+ *_retval = nullptr;
+
+ nsresult rv;
+ nsCOMPtr<nsIWindowsRegKey> regKey =
+ do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+ NS_LITERAL_STRING("feed\\shell\\open\\command"),
+ nsIWindowsRegKey::ACCESS_READ);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString path;
+ rv = regKey->ReadStringValue(EmptyString(), path);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (path.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ if (path.First() == '"') {
+ // Everything inside the quotes
+ path = Substring(path, 1, path.FindChar('"', 1) - 1);
+ }
+ else {
+ // Everything up to the first space
+ path = Substring(path, 0, path.FindChar(' '));
+ }
+
+ nsCOMPtr<nsIFile> defaultReader =
+ do_CreateInstance("@mozilla.org/file/local;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = defaultReader->InitWithPath(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool exists;
+ rv = defaultReader->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!exists)
+ return NS_ERROR_FAILURE;
+
+ NS_ADDREF(*_retval = defaultReader);
+ return NS_OK;
+}