Skip to content

Commit cfde427

Browse files
committed
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
1 parent 86aef92 commit cfde427

File tree

5 files changed

+300
-130
lines changed

5 files changed

+300
-130
lines changed

browser/components/shell/WindowsUserChoice.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@
2020
*/
2121

2222
#include <windows.h>
23+
#include <appmodel.h> // for GetPackageFamilyName
2324
#include <sddl.h> // for ConvertSidToStringSidW
2425
#include <wincrypt.h> // for CryptoAPI base64
2526
#include <bcrypt.h> // for CNG MD5
2627
#include <winternl.h> // for NT_SUCCESS()
2728

29+
#include "nsDebug.h"
2830
#include "mozilla/ArrayUtils.h"
2931
#include "mozilla/UniquePtr.h"
3032
#include "nsWindowsHelpers.h"
@@ -420,3 +422,53 @@ bool CheckProgIDExists(const wchar_t* aProgID) {
420422
::RegCloseKey(key);
421423
return true;
422424
}
425+
426+
nsresult GetMsixProgId(const wchar_t* assoc, UniquePtr<wchar_t[]>& aProgId) {
427+
// Retrieve the registry path to the package from registry path:
428+
// clang-format off
429+
// HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages\[Package Full Name]\App\Capabilities\[FileAssociations | URLAssociations]\[File | URL]
430+
// clang-format on
431+
432+
UINT32 pfnLen = 0;
433+
LONG rv = GetCurrentPackageFullName(&pfnLen, nullptr);
434+
NS_ENSURE_TRUE(rv != APPMODEL_ERROR_NO_PACKAGE, NS_ERROR_FAILURE);
435+
436+
auto pfn = mozilla::MakeUnique<wchar_t[]>(pfnLen);
437+
rv = GetCurrentPackageFullName(&pfnLen, pfn.get());
438+
NS_ENSURE_TRUE(rv == ERROR_SUCCESS, NS_ERROR_FAILURE);
439+
440+
const wchar_t* assocSuffix;
441+
if (assoc[0] == L'.') {
442+
// File association.
443+
assocSuffix = LR"(App\Capabilities\FileAssociations)";
444+
} else {
445+
// URL association.
446+
assocSuffix = LR"(App\Capabilities\URLAssociations)";
447+
}
448+
449+
const wchar_t* assocPathFmt =
450+
LR"(Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages\%s\%s)";
451+
int assocPathLen = _scwprintf(assocPathFmt, pfn.get(), assocSuffix);
452+
assocPathLen += 1; // _scwprintf does not include the terminator
453+
454+
auto assocPath = MakeUnique<wchar_t[]>(assocPathLen);
455+
_snwprintf_s(assocPath.get(), assocPathLen, _TRUNCATE, assocPathFmt,
456+
pfn.get(), assocSuffix);
457+
458+
LSTATUS ls;
459+
460+
// Retrieve the package association's ProgID, always in the form `AppX[32 hash
461+
// characters]`.
462+
const size_t appxProgIdLen = 37;
463+
auto progId = MakeUnique<wchar_t[]>(appxProgIdLen);
464+
DWORD progIdLen = appxProgIdLen * sizeof(wchar_t);
465+
ls = ::RegGetValueW(HKEY_CLASSES_ROOT, assocPath.get(), assoc, RRF_RT_REG_SZ,
466+
nullptr, (LPBYTE)progId.get(), &progIdLen);
467+
if (ls != ERROR_SUCCESS) {
468+
return NS_ERROR_WDBA_NO_PROGID;
469+
}
470+
471+
aProgId.swap(progId);
472+
473+
return NS_OK;
474+
}

browser/components/shell/WindowsUserChoice.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <windows.h>
1010

11+
#include "ErrorList.h" // for nsresult
1112
#include "mozilla/UniquePtr.h"
1213

1314
/*
@@ -100,4 +101,17 @@ mozilla::UniquePtr<wchar_t[]> FormatProgID(const wchar_t* aProgIDBase,
100101
*/
101102
bool CheckProgIDExists(const wchar_t* aProgID);
102103

104+
/*
105+
* Get the ProgID registered by Windows for the given association.
106+
*
107+
* The MSIX `AppManifest.xml` declares supported protocols and file
108+
* type associations. Upon installation, Windows generates
109+
* corresponding ProgIDs for them, of the form `AppX*`. This function
110+
* retrieves those generated ProgIDs (from the Windows registry).
111+
*
112+
* @return ProgID.
113+
*/
114+
nsresult GetMsixProgId(const wchar_t* assoc,
115+
mozilla::UniquePtr<wchar_t[]>& aProgId);
116+
103117
#endif // SHELL_WINDOWSUSERCHOICE_H__

browser/components/shell/nsWindowsShellService.cpp

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,41 @@ nsWindowsShellService::CheckAllProgIDsExist(bool* aResult) {
300300
if (!mozilla::widget::WinTaskbar::GetAppUserModelID(aumid)) {
301301
return NS_OK;
302302
}
303-
*aResult =
304-
CheckProgIDExists(FormatProgID(L"FirefoxURL", aumid.get()).get()) &&
305-
CheckProgIDExists(FormatProgID(L"FirefoxHTML", aumid.get()).get()) &&
306-
CheckProgIDExists(FormatProgID(L"FirefoxPDF", aumid.get()).get());
303+
304+
if (widget::WinUtils::HasPackageIdentity()) {
305+
UniquePtr<wchar_t[]> extraProgID;
306+
nsresult rv;
307+
bool result = true;
308+
309+
// "FirefoxURL".
310+
rv = GetMsixProgId(L"https", extraProgID);
311+
if (NS_WARN_IF(NS_FAILED(rv))) {
312+
return rv;
313+
}
314+
result = result && CheckProgIDExists(extraProgID.get());
315+
316+
// "FirefoxHTML".
317+
rv = GetMsixProgId(L".htm", extraProgID);
318+
if (NS_WARN_IF(NS_FAILED(rv))) {
319+
return rv;
320+
}
321+
result = result && CheckProgIDExists(extraProgID.get());
322+
323+
// "FirefoxPDF".
324+
rv = GetMsixProgId(L".pdf", extraProgID);
325+
if (NS_WARN_IF(NS_FAILED(rv))) {
326+
return rv;
327+
}
328+
result = result && CheckProgIDExists(extraProgID.get());
329+
330+
*aResult = result;
331+
} else {
332+
*aResult =
333+
CheckProgIDExists(FormatProgID(L"FirefoxURL", aumid.get()).get()) &&
334+
CheckProgIDExists(FormatProgID(L"FirefoxHTML", aumid.get()).get()) &&
335+
CheckProgIDExists(FormatProgID(L"FirefoxPDF", aumid.get()).get());
336+
}
337+
307338
return NS_OK;
308339
}
309340

python/mozbuild/mozbuild/repackaging/msix.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,8 @@ def get_branding(use_official, topsrcdir, build_app, finder, log=None):
198198
conf_vars = mozpath.join(topsrcdir, build_app, "confvars.sh")
199199

200200
def conf_vars_value(key):
201-
lines = open(conf_vars).readlines()
201+
lines = [line.strip() for line in open(conf_vars).readlines()]
202202
for line in lines:
203-
line = line.strip()
204203
if line and line[0] == "#":
205204
continue
206205
if key not in line:
@@ -833,7 +832,7 @@ def powershell(argstring, check=True):
833832
else:
834833
thumbprint = None
835834

836-
if not thumbprint:
835+
if force or not thumbprint:
837836
thumbprint = (
838837
powershell(
839838
(

0 commit comments

Comments
 (0)