1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 #include "LauncherRegistryInfo.h"
9 #include "commonupdatedir.h"
10 #include "mozilla/ArrayUtils.h"
11 #include "mozilla/Assertions.h"
12 #include "mozilla/NativeNt.h"
13 #include "mozilla/UniquePtr.h"
18 #include <type_traits>
20 #define EXPAND_STRING_MACRO2(t) t
21 #define EXPAND_STRING_MACRO(t) EXPAND_STRING_MACRO2(t)
23 // This function is copied from Chromium base/time/time_win.cc
24 // Returns the current value of the performance counter.
25 static uint64_t QPCNowRaw() {
26 LARGE_INTEGER perf_counter_now
= {};
27 // According to the MSDN documentation for QueryPerformanceCounter(), this
28 // will never fail on systems that run XP or later.
29 // https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter
30 ::QueryPerformanceCounter(&perf_counter_now
);
31 return perf_counter_now
.QuadPart
;
34 static mozilla::LauncherResult
<DWORD
> GetCurrentImageTimestamp() {
35 mozilla::nt::PEHeaders
headers(::GetModuleHandleW(nullptr));
37 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT
);
41 if (!headers
.GetTimeStamp(timestamp
)) {
42 return LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_DATA
);
49 static mozilla::LauncherResult
<mozilla::Maybe
<T
>> ReadRegistryValueData(
50 const nsAutoRegKey
& key
, const std::wstring
& name
, DWORD expectedType
) {
51 static_assert(std::is_trivial_v
<T
> && std::is_standard_layout_v
<T
>,
52 "Registry value type must be primitive.");
54 DWORD dataLen
= sizeof(data
);
56 LSTATUS status
= ::RegQueryValueExW(key
.get(), name
.c_str(), nullptr, &type
,
57 reinterpret_cast<PBYTE
>(&data
), &dataLen
);
58 if (status
== ERROR_FILE_NOT_FOUND
) {
59 return mozilla::Maybe
<T
>();
62 if (status
!= ERROR_SUCCESS
) {
63 return LAUNCHER_ERROR_FROM_WIN32(status
);
66 if (type
!= expectedType
) {
67 return LAUNCHER_ERROR_FROM_WIN32(ERROR_DATATYPE_MISMATCH
);
70 return mozilla::Some(data
);
73 static mozilla::LauncherResult
<mozilla::UniquePtr
<wchar_t[]>>
74 ReadRegistryValueString(const nsAutoRegKey
& aKey
, const std::wstring
& aName
) {
75 mozilla::UniquePtr
<wchar_t[]> buf
;
77 LSTATUS status
= ::RegGetValueW(aKey
.get(), nullptr, aName
.c_str(),
78 RRF_RT_REG_SZ
, nullptr, nullptr, &dataLen
);
79 if (status
== ERROR_FILE_NOT_FOUND
) {
83 if (status
!= ERROR_SUCCESS
) {
84 return LAUNCHER_ERROR_FROM_WIN32(status
);
87 buf
= mozilla::MakeUnique
<wchar_t[]>(dataLen
/ sizeof(wchar_t));
89 status
= ::RegGetValueW(aKey
.get(), nullptr, aName
.c_str(), RRF_RT_REG_SZ
,
90 nullptr, buf
.get(), &dataLen
);
91 if (status
!= ERROR_SUCCESS
) {
92 return LAUNCHER_ERROR_FROM_WIN32(status
);
98 static mozilla::LauncherVoidResult
WriteRegistryValueString(
99 const nsAutoRegKey
& aKey
, const std::wstring
& aName
,
100 const std::wstring
& aValue
) {
101 DWORD dataBytes
= (aValue
.size() + 1) * sizeof(wchar_t);
102 LSTATUS status
= ::RegSetValueExW(
103 aKey
.get(), aName
.c_str(), /*Reserved*/ 0, REG_SZ
,
104 reinterpret_cast<const BYTE
*>(aValue
.c_str()), dataBytes
);
105 if (status
!= ERROR_SUCCESS
) {
106 return LAUNCHER_ERROR_FROM_WIN32(status
);
109 return mozilla::Ok();
112 template <typename T
>
113 static mozilla::LauncherVoidResult
WriteRegistryValueData(
114 const nsAutoRegKey
& key
, const std::wstring
& name
, DWORD type
, T data
) {
115 static_assert(std::is_trivial_v
<T
> && std::is_standard_layout_v
<T
>,
116 "Registry value type must be primitive.");
118 ::RegSetValueExW(key
.get(), name
.c_str(), 0, type
,
119 reinterpret_cast<PBYTE
>(&data
), sizeof(data
));
120 if (status
!= ERROR_SUCCESS
) {
121 return LAUNCHER_ERROR_FROM_WIN32(status
);
124 return mozilla::Ok();
127 static mozilla::LauncherResult
<bool> DeleteRegistryValueData(
128 const nsAutoRegKey
& key
, const std::wstring
& name
) {
129 LSTATUS status
= ::RegDeleteValueW(key
, name
.c_str());
130 if (status
== ERROR_FILE_NOT_FOUND
) {
134 if (status
!= ERROR_SUCCESS
) {
135 return LAUNCHER_ERROR_FROM_WIN32(status
);
143 const wchar_t LauncherRegistryInfo::kLauncherSubKeyPath
[] =
144 L
"SOFTWARE\\" EXPAND_STRING_MACRO(MOZ_APP_VENDOR
) L
"\\" EXPAND_STRING_MACRO(
145 MOZ_APP_BASENAME
) L
"\\Launcher";
146 const wchar_t LauncherRegistryInfo::kLauncherSuffix
[] = L
"|Launcher";
147 const wchar_t LauncherRegistryInfo::kBrowserSuffix
[] = L
"|Browser";
148 const wchar_t LauncherRegistryInfo::kImageTimestampSuffix
[] = L
"|Image";
149 const wchar_t LauncherRegistryInfo::kTelemetrySuffix
[] = L
"|Telemetry";
150 const wchar_t LauncherRegistryInfo::kBlocklistSuffix
[] = L
"|Blocklist";
152 bool LauncherRegistryInfo::sAllowCommit
= true;
154 LauncherResult
<LauncherRegistryInfo::Disposition
> LauncherRegistryInfo::Open() {
156 return Disposition::OpenedExisting
;
161 LSTATUS result
= ::RegCreateKeyExW(
162 HKEY_CURRENT_USER
, kLauncherSubKeyPath
, 0, nullptr,
163 REG_OPTION_NON_VOLATILE
, KEY_ALL_ACCESS
, nullptr, &rawKey
, &disposition
);
164 if (result
!= ERROR_SUCCESS
) {
165 return LAUNCHER_ERROR_FROM_WIN32(result
);
170 switch (disposition
) {
171 case REG_CREATED_NEW_KEY
:
172 return Disposition::CreatedNew
;
173 case REG_OPENED_EXISTING_KEY
:
174 return Disposition::OpenedExisting
;
179 MOZ_ASSERT_UNREACHABLE("Invalid disposition from RegCreateKeyExW");
180 return LAUNCHER_ERROR_GENERIC();
183 LauncherVoidResult
LauncherRegistryInfo::ReflectPrefToRegistry(
184 const bool aEnable
) {
185 LauncherResult
<EnabledState
> curEnabledState
= IsEnabled();
186 if (curEnabledState
.isErr()) {
187 return curEnabledState
.propagateErr();
190 bool isCurrentlyEnabled
=
191 curEnabledState
.inspect() != EnabledState::ForceDisabled
;
192 if (isCurrentlyEnabled
== aEnable
) {
193 // Don't reflect to the registry unless the new enabled state is actually
194 // changing with respect to the current enabled state.
198 // Always delete the launcher timestamp
199 LauncherResult
<bool> clearedLauncherTimestamp
= ClearLauncherStartTimestamp();
200 MOZ_ASSERT(clearedLauncherTimestamp
.isOk());
201 if (clearedLauncherTimestamp
.isErr()) {
202 return clearedLauncherTimestamp
.propagateErr();
205 // Allow commit when we enable the launcher, otherwise block.
206 sAllowCommit
= aEnable
;
209 // Set the browser timestamp to 0 to indicate force-disabled
210 return WriteBrowserStartTimestamp(0ULL);
213 // Otherwise we delete the browser timestamp to start over fresh
214 LauncherResult
<bool> clearedBrowserTimestamp
= ClearBrowserStartTimestamp();
215 MOZ_ASSERT(clearedBrowserTimestamp
.isOk());
216 if (clearedBrowserTimestamp
.isErr()) {
217 return clearedBrowserTimestamp
.propagateErr();
223 LauncherVoidResult
LauncherRegistryInfo::ReflectTelemetryPrefToRegistry(
224 const bool aEnable
) {
225 LauncherResult
<Disposition
> disposition
= Open();
226 if (disposition
.isErr()) {
227 return disposition
.propagateErr();
230 return WriteRegistryValueData(mRegKey
, ResolveTelemetryValueName(), REG_DWORD
,
231 aEnable
? 1UL : 0UL);
234 LauncherResult
<LauncherRegistryInfo::ProcessType
> LauncherRegistryInfo::Check(
235 const ProcessType aDesiredType
, const CheckOption aOption
) {
236 LauncherResult
<Disposition
> disposition
= Open();
237 if (disposition
.isErr()) {
238 return disposition
.propagateErr();
241 LauncherResult
<DWORD
> ourImageTimestamp
= GetCurrentImageTimestamp();
242 if (ourImageTimestamp
.isErr()) {
243 return ourImageTimestamp
.propagateErr();
246 LauncherResult
<Maybe
<DWORD
>> savedImageTimestamp
= GetSavedImageTimestamp();
247 if (savedImageTimestamp
.isErr()) {
248 return savedImageTimestamp
.propagateErr();
251 // If we don't have a saved timestamp, or we do but it doesn't match with
252 // our current timestamp, clear previous values unless we're force-disabled.
253 if (savedImageTimestamp
.inspect().isNothing() ||
254 savedImageTimestamp
.inspect().value() != ourImageTimestamp
.inspect()) {
255 LauncherVoidResult clearResult
= ClearStartTimestamps();
256 if (clearResult
.isErr()) {
257 return clearResult
.propagateErr();
260 LauncherVoidResult writeResult
=
261 WriteImageTimestamp(ourImageTimestamp
.inspect());
262 if (writeResult
.isErr()) {
263 return writeResult
.propagateErr();
267 // If we're going to be running as the browser process, or there is no
268 // existing values to check, just write our timestamp and return.
269 if (aDesiredType
== ProcessType::Browser
) {
270 mBrowserTimestampToWrite
= Some(QPCNowRaw());
271 return ProcessType::Browser
;
274 if (disposition
.inspect() == Disposition::CreatedNew
) {
275 mLauncherTimestampToWrite
= Some(QPCNowRaw());
276 return ProcessType::Launcher
;
279 if (disposition
.inspect() != Disposition::OpenedExisting
) {
280 MOZ_ASSERT_UNREACHABLE("Invalid |disposition|");
281 return LAUNCHER_ERROR_GENERIC();
284 LauncherResult
<Maybe
<uint64_t>> lastLauncherTimestampResult
=
285 GetLauncherStartTimestamp();
286 if (lastLauncherTimestampResult
.isErr()) {
287 return lastLauncherTimestampResult
.propagateErr();
290 LauncherResult
<Maybe
<uint64_t>> lastBrowserTimestampResult
=
291 GetBrowserStartTimestamp();
292 if (lastBrowserTimestampResult
.isErr()) {
293 return lastBrowserTimestampResult
.propagateErr();
296 const Maybe
<uint64_t>& lastLauncherTimestamp
=
297 lastLauncherTimestampResult
.inspect();
298 const Maybe
<uint64_t>& lastBrowserTimestamp
=
299 lastBrowserTimestampResult
.inspect();
301 ProcessType typeToRunAs
= aDesiredType
;
303 if (lastLauncherTimestamp
.isSome() != lastBrowserTimestamp
.isSome()) {
304 // If we have a launcher timestamp but no browser timestamp (or vice versa),
305 // that's bad because it is indicating that the browser can't run with
306 // the launcher process.
307 typeToRunAs
= ProcessType::Browser
;
308 } else if (lastLauncherTimestamp
.isSome()) {
309 // if we have both timestamps, we want to ensure that the launcher timestamp
310 // is earlier than the browser timestamp.
311 if (aDesiredType
== ProcessType::Launcher
) {
312 bool areTimestampsOk
=
313 lastLauncherTimestamp
.value() < lastBrowserTimestamp
.value();
314 if (!areTimestampsOk
) {
315 typeToRunAs
= ProcessType::Browser
;
319 // If we have neither timestamp, then we should try running as suggested
320 // by |aDesiredType|.
321 // We shouldn't really have this scenario unless we're going to be running
322 // as the launcher process.
323 MOZ_ASSERT(typeToRunAs
== ProcessType::Launcher
);
324 // No change to typeToRunAs
327 // Debugging setting that forces the desired type regardless of the various
328 // tests that have been performed.
329 if (aOption
== CheckOption::Force
) {
330 typeToRunAs
= aDesiredType
;
333 switch (typeToRunAs
) {
334 case ProcessType::Browser
:
335 if (aDesiredType
!= typeToRunAs
) {
336 // We were hoping to run as the launcher, but some failure has caused
337 // us to run as the browser. Set the browser timestamp to zero as an
339 mBrowserTimestampToWrite
= Some(0ULL);
341 mBrowserTimestampToWrite
= Some(QPCNowRaw());
344 case ProcessType::Launcher
:
345 mLauncherTimestampToWrite
= Some(QPCNowRaw());
348 MOZ_ASSERT_UNREACHABLE("Invalid |typeToRunAs|");
349 return LAUNCHER_ERROR_GENERIC();
355 LauncherVoidResult
LauncherRegistryInfo::DisableDueToFailure() {
356 LauncherResult
<Disposition
> disposition
= Open();
357 if (disposition
.isErr()) {
358 return disposition
.propagateErr();
360 LauncherVoidResult result
= WriteBrowserStartTimestamp(0ULL);
362 // Block commit when we disable the launcher. It could be allowed
363 // when the image timestamp is updated.
364 sAllowCommit
= false;
369 LauncherVoidResult
LauncherRegistryInfo::Commit() {
375 LauncherResult
<Disposition
> disposition
= Open();
376 if (disposition
.isErr()) {
377 return disposition
.propagateErr();
380 if (mLauncherTimestampToWrite
.isSome()) {
381 LauncherVoidResult writeResult
=
382 WriteLauncherStartTimestamp(mLauncherTimestampToWrite
.value());
383 if (writeResult
.isErr()) {
384 return writeResult
.propagateErr();
386 mLauncherTimestampToWrite
= Nothing();
389 if (mBrowserTimestampToWrite
.isSome()) {
390 LauncherVoidResult writeResult
=
391 WriteBrowserStartTimestamp(mBrowserTimestampToWrite
.value());
392 if (writeResult
.isErr()) {
393 return writeResult
.propagateErr();
395 mBrowserTimestampToWrite
= Nothing();
401 void LauncherRegistryInfo::Abort() {
402 mLauncherTimestampToWrite
= mBrowserTimestampToWrite
= Nothing();
405 LauncherRegistryInfo::EnabledState
LauncherRegistryInfo::GetEnabledState(
406 const Maybe
<uint64_t>& aLauncherTs
, const Maybe
<uint64_t>& aBrowserTs
) {
407 if (aBrowserTs
.isSome()) {
408 if (aLauncherTs
.isSome()) {
409 if (aLauncherTs
.value() < aBrowserTs
.value()) {
410 // Both timestamps exist and the browser's timestamp is later.
411 return EnabledState::Enabled
;
413 } else if (aBrowserTs
.value() == 0ULL) {
414 // Only browser's timestamp exists and its value is 0.
415 return EnabledState::ForceDisabled
;
417 } else if (aLauncherTs
.isNothing()) {
418 // Neither timestamps exist.
419 return EnabledState::Enabled
;
422 // Everything else is FailDisabled.
423 return EnabledState::FailDisabled
;
426 LauncherResult
<LauncherRegistryInfo::EnabledState
>
427 LauncherRegistryInfo::IsEnabled() {
428 LauncherResult
<Disposition
> disposition
= Open();
429 if (disposition
.isErr()) {
430 return disposition
.propagateErr();
433 LauncherResult
<Maybe
<uint64_t>> lastLauncherTimestamp
=
434 GetLauncherStartTimestamp();
435 if (lastLauncherTimestamp
.isErr()) {
436 return lastLauncherTimestamp
.propagateErr();
439 LauncherResult
<Maybe
<uint64_t>> lastBrowserTimestamp
=
440 GetBrowserStartTimestamp();
441 if (lastBrowserTimestamp
.isErr()) {
442 return lastBrowserTimestamp
.propagateErr();
445 return GetEnabledState(lastLauncherTimestamp
.inspect(),
446 lastBrowserTimestamp
.inspect());
449 LauncherResult
<bool> LauncherRegistryInfo::IsTelemetryEnabled() {
450 LauncherResult
<Disposition
> disposition
= Open();
451 if (disposition
.isErr()) {
452 return disposition
.propagateErr();
455 LauncherResult
<Maybe
<DWORD
>> result
= ReadRegistryValueData
<DWORD
>(
456 mRegKey
, ResolveTelemetryValueName(), REG_DWORD
);
457 if (result
.isErr()) {
458 return result
.propagateErr();
461 if (result
.inspect().isNothing()) {
462 // Value does not exist, treat as false
466 return result
.inspect().value() != 0;
469 const std::wstring
& LauncherRegistryInfo::ResolveLauncherValueName() {
470 if (mLauncherValueName
.empty()) {
471 mLauncherValueName
.assign(mBinPath
);
472 mLauncherValueName
.append(kLauncherSuffix
,
473 ArrayLength(kLauncherSuffix
) - 1);
476 return mLauncherValueName
;
479 const std::wstring
& LauncherRegistryInfo::ResolveBrowserValueName() {
480 if (mBrowserValueName
.empty()) {
481 mBrowserValueName
.assign(mBinPath
);
482 mBrowserValueName
.append(kBrowserSuffix
, ArrayLength(kBrowserSuffix
) - 1);
485 return mBrowserValueName
;
488 const std::wstring
& LauncherRegistryInfo::ResolveImageTimestampValueName() {
489 if (mImageValueName
.empty()) {
490 mImageValueName
.assign(mBinPath
);
491 mImageValueName
.append(kImageTimestampSuffix
,
492 ArrayLength(kImageTimestampSuffix
) - 1);
495 return mImageValueName
;
498 const std::wstring
& LauncherRegistryInfo::ResolveTelemetryValueName() {
499 if (mTelemetryValueName
.empty()) {
500 mTelemetryValueName
.assign(mBinPath
);
501 mTelemetryValueName
.append(kTelemetrySuffix
,
502 ArrayLength(kTelemetrySuffix
) - 1);
505 return mTelemetryValueName
;
508 const std::wstring
& LauncherRegistryInfo::ResolveBlocklistValueName() {
509 if (mBlocklistValueName
.empty()) {
510 mBlocklistValueName
.assign(mBinPath
);
511 mBlocklistValueName
.append(kBlocklistSuffix
,
512 ArrayLength(kBlocklistSuffix
) - 1);
515 return mBlocklistValueName
;
518 LauncherVoidResult
LauncherRegistryInfo::WriteLauncherStartTimestamp(
520 return WriteRegistryValueData(mRegKey
, ResolveLauncherValueName(), REG_QWORD
,
524 LauncherVoidResult
LauncherRegistryInfo::WriteBrowserStartTimestamp(
526 return WriteRegistryValueData(mRegKey
, ResolveBrowserValueName(), REG_QWORD
,
530 LauncherVoidResult
LauncherRegistryInfo::WriteImageTimestamp(DWORD aTimestamp
) {
531 return WriteRegistryValueData(mRegKey
, ResolveImageTimestampValueName(),
532 REG_DWORD
, aTimestamp
);
535 LauncherResult
<bool> LauncherRegistryInfo::ClearLauncherStartTimestamp() {
536 return DeleteRegistryValueData(mRegKey
, ResolveLauncherValueName());
539 LauncherResult
<bool> LauncherRegistryInfo::ClearBrowserStartTimestamp() {
540 return DeleteRegistryValueData(mRegKey
, ResolveBrowserValueName());
543 LauncherVoidResult
LauncherRegistryInfo::ClearStartTimestamps() {
544 LauncherResult
<EnabledState
> enabled
= IsEnabled();
545 if (enabled
.isOk() && enabled
.inspect() == EnabledState::ForceDisabled
) {
546 // We don't clear anything when we're force disabled - we need to maintain
547 // the current registry state in this case.
551 LauncherResult
<bool> clearedLauncherTimestamp
= ClearLauncherStartTimestamp();
552 if (clearedLauncherTimestamp
.isErr()) {
553 return clearedLauncherTimestamp
.propagateErr();
556 LauncherResult
<bool> clearedBrowserTimestamp
= ClearBrowserStartTimestamp();
557 if (clearedBrowserTimestamp
.isErr()) {
558 return clearedBrowserTimestamp
.propagateErr();
561 // Reset both timestamps to align with registry deletion
562 mLauncherTimestampToWrite
= mBrowserTimestampToWrite
= Nothing();
564 // Disablement is gone. Let's allow commit.
570 LauncherResult
<Maybe
<DWORD
>> LauncherRegistryInfo::GetSavedImageTimestamp() {
571 return ReadRegistryValueData
<DWORD
>(mRegKey
, ResolveImageTimestampValueName(),
575 LauncherResult
<Maybe
<uint64_t>>
576 LauncherRegistryInfo::GetLauncherStartTimestamp() {
577 return ReadRegistryValueData
<uint64_t>(mRegKey
, ResolveLauncherValueName(),
581 LauncherResult
<Maybe
<uint64_t>>
582 LauncherRegistryInfo::GetBrowserStartTimestamp() {
583 return ReadRegistryValueData
<uint64_t>(mRegKey
, ResolveBrowserValueName(),
587 LauncherResult
<std::wstring
>
588 LauncherRegistryInfo::BuildDefaultBlocklistFilename() {
589 // These flags are chosen to avoid I/O, see bug 1363398.
591 KF_FLAG_SIMPLE_IDLIST
| KF_FLAG_DONT_VERIFY
| KF_FLAG_NO_ALIAS
;
592 PWSTR rawPath
= nullptr;
594 ::SHGetKnownFolderPath(FOLDERID_RoamingAppData
, flags
, nullptr, &rawPath
);
596 ::CoTaskMemFree(rawPath
);
597 return LAUNCHER_ERROR_FROM_HRESULT(hr
);
600 UniquePtr
<wchar_t, CoTaskMemFreeDeleter
> appDataPath(rawPath
);
601 std::wstring
defaultBlocklistPath(appDataPath
.get());
603 UniquePtr
<NS_tchar
[]> hash
;
604 std::wstring binPathLower
;
605 binPathLower
.reserve(mBinPath
.size());
606 std::transform(mBinPath
.begin(), mBinPath
.end(),
607 std::back_inserter(binPathLower
), std::towlower
);
608 if (!::GetInstallHash(reinterpret_cast<const char16_t
*>(binPathLower
.c_str()),
610 return LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_DATA
);
613 defaultBlocklistPath
.append(
614 L
"\\" MOZ_APP_VENDOR L
"\\" MOZ_APP_BASENAME L
"\\blocklist-");
615 defaultBlocklistPath
.append(hash
.get());
617 return defaultBlocklistPath
;
620 LauncherResult
<std::wstring
> LauncherRegistryInfo::GetBlocklistFileName() {
621 LauncherResult
<Disposition
> disposition
= Open();
622 if (disposition
.isErr()) {
623 return disposition
.propagateErr();
626 LauncherResult
<UniquePtr
<wchar_t[]>> readResult
=
627 ReadRegistryValueString(mRegKey
, ResolveBlocklistValueName());
628 if (readResult
.isErr()) {
629 return readResult
.propagateErr();
632 if (readResult
.inspect()) {
633 UniquePtr
<wchar_t[]> buf
= readResult
.unwrap();
634 return std::wstring(buf
.get());
637 LauncherResult
<std::wstring
> defaultBlocklistPath
=
638 BuildDefaultBlocklistFilename();
639 if (defaultBlocklistPath
.isErr()) {
640 return defaultBlocklistPath
.propagateErr();
643 LauncherVoidResult writeResult
= WriteRegistryValueString(
644 mRegKey
, ResolveBlocklistValueName(), defaultBlocklistPath
.inspect());
645 if (writeResult
.isErr()) {
646 return writeResult
.propagateErr();
649 return defaultBlocklistPath
;
652 } // namespace mozilla