gecko-dev/browser/components/shell/WindowsDefaultBrowser.cpp

178 lines
5.6 KiB
C++

/* -*- 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/. */
/**
* This file exists so that LaunchModernSettingsDialogDefaultApps can be called
* without linking to libxul.
*/
#include "WindowsDefaultBrowser.h"
#include "city.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/WindowsVersion.h"
#include "mozilla/WinHeaderOnlyUtils.h"
#include "nsDebug.h"
#include "nsError.h"
// Needed for access to IApplicationActivationManager
// This must be before any other includes that might include shlobj.h
#ifdef _WIN32_WINNT
# undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0600
#define INITGUID
#undef NTDDI_VERSION
#define NTDDI_VERSION NTDDI_WIN8
#include <shlobj.h>
#include <lm.h>
#include <shlwapi.h>
#include <wchar.h>
#include <windows.h>
#define APP_REG_NAME_BASE L"Firefox-"
static bool IsWindowsLogonConnected() {
WCHAR userName[UNLEN + 1];
DWORD size = mozilla::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() {
const wchar_t* keyPath = L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations";
const wchar_t* valueName = L"IsConnectedAtLogon";
uint32_t value = 0;
DWORD size = sizeof(uint32_t);
LSTATUS ls = RegGetValueW(HKEY_CURRENT_USER, keyPath, valueName, RRF_RT_ANY,
nullptr, &value, &size);
if (ls != ERROR_SUCCESS) {
return false;
}
return !!value;
}
// Generates the install directory without a trailing path separator.
nsresult GetInstallDirectory(mozilla::UniquePtr<wchar_t[]>& installPath) {
installPath = mozilla::GetFullBinaryPath();
// It's not safe to use PathRemoveFileSpecW with strings longer than MAX_PATH
// (including null terminator).
if (wcslen(installPath.get()) >= MAX_PATH) {
return NS_ERROR_FAILURE;
}
PathRemoveFileSpecW(installPath.get());
return NS_OK;
}
nsresult GetAppRegName(mozilla::UniquePtr<wchar_t[]>& aAppRegName) {
mozilla::UniquePtr<wchar_t[]> appDirStr;
nsresult rv = GetInstallDirectory(appDirStr);
// Can't use NS_ENSURE_SUCCESS in non XUL code. In debug mode it uses
// nsString.
if (NS_FAILED(rv)) {
return rv;
}
uint64_t hash = CityHash64(reinterpret_cast<char*>(appDirStr.get()),
wcslen(appDirStr.get()) * sizeof(wchar_t));
const wchar_t* format = L"%s%I64X";
int bufferSize = _scwprintf(format, APP_REG_NAME_BASE, hash);
++bufferSize; // Extra byte for terminating null
aAppRegName = mozilla::MakeUnique<wchar_t[]>(bufferSize);
_snwprintf_s(aAppRegName.get(), bufferSize, _TRUNCATE, format,
APP_REG_NAME_BASE, hash);
return rv;
}
nsresult LaunchControlPanelDefaultPrograms() {
// 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;
}
const wchar_t* paramFormat =
L"control.exe /name Microsoft.DefaultPrograms "
L"/page pageDefaultProgram\\pageAdvancedSettings?pszAppName=%s";
mozilla::UniquePtr<wchar_t[]> appRegName;
GetAppRegName(appRegName);
int bufferSize = _scwprintf(paramFormat, appRegName.get());
++bufferSize; // Extra byte for terminating null
mozilla::UniquePtr<wchar_t[]> params =
mozilla::MakeUnique<wchar_t[]>(bufferSize);
_snwprintf_s(params.get(), bufferSize, _TRUNCATE, paramFormat,
appRegName.get());
STARTUPINFOW si = {sizeof(si), 0};
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWDEFAULT;
PROCESS_INFORMATION pi = {0};
if (!CreateProcessW(controlEXEPath, params.get(), nullptr, nullptr, FALSE, 0,
nullptr, nullptr, &si, &pi)) {
return NS_ERROR_FAILURE;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return NS_OK;
}
nsresult LaunchModernSettingsDialogDefaultApps() {
if (!mozilla::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;
}