Bug 1895153 - Implement "Find in page..." menu functionality r=android-reviewers...
[gecko.git] / toolkit / xre / LauncherRegistryInfo.cpp
blobb80ee77f4d00c1504d54ef2fd180da889d606786
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"
15 #include <cwctype>
16 #include <shlobj.h>
17 #include <string>
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));
36 if (!headers) {
37 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
40 DWORD timestamp;
41 if (!headers.GetTimeStamp(timestamp)) {
42 return LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_DATA);
45 return timestamp;
48 template <typename T>
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.");
53 T data;
54 DWORD dataLen = sizeof(data);
55 DWORD type;
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;
76 DWORD dataLen;
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) {
80 return buf;
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);
95 return buf;
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.");
117 LSTATUS status =
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) {
131 return false;
134 if (status != ERROR_SUCCESS) {
135 return LAUNCHER_ERROR_FROM_WIN32(status);
138 return true;
141 namespace mozilla {
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() {
155 if (!!mRegKey) {
156 return Disposition::OpenedExisting;
159 DWORD disposition;
160 HKEY rawKey;
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);
168 mRegKey.own(rawKey);
170 switch (disposition) {
171 case REG_CREATED_NEW_KEY:
172 return Disposition::CreatedNew;
173 case REG_OPENED_EXISTING_KEY:
174 return Disposition::OpenedExisting;
175 default:
176 break;
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.
195 return Ok();
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;
208 if (!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();
220 return Ok();
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;
318 } else {
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
338 // indicator.
339 mBrowserTimestampToWrite = Some(0ULL);
340 } else {
341 mBrowserTimestampToWrite = Some(QPCNowRaw());
343 break;
344 case ProcessType::Launcher:
345 mLauncherTimestampToWrite = Some(QPCNowRaw());
346 break;
347 default:
348 MOZ_ASSERT_UNREACHABLE("Invalid |typeToRunAs|");
349 return LAUNCHER_ERROR_GENERIC();
352 return typeToRunAs;
355 LauncherVoidResult LauncherRegistryInfo::DisableDueToFailure() {
356 LauncherResult<Disposition> disposition = Open();
357 if (disposition.isErr()) {
358 return disposition.propagateErr();
360 LauncherVoidResult result = WriteBrowserStartTimestamp(0ULL);
361 if (result.isOk()) {
362 // Block commit when we disable the launcher. It could be allowed
363 // when the image timestamp is updated.
364 sAllowCommit = false;
366 return result;
369 LauncherVoidResult LauncherRegistryInfo::Commit() {
370 if (!sAllowCommit) {
371 Abort();
372 return Ok();
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();
398 return Ok();
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
463 return 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(
519 uint64_t aValue) {
520 return WriteRegistryValueData(mRegKey, ResolveLauncherValueName(), REG_QWORD,
521 aValue);
524 LauncherVoidResult LauncherRegistryInfo::WriteBrowserStartTimestamp(
525 uint64_t aValue) {
526 return WriteRegistryValueData(mRegKey, ResolveBrowserValueName(), REG_QWORD,
527 aValue);
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.
548 return Ok();
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.
565 sAllowCommit = true;
567 return Ok();
570 LauncherResult<Maybe<DWORD>> LauncherRegistryInfo::GetSavedImageTimestamp() {
571 return ReadRegistryValueData<DWORD>(mRegKey, ResolveImageTimestampValueName(),
572 REG_DWORD);
575 LauncherResult<Maybe<uint64_t>>
576 LauncherRegistryInfo::GetLauncherStartTimestamp() {
577 return ReadRegistryValueData<uint64_t>(mRegKey, ResolveLauncherValueName(),
578 REG_QWORD);
581 LauncherResult<Maybe<uint64_t>>
582 LauncherRegistryInfo::GetBrowserStartTimestamp() {
583 return ReadRegistryValueData<uint64_t>(mRegKey, ResolveBrowserValueName(),
584 REG_QWORD);
587 LauncherResult<std::wstring>
588 LauncherRegistryInfo::BuildDefaultBlocklistFilename() {
589 // These flags are chosen to avoid I/O, see bug 1363398.
590 const DWORD flags =
591 KF_FLAG_SIMPLE_IDLIST | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_ALIAS;
592 PWSTR rawPath = nullptr;
593 HRESULT hr =
594 ::SHGetKnownFolderPath(FOLDERID_RoamingAppData, flags, nullptr, &rawPath);
595 if (FAILED(hr)) {
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()),
609 hash)) {
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