Backed out changeset 45ab2db62a65 (bug 1818418) for causing bustages in WindowsUserChoice.h
This commit is contained in:
@@ -20,16 +20,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <appmodel.h> // for GetPackageFamilyName
|
|
||||||
#include <sddl.h> // for ConvertSidToStringSidW
|
#include <sddl.h> // for ConvertSidToStringSidW
|
||||||
#include <wincrypt.h> // for CryptoAPI base64
|
#include <wincrypt.h> // for CryptoAPI base64
|
||||||
#include <bcrypt.h> // for CNG MD5
|
#include <bcrypt.h> // for CNG MD5
|
||||||
#include <winternl.h> // for NT_SUCCESS()
|
#include <winternl.h> // for NT_SUCCESS()
|
||||||
|
|
||||||
#include "ErrorList.h"
|
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
#include "mozilla/UniquePtr.h"
|
#include "mozilla/UniquePtr.h"
|
||||||
#include "nsDebug.h"
|
|
||||||
#include "nsWindowsHelpers.h"
|
#include "nsWindowsHelpers.h"
|
||||||
|
|
||||||
#include "WindowsUserChoice.h"
|
#include "WindowsUserChoice.h"
|
||||||
@@ -423,53 +420,3 @@ bool CheckProgIDExists(const wchar_t* aProgID) {
|
|||||||
::RegCloseKey(key);
|
::RegCloseKey(key);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult GetMsixProgId(const wchar_t* assoc, UniquePtr<wchar_t[]>& aProgId) {
|
|
||||||
// Retrieve the registry path to the package from registry path:
|
|
||||||
// clang-format off
|
|
||||||
// HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages\[Package Full Name]\App\Capabilities\[FileAssociations | URLAssociations]\[File | URL]
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
UINT32 pfnLen = 0;
|
|
||||||
LONG rv = GetCurrentPackageFullName(&pfnLen, nullptr);
|
|
||||||
NS_ENSURE_TRUE(rv != APPMODEL_ERROR_NO_PACKAGE, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
auto pfn = mozilla::MakeUnique<wchar_t[]>(pfnLen);
|
|
||||||
rv = GetCurrentPackageFullName(&pfnLen, pfn.get());
|
|
||||||
NS_ENSURE_TRUE(rv == ERROR_SUCCESS, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
const wchar_t* assocSuffix;
|
|
||||||
if (assoc[0] == L'.') {
|
|
||||||
// File association.
|
|
||||||
assocSuffix = LR"(App\Capabilities\FileAssociations)";
|
|
||||||
} else {
|
|
||||||
// URL association.
|
|
||||||
assocSuffix = LR"(App\Capabilities\URLAssociations)";
|
|
||||||
}
|
|
||||||
|
|
||||||
const wchar_t* assocPathFmt =
|
|
||||||
LR"(Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages\%s\%s)";
|
|
||||||
int assocPathLen = _scwprintf(assocPathFmt, pfn.get(), assocSuffix);
|
|
||||||
assocPathLen += 1; // _scwprintf does not include the terminator
|
|
||||||
|
|
||||||
auto assocPath = MakeUnique<wchar_t[]>(assocPathLen);
|
|
||||||
_snwprintf_s(assocPath.get(), assocPathLen, _TRUNCATE, assocPathFmt,
|
|
||||||
pfn.get(), assocSuffix);
|
|
||||||
|
|
||||||
LSTATUS ls;
|
|
||||||
|
|
||||||
// Retrieve the package association's ProgID, always in the form `AppX[32 hash
|
|
||||||
// characters]`.
|
|
||||||
const size_t appxProgIdLen = 37;
|
|
||||||
auto progId = MakeUnique<wchar_t[]>(appxProgIdLen);
|
|
||||||
DWORD progIdLen = appxProgIdLen * sizeof(wchar_t);
|
|
||||||
ls = ::RegGetValueW(HKEY_CLASSES_ROOT, assocPath.get(), assoc, RRF_RT_REG_SZ,
|
|
||||||
nullptr, (LPBYTE)progId.get(), &progIdLen);
|
|
||||||
if (ls != ERROR_SUCCESS) {
|
|
||||||
return NS_ERROR_WDBA_NO_PROGID;
|
|
||||||
}
|
|
||||||
|
|
||||||
aProgId.swap(progId);
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -100,17 +100,4 @@ mozilla::UniquePtr<wchar_t[]> FormatProgID(const wchar_t* aProgIDBase,
|
|||||||
*/
|
*/
|
||||||
bool CheckProgIDExists(const wchar_t* aProgID);
|
bool CheckProgIDExists(const wchar_t* aProgID);
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the ProgID registered by Windows for the given association.
|
|
||||||
*
|
|
||||||
* The MSIX `AppManifest.xml` declares supported protocols and file
|
|
||||||
* type associations. Upon installation, Windows generates
|
|
||||||
* corresponding ProgIDs for them, of the form `AppX*`. This function
|
|
||||||
* retrieves those generated ProgIDs (from the Windows registry).
|
|
||||||
*
|
|
||||||
* @return ProgID.
|
|
||||||
*/
|
|
||||||
nsresult GetMsixProgId(const wchar_t* assoc,
|
|
||||||
mozilla::UniquePtr<wchar_t[]>& aProgId);
|
|
||||||
|
|
||||||
#endif // SHELL_WINDOWSUSERCHOICE_H__
|
#endif // SHELL_WINDOWSUSERCHOICE_H__
|
||||||
|
|||||||
@@ -300,41 +300,10 @@ nsWindowsShellService::CheckAllProgIDsExist(bool* aResult) {
|
|||||||
if (!mozilla::widget::WinTaskbar::GetAppUserModelID(aumid)) {
|
if (!mozilla::widget::WinTaskbar::GetAppUserModelID(aumid)) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
*aResult =
|
||||||
if (widget::WinUtils::HasPackageIdentity()) {
|
CheckProgIDExists(FormatProgID(L"FirefoxURL", aumid.get()).get()) &&
|
||||||
UniquePtr<wchar_t[]> extraProgID;
|
CheckProgIDExists(FormatProgID(L"FirefoxHTML", aumid.get()).get()) &&
|
||||||
nsresult rv;
|
CheckProgIDExists(FormatProgID(L"FirefoxPDF", aumid.get()).get());
|
||||||
bool result = true;
|
|
||||||
|
|
||||||
// "FirefoxURL".
|
|
||||||
rv = GetMsixProgId(L"https", extraProgID);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
result = result && CheckProgIDExists(extraProgID.get());
|
|
||||||
|
|
||||||
// "FirefoxHTML".
|
|
||||||
rv = GetMsixProgId(L".htm", extraProgID);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
result = result && CheckProgIDExists(extraProgID.get());
|
|
||||||
|
|
||||||
// "FirefoxPDF".
|
|
||||||
rv = GetMsixProgId(L".pdf", extraProgID);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
result = result && CheckProgIDExists(extraProgID.get());
|
|
||||||
|
|
||||||
*aResult = result;
|
|
||||||
} else {
|
|
||||||
*aResult =
|
|
||||||
CheckProgIDExists(FormatProgID(L"FirefoxURL", aumid.get()).get()) &&
|
|
||||||
CheckProgIDExists(FormatProgID(L"FirefoxHTML", aumid.get()).get()) &&
|
|
||||||
CheckProgIDExists(FormatProgID(L"FirefoxPDF", aumid.get()).get());
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -198,8 +198,9 @@ def get_branding(use_official, topsrcdir, build_app, finder, log=None):
|
|||||||
conf_vars = mozpath.join(topsrcdir, build_app, "confvars.sh")
|
conf_vars = mozpath.join(topsrcdir, build_app, "confvars.sh")
|
||||||
|
|
||||||
def conf_vars_value(key):
|
def conf_vars_value(key):
|
||||||
lines = [line.strip() for line in open(conf_vars).readlines()]
|
lines = open(conf_vars).readlines()
|
||||||
for line in lines:
|
for line in lines:
|
||||||
|
line = line.strip()
|
||||||
if line and line[0] == "#":
|
if line and line[0] == "#":
|
||||||
continue
|
continue
|
||||||
if key not in line:
|
if key not in line:
|
||||||
@@ -832,7 +833,7 @@ def _sign_msix_win(output, force, log, verbose):
|
|||||||
else:
|
else:
|
||||||
thumbprint = None
|
thumbprint = None
|
||||||
|
|
||||||
if force or not thumbprint:
|
if not thumbprint:
|
||||||
thumbprint = (
|
thumbprint = (
|
||||||
powershell(
|
powershell(
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -4,11 +4,9 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <appmodel.h>
|
|
||||||
#include <shlobj.h> // for SHChangeNotify and IApplicationAssociationRegistration
|
#include <shlobj.h> // for SHChangeNotify and IApplicationAssociationRegistration
|
||||||
|
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
#include "mozilla/CmdLineAndEnvUtils.h"
|
|
||||||
#include "mozilla/RefPtr.h"
|
#include "mozilla/RefPtr.h"
|
||||||
#include "mozilla/UniquePtr.h"
|
#include "mozilla/UniquePtr.h"
|
||||||
#include "mozilla/WindowsVersion.h"
|
#include "mozilla/WindowsVersion.h"
|
||||||
@@ -88,8 +86,50 @@ static bool CheckEqualMinutes(SYSTEMTIME aSystemTime1,
|
|||||||
(fileTime1.dwHighDateTime == fileTime2.dwHighDateTime);
|
(fileTime1.dwHighDateTime == fileTime2.dwHighDateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool SetUserChoiceRegistry(const wchar_t* aExt, const wchar_t* aProgID,
|
/*
|
||||||
mozilla::UniquePtr<wchar_t[]> aHash) {
|
* Set an association with a UserChoice key
|
||||||
|
*
|
||||||
|
* Removes the old key, creates a new one with ProgID and Hash set to
|
||||||
|
* enable a new asociation.
|
||||||
|
*
|
||||||
|
* @param aExt File type or protocol to associate
|
||||||
|
* @param aSid Current user's string SID
|
||||||
|
* @param aProgID ProgID to use for the asociation
|
||||||
|
*
|
||||||
|
* @return true if successful, false on error.
|
||||||
|
*/
|
||||||
|
static bool SetUserChoice(const wchar_t* aExt, const wchar_t* aSid,
|
||||||
|
const wchar_t* aProgID) {
|
||||||
|
SYSTEMTIME hashTimestamp;
|
||||||
|
::GetSystemTime(&hashTimestamp);
|
||||||
|
auto hash = GenerateUserChoiceHash(aExt, aSid, aProgID, hashTimestamp);
|
||||||
|
if (!hash) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The hash changes at the end of each minute, so check that the hash should
|
||||||
|
// be the same by the time we're done writing.
|
||||||
|
const ULONGLONG kWriteTimingThresholdMilliseconds = 100;
|
||||||
|
// Generating the hash could have taken some time, so start from now.
|
||||||
|
SYSTEMTIME writeEndTimestamp;
|
||||||
|
::GetSystemTime(&writeEndTimestamp);
|
||||||
|
if (!AddMillisecondsToSystemTime(writeEndTimestamp,
|
||||||
|
kWriteTimingThresholdMilliseconds)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!CheckEqualMinutes(hashTimestamp, writeEndTimestamp)) {
|
||||||
|
LOG_ERROR_MESSAGE(
|
||||||
|
L"Hash is too close to expiration, sleeping until next hash.");
|
||||||
|
::Sleep(kWriteTimingThresholdMilliseconds * 2);
|
||||||
|
|
||||||
|
// For consistency, use the current time.
|
||||||
|
::GetSystemTime(&hashTimestamp);
|
||||||
|
hash = GenerateUserChoiceHash(aExt, aSid, aProgID, hashTimestamp);
|
||||||
|
if (!hash) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto assocKeyPath = GetAssociationKeyPath(aExt);
|
auto assocKeyPath = GetAssociationKeyPath(aExt);
|
||||||
if (!assocKeyPath) {
|
if (!assocKeyPath) {
|
||||||
return false;
|
return false;
|
||||||
@@ -134,9 +174,9 @@ static bool SetUserChoiceRegistry(const wchar_t* aExt, const wchar_t* aProgID,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD hashByteCount = (::lstrlenW(aHash.get()) + 1) * sizeof(wchar_t);
|
DWORD hashByteCount = (::lstrlenW(hash.get()) + 1) * sizeof(wchar_t);
|
||||||
ls = ::RegSetValueExW(userChoiceKey.get(), L"Hash", 0, REG_SZ,
|
ls = ::RegSetValueExW(userChoiceKey.get(), L"Hash", 0, REG_SZ,
|
||||||
reinterpret_cast<const unsigned char*>(aHash.get()),
|
reinterpret_cast<const unsigned char*>(hash.get()),
|
||||||
hashByteCount);
|
hashByteCount);
|
||||||
if (ls != ERROR_SUCCESS) {
|
if (ls != ERROR_SUCCESS) {
|
||||||
LOG_ERROR(HRESULT_FROM_WIN32(ls));
|
LOG_ERROR(HRESULT_FROM_WIN32(ls));
|
||||||
@@ -146,162 +186,6 @@ static bool SetUserChoiceRegistry(const wchar_t* aExt, const wchar_t* aProgID,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool LaunchReg(int aArgsLength, const wchar_t* const* aArgs) {
|
|
||||||
mozilla::UniquePtr<wchar_t[]> regPath =
|
|
||||||
mozilla::MakeUnique<wchar_t[]>(MAX_PATH + 1);
|
|
||||||
if (!ConstructSystem32Path(L"reg.exe", regPath.get(), MAX_PATH + 1)) {
|
|
||||||
LOG_ERROR_MESSAGE(L"Failed to construct path to reg.exe");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const wchar_t* regArgs[] = {regPath.get()};
|
|
||||||
mozilla::UniquePtr<wchar_t[]> regCmdLine(mozilla::MakeCommandLine(
|
|
||||||
mozilla::ArrayLength(regArgs), const_cast<wchar_t**>(regArgs),
|
|
||||||
aArgsLength, const_cast<wchar_t**>(aArgs)));
|
|
||||||
|
|
||||||
PROCESS_INFORMATION pi;
|
|
||||||
STARTUPINFOW si = {sizeof(si)};
|
|
||||||
si.dwFlags = STARTF_USESHOWWINDOW;
|
|
||||||
si.wShowWindow = SW_HIDE;
|
|
||||||
if (!::CreateProcessW(regPath.get(), regCmdLine.get(), nullptr, nullptr,
|
|
||||||
FALSE, 0, nullptr, nullptr, &si, &pi)) {
|
|
||||||
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
LOG_ERROR(hr);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsAutoHandle process(pi.hProcess);
|
|
||||||
nsAutoHandle mainThread(pi.hThread);
|
|
||||||
|
|
||||||
DWORD exitCode;
|
|
||||||
if (::WaitForSingleObject(process.get(), INFINITE) == WAIT_OBJECT_0 &&
|
|
||||||
::GetExitCodeProcess(process.get(), &exitCode)) {
|
|
||||||
// N.b.: `reg.exe` returns 0 (unchanged) or 2 (changed) on success.
|
|
||||||
bool success = (exitCode == 0 || exitCode == 2);
|
|
||||||
if (!success) {
|
|
||||||
LOG_ERROR_MESSAGE(L"%s returned failure exitCode %d", regCmdLine.get(),
|
|
||||||
exitCode);
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool SetUserChoiceCommand(const wchar_t* aExt, const wchar_t* aProgID,
|
|
||||||
const wchar_t* aHash) {
|
|
||||||
auto assocKeyPath = GetAssociationKeyPath(aExt);
|
|
||||||
if (!assocKeyPath) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const wchar_t* formatString = L"HKCU\\%s\\UserChoice";
|
|
||||||
int bufferSize = _scwprintf(formatString, assocKeyPath.get());
|
|
||||||
++bufferSize; // Extra character for terminating null
|
|
||||||
mozilla::UniquePtr<wchar_t[]> userChoiceKeyPath =
|
|
||||||
mozilla::MakeUnique<wchar_t[]>(bufferSize);
|
|
||||||
_snwprintf_s(userChoiceKeyPath.get(), bufferSize, _TRUNCATE, formatString,
|
|
||||||
assocKeyPath.get());
|
|
||||||
|
|
||||||
const wchar_t* deleteArgs[] = {
|
|
||||||
L"DELETE",
|
|
||||||
userChoiceKeyPath.get(),
|
|
||||||
L"/F",
|
|
||||||
};
|
|
||||||
if (!LaunchReg(mozilla::ArrayLength(deleteArgs), deleteArgs)) {
|
|
||||||
LOG_ERROR_MESSAGE(L"Failed to reg.exe DELETE; ignoring and continuing.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Like REG ADD [ROOT\]RegKey /V ValueName [/T DataType] [/S Separator] [/D
|
|
||||||
// Data] [/F] [/reg:32] [/reg:64]
|
|
||||||
|
|
||||||
const wchar_t* progIDArgs[] = {
|
|
||||||
L"ADD", userChoiceKeyPath.get(),
|
|
||||||
L"/F", L"/V",
|
|
||||||
L"ProgID", L"/T",
|
|
||||||
L"REG_SZ", L"/D",
|
|
||||||
aProgID,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!LaunchReg(mozilla::ArrayLength(progIDArgs), progIDArgs)) {
|
|
||||||
// LaunchReg will have logged an error message already.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const wchar_t* hashArgs[] = {
|
|
||||||
L"ADD", userChoiceKeyPath.get(),
|
|
||||||
L"/F", L"/V",
|
|
||||||
L"Hash", L"/T",
|
|
||||||
L"REG_SZ", L"/D",
|
|
||||||
aHash,
|
|
||||||
};
|
|
||||||
if (!LaunchReg(mozilla::ArrayLength(hashArgs), hashArgs)) {
|
|
||||||
// LaunchReg will have logged an error message already.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set an association with a UserChoice key
|
|
||||||
*
|
|
||||||
* Removes the old key, creates a new one with ProgID and Hash set to
|
|
||||||
* enable a new asociation.
|
|
||||||
*
|
|
||||||
* @param aExt File type or protocol to associate
|
|
||||||
* @param aSid Current user's string SID
|
|
||||||
* @param aProgID ProgID to use for the asociation
|
|
||||||
*
|
|
||||||
* @return true if successful, false on error.
|
|
||||||
*/
|
|
||||||
static bool SetUserChoice(const wchar_t* aExt, const wchar_t* aSid,
|
|
||||||
const wchar_t* aProgID) {
|
|
||||||
// This might be slow to query, so do it before generating timestamps and
|
|
||||||
// hashes.
|
|
||||||
UINT32 pfnLen = 0;
|
|
||||||
bool inMsix =
|
|
||||||
GetCurrentPackageFullName(&pfnLen, nullptr) != APPMODEL_ERROR_NO_PACKAGE;
|
|
||||||
|
|
||||||
SYSTEMTIME hashTimestamp;
|
|
||||||
::GetSystemTime(&hashTimestamp);
|
|
||||||
auto hash = GenerateUserChoiceHash(aExt, aSid, aProgID, hashTimestamp);
|
|
||||||
if (!hash) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The hash changes at the end of each minute, so check that the hash should
|
|
||||||
// be the same by the time we're done writing.
|
|
||||||
const ULONGLONG kWriteTimingThresholdMilliseconds = 1000;
|
|
||||||
// Generating the hash could have taken some time, so start from now.
|
|
||||||
SYSTEMTIME writeEndTimestamp;
|
|
||||||
::GetSystemTime(&writeEndTimestamp);
|
|
||||||
if (!AddMillisecondsToSystemTime(writeEndTimestamp,
|
|
||||||
kWriteTimingThresholdMilliseconds)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!CheckEqualMinutes(hashTimestamp, writeEndTimestamp)) {
|
|
||||||
LOG_ERROR_MESSAGE(
|
|
||||||
L"Hash is too close to expiration, sleeping until next hash.");
|
|
||||||
::Sleep(kWriteTimingThresholdMilliseconds * 2);
|
|
||||||
|
|
||||||
// For consistency, use the current time.
|
|
||||||
::GetSystemTime(&hashTimestamp);
|
|
||||||
hash = GenerateUserChoiceHash(aExt, aSid, aProgID, hashTimestamp);
|
|
||||||
if (!hash) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inMsix) {
|
|
||||||
// We're in an MSIX package, thus need to use reg.exe.
|
|
||||||
return SetUserChoiceCommand(aExt, aProgID, hash.get());
|
|
||||||
} else {
|
|
||||||
// We're outside of an MSIX package and can use the Win32 Registry API.
|
|
||||||
return SetUserChoiceRegistry(aExt, aProgID, std::move(hash));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool VerifyUserDefault(const wchar_t* aExt, const wchar_t* aProgID) {
|
static bool VerifyUserDefault(const wchar_t* aExt, const wchar_t* aProgID) {
|
||||||
RefPtr<IApplicationAssociationRegistration> pAAR;
|
RefPtr<IApplicationAssociationRegistration> pAAR;
|
||||||
HRESULT hr = ::CoCreateInstance(
|
HRESULT hr = ::CoCreateInstance(
|
||||||
@@ -343,19 +227,29 @@ static bool VerifyUserDefault(const wchar_t* aExt, const wchar_t* aProgID) {
|
|||||||
|
|
||||||
nsresult SetDefaultBrowserUserChoice(
|
nsresult SetDefaultBrowserUserChoice(
|
||||||
const wchar_t* aAumi, const nsTArray<nsString>& aExtraFileExtensions) {
|
const wchar_t* aAumi, const nsTArray<nsString>& aExtraFileExtensions) {
|
||||||
// Verify that the implementation of UserChoice hashing has not changed by
|
auto urlProgID = FormatProgID(L"FirefoxURL", aAumi);
|
||||||
// computing the current default hash and comparing with the existing value.
|
if (!CheckProgIDExists(urlProgID.get())) {
|
||||||
|
LOG_ERROR_MESSAGE(L"ProgID %s not found", urlProgID.get());
|
||||||
|
return NS_ERROR_WDBA_NO_PROGID;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto htmlProgID = FormatProgID(L"FirefoxHTML", aAumi);
|
||||||
|
if (!CheckProgIDExists(htmlProgID.get())) {
|
||||||
|
LOG_ERROR_MESSAGE(L"ProgID %s not found", htmlProgID.get());
|
||||||
|
return NS_ERROR_WDBA_NO_PROGID;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pdfProgID = FormatProgID(L"FirefoxPDF", aAumi);
|
||||||
|
if (!CheckProgIDExists(pdfProgID.get())) {
|
||||||
|
LOG_ERROR_MESSAGE(L"ProgID %s not found", pdfProgID.get());
|
||||||
|
return NS_ERROR_WDBA_NO_PROGID;
|
||||||
|
}
|
||||||
|
|
||||||
if (!CheckBrowserUserChoiceHashes()) {
|
if (!CheckBrowserUserChoiceHashes()) {
|
||||||
LOG_ERROR_MESSAGE(L"UserChoice Hash mismatch");
|
LOG_ERROR_MESSAGE(L"UserChoice Hash mismatch");
|
||||||
return NS_ERROR_WDBA_HASH_CHECK;
|
return NS_ERROR_WDBA_HASH_CHECK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsTArray<nsString> browserDefaults = {
|
|
||||||
u"https"_ns, u"FirefoxURL"_ns, u"http"_ns, u"FirefoxURL"_ns,
|
|
||||||
u".html"_ns, u"FirefoxHTML"_ns, u".htm"_ns, u"FirefoxHTML"_ns};
|
|
||||||
|
|
||||||
browserDefaults.AppendElements(aExtraFileExtensions);
|
|
||||||
|
|
||||||
if (!mozilla::IsWin10CreatorsUpdateOrLater()) {
|
if (!mozilla::IsWin10CreatorsUpdateOrLater()) {
|
||||||
LOG_ERROR_MESSAGE(L"UserChoice hash matched, but Windows build is too old");
|
LOG_ERROR_MESSAGE(L"UserChoice hash matched, but Windows build is too old");
|
||||||
return NS_ERROR_WDBA_BUILD;
|
return NS_ERROR_WDBA_BUILD;
|
||||||
@@ -366,16 +260,52 @@ nsresult SetDefaultBrowserUserChoice(
|
|||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv = SetDefaultExtensionHandlersUserChoiceImpl(aAumi, sid.get(),
|
bool ok = true;
|
||||||
browserDefaults);
|
bool defaultRejected = false;
|
||||||
if (!NS_SUCCEEDED(rv)) {
|
|
||||||
LOG_ERROR_MESSAGE(L"Failed setting default with %s", aAumi);
|
struct {
|
||||||
|
const wchar_t* ext;
|
||||||
|
const wchar_t* progID;
|
||||||
|
} associations[] = {{L"https", urlProgID.get()},
|
||||||
|
{L"http", urlProgID.get()},
|
||||||
|
{L".html", htmlProgID.get()},
|
||||||
|
{L".htm", htmlProgID.get()}};
|
||||||
|
for (size_t i = 0; i < mozilla::ArrayLength(associations); ++i) {
|
||||||
|
if (!SetUserChoice(associations[i].ext, sid.get(),
|
||||||
|
associations[i].progID)) {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
} else if (!VerifyUserDefault(associations[i].ext,
|
||||||
|
associations[i].progID)) {
|
||||||
|
defaultRejected = true;
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
nsresult rv = SetDefaultExtensionHandlersUserChoiceImpl(
|
||||||
|
aAumi, sid.get(), aExtraFileExtensions);
|
||||||
|
if (rv == NS_ERROR_WDBA_REJECTED) {
|
||||||
|
ok = false;
|
||||||
|
defaultRejected = true;
|
||||||
|
} else if (rv == NS_ERROR_FAILURE) {
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify shell to refresh icons
|
// Notify shell to refresh icons
|
||||||
::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
|
::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
|
||||||
|
|
||||||
return rv;
|
if (!ok) {
|
||||||
|
LOG_ERROR_MESSAGE(L"Failed setting default with %s", aAumi);
|
||||||
|
if (defaultRejected) {
|
||||||
|
return NS_ERROR_WDBA_REJECTED;
|
||||||
|
}
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult SetDefaultExtensionHandlersUserChoice(
|
nsresult SetDefaultExtensionHandlersUserChoice(
|
||||||
@@ -385,25 +315,35 @@ nsresult SetDefaultExtensionHandlersUserChoice(
|
|||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ok = true;
|
||||||
|
bool defaultRejected = false;
|
||||||
|
|
||||||
nsresult rv = SetDefaultExtensionHandlersUserChoiceImpl(aAumi, sid.get(),
|
nsresult rv = SetDefaultExtensionHandlersUserChoiceImpl(aAumi, sid.get(),
|
||||||
aFileExtensions);
|
aFileExtensions);
|
||||||
if (!NS_SUCCEEDED(rv)) {
|
if (rv == NS_ERROR_WDBA_REJECTED) {
|
||||||
LOG_ERROR_MESSAGE(L"Failed setting default with %s", aAumi);
|
ok = false;
|
||||||
|
defaultRejected = true;
|
||||||
|
} else if (rv == NS_ERROR_FAILURE) {
|
||||||
|
ok = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify shell to refresh icons
|
// Notify shell to refresh icons
|
||||||
::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
|
::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
|
||||||
|
|
||||||
return rv;
|
if (!ok) {
|
||||||
|
LOG_ERROR_MESSAGE(L"Failed setting default with %s", aAumi);
|
||||||
|
if (defaultRejected) {
|
||||||
|
return NS_ERROR_WDBA_REJECTED;
|
||||||
|
}
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult SetDefaultExtensionHandlersUserChoiceImpl(
|
nsresult SetDefaultExtensionHandlersUserChoiceImpl(
|
||||||
const wchar_t* aAumi, const wchar_t* const aSid,
|
const wchar_t* aAumi, const wchar_t* const aSid,
|
||||||
const nsTArray<nsString>& aFileExtensions) {
|
const nsTArray<nsString>& aFileExtensions) {
|
||||||
UINT32 pfnLen = 0;
|
|
||||||
bool inMsix =
|
|
||||||
GetCurrentPackageFullName(&pfnLen, nullptr) != APPMODEL_ERROR_NO_PACKAGE;
|
|
||||||
|
|
||||||
for (size_t i = 0; i + 1 < aFileExtensions.Length(); i += 2) {
|
for (size_t i = 0; i + 1 < aFileExtensions.Length(); i += 2) {
|
||||||
const wchar_t* extraFileExtension =
|
const wchar_t* extraFileExtension =
|
||||||
PromiseFlatString(aFileExtensions[i]).get();
|
PromiseFlatString(aFileExtensions[i]).get();
|
||||||
@@ -411,21 +351,7 @@ nsresult SetDefaultExtensionHandlersUserChoiceImpl(
|
|||||||
PromiseFlatString(aFileExtensions[i + 1]).get();
|
PromiseFlatString(aFileExtensions[i + 1]).get();
|
||||||
// Formatting the ProgID here prevents using this helper to target arbitrary
|
// Formatting the ProgID here prevents using this helper to target arbitrary
|
||||||
// ProgIDs.
|
// ProgIDs.
|
||||||
UniquePtr<wchar_t[]> extraProgID;
|
auto extraProgID = FormatProgID(extraProgIDRoot, aAumi);
|
||||||
if (inMsix) {
|
|
||||||
nsresult rv = GetMsixProgId(extraFileExtension, extraProgID);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
LOG_ERROR_MESSAGE(L"Failed to retrieve MSIX progID for %s",
|
|
||||||
extraFileExtension);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
extraProgID = FormatProgID(extraProgIDRoot, aAumi);
|
|
||||||
if (!CheckProgIDExists(extraProgID.get())) {
|
|
||||||
LOG_ERROR_MESSAGE(L"ProgID %s not found", extraProgID.get());
|
|
||||||
return NS_ERROR_WDBA_NO_PROGID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SetUserChoice(extraFileExtension, aSid, extraProgID.get())) {
|
if (!SetUserChoice(extraFileExtension, aSid, extraProgID.get())) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|||||||
Reference in New Issue
Block a user