mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 02:14:43 +00:00
Bug 1818418 - Use reg.exe
to implement MSIX 1-click set-to-default. r=mhughes
This approach uses `reg.exe` to delete and recreate the relevant HKCU registry key-value pairs. Differential Revision: https://phabricator.services.mozilla.com/D170717
This commit is contained in:
parent
eed4839cc0
commit
24147f6234
@ -20,13 +20,16 @@
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <appmodel.h> // for GetPackageFamilyName
|
||||
#include <sddl.h> // for ConvertSidToStringSidW
|
||||
#include <wincrypt.h> // for CryptoAPI base64
|
||||
#include <bcrypt.h> // for CNG MD5
|
||||
#include <winternl.h> // for NT_SUCCESS()
|
||||
|
||||
#include "ErrorList.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
|
||||
#include "WindowsUserChoice.h"
|
||||
@ -420,3 +423,53 @@ bool CheckProgIDExists(const wchar_t* aProgID) {
|
||||
::RegCloseKey(key);
|
||||
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,4 +100,17 @@ mozilla::UniquePtr<wchar_t[]> FormatProgID(const wchar_t* aProgIDBase,
|
||||
*/
|
||||
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__
|
||||
|
@ -300,10 +300,41 @@ nsWindowsShellService::CheckAllProgIDsExist(bool* aResult) {
|
||||
if (!mozilla::widget::WinTaskbar::GetAppUserModelID(aumid)) {
|
||||
return NS_OK;
|
||||
}
|
||||
*aResult =
|
||||
CheckProgIDExists(FormatProgID(L"FirefoxURL", aumid.get()).get()) &&
|
||||
CheckProgIDExists(FormatProgID(L"FirefoxHTML", aumid.get()).get()) &&
|
||||
CheckProgIDExists(FormatProgID(L"FirefoxPDF", aumid.get()).get());
|
||||
|
||||
if (widget::WinUtils::HasPackageIdentity()) {
|
||||
UniquePtr<wchar_t[]> extraProgID;
|
||||
nsresult rv;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -198,9 +198,8 @@ def get_branding(use_official, topsrcdir, build_app, finder, log=None):
|
||||
conf_vars = mozpath.join(topsrcdir, build_app, "confvars.sh")
|
||||
|
||||
def conf_vars_value(key):
|
||||
lines = open(conf_vars).readlines()
|
||||
lines = [line.strip() for line in open(conf_vars).readlines()]
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line and line[0] == "#":
|
||||
continue
|
||||
if key not in line:
|
||||
@ -833,7 +832,7 @@ def _sign_msix_win(output, force, log, verbose):
|
||||
else:
|
||||
thumbprint = None
|
||||
|
||||
if not thumbprint:
|
||||
if force or not thumbprint:
|
||||
thumbprint = (
|
||||
powershell(
|
||||
(
|
||||
|
@ -4,9 +4,11 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <windows.h>
|
||||
#include <appmodel.h>
|
||||
#include <shlobj.h> // for SHChangeNotify and IApplicationAssociationRegistration
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/CmdLineAndEnvUtils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
@ -86,50 +88,8 @@ static bool CheckEqualMinutes(SYSTEMTIME aSystemTime1,
|
||||
(fileTime1.dwHighDateTime == fileTime2.dwHighDateTime);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
static bool SetUserChoiceRegistry(const wchar_t* aExt, const wchar_t* aProgID,
|
||||
mozilla::UniquePtr<wchar_t[]> aHash) {
|
||||
auto assocKeyPath = GetAssociationKeyPath(aExt);
|
||||
if (!assocKeyPath) {
|
||||
return false;
|
||||
@ -174,9 +134,9 @@ static bool SetUserChoice(const wchar_t* aExt, const wchar_t* aSid,
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD hashByteCount = (::lstrlenW(hash.get()) + 1) * sizeof(wchar_t);
|
||||
DWORD hashByteCount = (::lstrlenW(aHash.get()) + 1) * sizeof(wchar_t);
|
||||
ls = ::RegSetValueExW(userChoiceKey.get(), L"Hash", 0, REG_SZ,
|
||||
reinterpret_cast<const unsigned char*>(hash.get()),
|
||||
reinterpret_cast<const unsigned char*>(aHash.get()),
|
||||
hashByteCount);
|
||||
if (ls != ERROR_SUCCESS) {
|
||||
LOG_ERROR(HRESULT_FROM_WIN32(ls));
|
||||
@ -186,6 +146,162 @@ static bool SetUserChoice(const wchar_t* aExt, const wchar_t* aSid,
|
||||
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) {
|
||||
RefPtr<IApplicationAssociationRegistration> pAAR;
|
||||
HRESULT hr = ::CoCreateInstance(
|
||||
@ -227,29 +343,19 @@ static bool VerifyUserDefault(const wchar_t* aExt, const wchar_t* aProgID) {
|
||||
|
||||
nsresult SetDefaultBrowserUserChoice(
|
||||
const wchar_t* aAumi, const nsTArray<nsString>& aExtraFileExtensions) {
|
||||
auto urlProgID = FormatProgID(L"FirefoxURL", aAumi);
|
||||
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;
|
||||
}
|
||||
|
||||
// Verify that the implementation of UserChoice hashing has not changed by
|
||||
// computing the current default hash and comparing with the existing value.
|
||||
if (!CheckBrowserUserChoiceHashes()) {
|
||||
LOG_ERROR_MESSAGE(L"UserChoice Hash mismatch");
|
||||
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()) {
|
||||
LOG_ERROR_MESSAGE(L"UserChoice hash matched, but Windows build is too old");
|
||||
return NS_ERROR_WDBA_BUILD;
|
||||
@ -260,52 +366,16 @@ nsresult SetDefaultBrowserUserChoice(
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
bool defaultRejected = false;
|
||||
|
||||
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;
|
||||
}
|
||||
nsresult rv = SetDefaultExtensionHandlersUserChoiceImpl(aAumi, sid.get(),
|
||||
browserDefaults);
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
LOG_ERROR_MESSAGE(L"Failed setting default with %s", aAumi);
|
||||
}
|
||||
|
||||
// Notify shell to refresh icons
|
||||
::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
|
||||
|
||||
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;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult SetDefaultExtensionHandlersUserChoice(
|
||||
@ -315,35 +385,25 @@ nsresult SetDefaultExtensionHandlersUserChoice(
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
bool defaultRejected = false;
|
||||
|
||||
nsresult rv = SetDefaultExtensionHandlersUserChoiceImpl(aAumi, sid.get(),
|
||||
aFileExtensions);
|
||||
if (rv == NS_ERROR_WDBA_REJECTED) {
|
||||
ok = false;
|
||||
defaultRejected = true;
|
||||
} else if (rv == NS_ERROR_FAILURE) {
|
||||
ok = false;
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
LOG_ERROR_MESSAGE(L"Failed setting default with %s", aAumi);
|
||||
}
|
||||
|
||||
// Notify shell to refresh icons
|
||||
::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
|
||||
|
||||
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;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult SetDefaultExtensionHandlersUserChoiceImpl(
|
||||
const wchar_t* aAumi, const wchar_t* const aSid,
|
||||
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) {
|
||||
const wchar_t* extraFileExtension =
|
||||
PromiseFlatString(aFileExtensions[i]).get();
|
||||
@ -351,7 +411,21 @@ nsresult SetDefaultExtensionHandlersUserChoiceImpl(
|
||||
PromiseFlatString(aFileExtensions[i + 1]).get();
|
||||
// Formatting the ProgID here prevents using this helper to target arbitrary
|
||||
// ProgIDs.
|
||||
auto extraProgID = FormatProgID(extraProgIDRoot, aAumi);
|
||||
UniquePtr<wchar_t[]> extraProgID;
|
||||
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())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
|
Loading…
Reference in New Issue
Block a user