diff --git a/browser/app/winlauncher/LauncherProcessWin.cpp b/browser/app/winlauncher/LauncherProcessWin.cpp index 3ddb5d30ce69..1a8fce484643 100644 --- a/browser/app/winlauncher/LauncherProcessWin.cpp +++ b/browser/app/winlauncher/LauncherProcessWin.cpp @@ -163,7 +163,12 @@ static bool DoLauncherProcessChecks(int& argc, wchar_t** argv) { return result; } +#if defined(MOZ_LAUNCHER_PROCESS) +static mozilla::Maybe RunAsLauncherProcess( + mozilla::LauncherRegistryInfo& aRegInfo, int& argc, wchar_t** argv) { +#else static mozilla::Maybe RunAsLauncherProcess(int& argc, wchar_t** argv) { +#endif // defined(MOZ_LAUNCHER_PROCESS) // return fast when we're a child process. // (The remainder of this function has some side effects that are // undesirable for content processes) @@ -190,9 +195,8 @@ static mozilla::Maybe RunAsLauncherProcess(int& argc, wchar_t** argv) { forceLauncher ? mozilla::LauncherRegistryInfo::CheckOption::Force : mozilla::LauncherRegistryInfo::CheckOption::Default; - mozilla::LauncherRegistryInfo regInfo; mozilla::LauncherResult - runAsType = regInfo.Check(desiredType, checkOption); + runAsType = aRegInfo.Check(desiredType, checkOption); if (runAsType.isErr()) { mozilla::HandleLauncherError(runAsType); @@ -228,8 +232,20 @@ Maybe LauncherMain(int& argc, wchar_t* argv[], SetLauncherErrorForceEventLog(); } +#if defined(MOZ_LAUNCHER_PROCESS) + LauncherRegistryInfo regInfo; + Maybe runAsLauncher = RunAsLauncherProcess(regInfo, argc, argv); +#else Maybe runAsLauncher = RunAsLauncherProcess(argc, argv); +#endif // defined(MOZ_LAUNCHER_PROCESS) if (!runAsLauncher || !runAsLauncher.value()) { +#if defined(MOZ_LAUNCHER_PROCESS) + // Update the registry as Browser + LauncherVoidResult commitResult = regInfo.Commit(); + if (commitResult.isErr()) { + mozilla::HandleLauncherError(commitResult); + } +#endif // defined(MOZ_LAUNCHER_PROCESS) return Nothing(); } @@ -283,6 +299,15 @@ Maybe LauncherMain(int& argc, wchar_t* argv[], return Some(0); } +#if defined(MOZ_LAUNCHER_PROCESS) + // Update the registry as Launcher + LauncherVoidResult commitResult = regInfo.Commit(); + if (commitResult.isErr()) { + mozilla::HandleLauncherError(commitResult); + return Nothing(); + } +#endif // defined(MOZ_LAUNCHER_PROCESS) + // Now proceed with setting up the parameters for process creation UniquePtr cmdLine(MakeCommandLine(argc, argv)); if (!cmdLine) { diff --git a/testing/cppunittest.ini b/testing/cppunittest.ini index 173b9557b350..c9f2e2dca203 100644 --- a/testing/cppunittest.ini +++ b/testing/cppunittest.ini @@ -27,6 +27,8 @@ skip-if = os != 'win' [TestIntegerPrintfMacros] [TestIntegerRange] [TestJSONWriter] +[TestLauncherRegistryInfo] +skip-if = os != 'win' [TestLinkedList] [TestMacroArgs] [TestMacroForEach] diff --git a/toolkit/xre/LauncherRegistryInfo.cpp b/toolkit/xre/LauncherRegistryInfo.cpp index 3ea387a4a1d3..46c5511ba0a8 100644 --- a/toolkit/xre/LauncherRegistryInfo.cpp +++ b/toolkit/xre/LauncherRegistryInfo.cpp @@ -6,9 +6,9 @@ #include "LauncherRegistryInfo.h" +#include "mozilla/UniquePtr.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" -#include "mozilla/CmdLineAndEnvUtils.h" #include "mozilla/NativeNt.h" #include @@ -16,6 +16,85 @@ #define EXPAND_STRING_MACRO2(t) t #define EXPAND_STRING_MACRO(t) EXPAND_STRING_MACRO2(t) +// This function is copied from Chromium base/time/time_win.cc +// Returns the current value of the performance counter. +static uint64_t QPCNowRaw() { + LARGE_INTEGER perf_counter_now = {}; + // According to the MSDN documentation for QueryPerformanceCounter(), this + // will never fail on systems that run XP or later. + // https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter + ::QueryPerformanceCounter(&perf_counter_now); + return perf_counter_now.QuadPart; +} + +static mozilla::LauncherResult GetCurrentImageTimestamp() { + mozilla::nt::PEHeaders headers(::GetModuleHandleW(nullptr)); + if (!headers) { + return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT); + } + + DWORD timestamp; + if (!headers.GetTimeStamp(timestamp)) { + return LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_DATA); + } + + return timestamp; +} + +template +static mozilla::LauncherResult> ReadRegistryValueData( + const nsAutoRegKey& key, const std::wstring& name, DWORD expectedType) { + static_assert(mozilla::IsPod::value, + "Registry value type must be primitive."); + T data; + DWORD dataLen = sizeof(data); + DWORD type; + LSTATUS status = ::RegQueryValueExW(key.get(), name.c_str(), nullptr, &type, + reinterpret_cast(&data), &dataLen); + if (status == ERROR_FILE_NOT_FOUND) { + return mozilla::Maybe(); + } + + if (status != ERROR_SUCCESS) { + return LAUNCHER_ERROR_FROM_WIN32(status); + } + + if (type != expectedType) { + return LAUNCHER_ERROR_FROM_WIN32(ERROR_DATATYPE_MISMATCH); + } + + return mozilla::Some(data); +} + +template +static mozilla::LauncherVoidResult WriteRegistryValueData( + const nsAutoRegKey& key, const std::wstring& name, DWORD type, T data) { + static_assert(mozilla::IsPod::value, + "Registry value type must be primitive."); + LSTATUS status = + ::RegSetValueExW(key.get(), name.c_str(), 0, type, + reinterpret_cast(&data), sizeof(data)); + if (status != ERROR_SUCCESS) { + return LAUNCHER_ERROR_FROM_WIN32(status); + } + + return mozilla::Ok(); +} + +static mozilla::LauncherResult DeleteRegistryValueData( + const nsAutoRegKey& key, const std::wstring& name) { + LSTATUS status = ::RegDeleteValueW(key, name.c_str()); + if (status == ERROR_FILE_NOT_FOUND) { + return false; + } + + if (status != ERROR_SUCCESS) { + return LAUNCHER_ERROR_FROM_WIN32(status); + } + + return true; +} + namespace mozilla { const wchar_t LauncherRegistryInfo::kLauncherSubKeyPath[] = @@ -26,6 +105,8 @@ const wchar_t LauncherRegistryInfo::kBrowserSuffix[] = L"|Browser"; const wchar_t LauncherRegistryInfo::kImageTimestampSuffix[] = L"|Image"; const wchar_t LauncherRegistryInfo::kTelemetrySuffix[] = L"|Telemetry"; +bool LauncherRegistryInfo::sAllowCommit = true; + LauncherResult LauncherRegistryInfo::Open() { if (!!mRegKey) { return Disposition::OpenedExisting; @@ -71,21 +152,22 @@ LauncherVoidResult LauncherRegistryInfo::ReflectPrefToRegistry( } // Always delete the launcher timestamp - LauncherResult clearedLauncherTimestamp = - ClearStartTimestamp(ProcessType::Launcher); + LauncherResult clearedLauncherTimestamp = ClearLauncherStartTimestamp(); MOZ_ASSERT(clearedLauncherTimestamp.isOk()); if (clearedLauncherTimestamp.isErr()) { return LAUNCHER_ERROR_FROM_RESULT(clearedLauncherTimestamp); } + // Allow commit when we enable the launcher, otherwise block. + sAllowCommit = aEnable; + if (!aEnable) { // Set the browser timestamp to 0 to indicate force-disabled - return WriteStartTimestamp(ProcessType::Browser, Some(0ULL)); + return WriteBrowserStartTimestamp(0ULL); } // Otherwise we delete the browser timestamp to start over fresh - LauncherResult clearedBrowserTimestamp = - ClearStartTimestamp(ProcessType::Browser); + LauncherResult clearedBrowserTimestamp = ClearBrowserStartTimestamp(); MOZ_ASSERT(clearedBrowserTimestamp.isOk()); if (clearedBrowserTimestamp.isErr()) { return LAUNCHER_ERROR_FROM_RESULT(clearedBrowserTimestamp); @@ -101,18 +183,8 @@ LauncherVoidResult LauncherRegistryInfo::ReflectTelemetryPrefToRegistry( return LAUNCHER_ERROR_FROM_RESULT(disposition); } - std::wstring valueName(ResolveTelemetryValueName()); - - DWORD value = aEnable ? 1UL : 0UL; - DWORD len = sizeof(value); - LSTATUS result = - ::RegSetValueExW(mRegKey.get(), valueName.c_str(), 0, REG_DWORD, - reinterpret_cast(&value), len); - if (result != ERROR_SUCCESS) { - return LAUNCHER_ERROR_FROM_WIN32(result); - } - - return Ok(); + return WriteRegistryValueData(mRegKey, ResolveTelemetryValueName(), REG_DWORD, + aEnable ? 1UL : 0UL); } LauncherResult LauncherRegistryInfo::Check( @@ -127,17 +199,15 @@ LauncherResult LauncherRegistryInfo::Check( return LAUNCHER_ERROR_FROM_RESULT(ourImageTimestamp); } - LauncherResult savedImageTimestamp = GetSavedImageTimestamp(); - if (savedImageTimestamp.isErr() && - savedImageTimestamp.unwrapErr() != - WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) { + LauncherResult> savedImageTimestamp = GetSavedImageTimestamp(); + if (savedImageTimestamp.isErr()) { return LAUNCHER_ERROR_FROM_RESULT(savedImageTimestamp); } // If we don't have a saved timestamp, or we do but it doesn't match with - // our current timestamp, clear previous values. - if (savedImageTimestamp.isErr() || - savedImageTimestamp.inspect() != ourImageTimestamp.inspect()) { + // our current timestamp, clear previous values unless we're force-disabled. + if (savedImageTimestamp.inspect().isNothing() || + savedImageTimestamp.inspect().value() != ourImageTimestamp.inspect()) { LauncherVoidResult clearResult = ClearStartTimestamps(); if (clearResult.isErr()) { return LAUNCHER_ERROR_FROM_RESULT(clearResult); @@ -150,39 +220,53 @@ LauncherResult LauncherRegistryInfo::Check( } } - ProcessType typeToRunAs = aDesiredType; - - if (disposition.inspect() == Disposition::CreatedNew || - aDesiredType == ProcessType::Browser) { - // No existing values to check, or we're going to be running as the browser - // process: just write our timestamp and return - LauncherVoidResult wroteTimestamp = WriteStartTimestamp(typeToRunAs); - if (wroteTimestamp.isErr()) { - return LAUNCHER_ERROR_FROM_RESULT(wroteTimestamp); - } - - return typeToRunAs; + // If we're going to be running as the browser process, or there is no + // existing values to check, just write our timestamp and return. + if (aDesiredType == ProcessType::Browser) { + mBrowserTimestampToWrite = Some(QPCNowRaw()); + return ProcessType::Browser; } - LauncherResult lastLauncherTimestamp = - GetStartTimestamp(ProcessType::Launcher); - bool haveLauncherTs = lastLauncherTimestamp.isOk(); + if (disposition.inspect() == Disposition::CreatedNew) { + mLauncherTimestampToWrite = Some(QPCNowRaw()); + return ProcessType::Launcher; + } - LauncherResult lastBrowserTimestamp = - GetStartTimestamp(ProcessType::Browser); - bool haveBrowserTs = lastBrowserTimestamp.isOk(); + if (disposition.inspect() != Disposition::OpenedExisting) { + MOZ_ASSERT_UNREACHABLE("Invalid |disposition|"); + return LAUNCHER_ERROR_GENERIC(); + } - if (haveLauncherTs != haveBrowserTs) { + LauncherResult> lastLauncherTimestampResult = + GetLauncherStartTimestamp(); + if (lastLauncherTimestampResult.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(lastLauncherTimestampResult); + } + + LauncherResult> lastBrowserTimestampResult = + GetBrowserStartTimestamp(); + if (lastBrowserTimestampResult.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(lastBrowserTimestampResult); + } + + const Maybe& lastLauncherTimestamp = + lastLauncherTimestampResult.inspect(); + const Maybe& lastBrowserTimestamp = + lastBrowserTimestampResult.inspect(); + + ProcessType typeToRunAs = aDesiredType; + + if (lastLauncherTimestamp.isSome() != lastBrowserTimestamp.isSome()) { // If we have a launcher timestamp but no browser timestamp (or vice versa), // that's bad because it is indicating that the browser can't run with // the launcher process. typeToRunAs = ProcessType::Browser; - } else if (haveLauncherTs) { + } else if (lastLauncherTimestamp.isSome()) { // if we have both timestamps, we want to ensure that the launcher timestamp // is earlier than the browser timestamp. if (aDesiredType == ProcessType::Launcher) { bool areTimestampsOk = - lastLauncherTimestamp.inspect() < lastBrowserTimestamp.inspect(); + lastLauncherTimestamp.value() < lastBrowserTimestamp.value(); if (!areTimestampsOk) { typeToRunAs = ProcessType::Browser; } @@ -202,18 +286,23 @@ LauncherResult LauncherRegistryInfo::Check( typeToRunAs = aDesiredType; } - LauncherVoidResult wroteTimestamp = Ok(); - - if (typeToRunAs == ProcessType::Browser && aDesiredType != typeToRunAs) { - // We were hoping to run as the launcher, but some failure has caused us - // to run as the browser. Set the browser timestamp to zero as an indicator. - wroteTimestamp = WriteStartTimestamp(typeToRunAs, Some(0ULL)); - } else { - wroteTimestamp = WriteStartTimestamp(typeToRunAs); - } - - if (wroteTimestamp.isErr()) { - return LAUNCHER_ERROR_FROM_RESULT(wroteTimestamp); + switch (typeToRunAs) { + case ProcessType::Browser: + if (aDesiredType != typeToRunAs) { + // We were hoping to run as the launcher, but some failure has caused + // us to run as the browser. Set the browser timestamp to zero as an + // indicator. + mBrowserTimestampToWrite = Some(0ULL); + } else { + mBrowserTimestampToWrite = Some(QPCNowRaw()); + } + break; + case ProcessType::Launcher: + mLauncherTimestampToWrite = Some(QPCNowRaw()); + break; + default: + MOZ_ASSERT_UNREACHABLE("Invalid |typeToRunAs|"); + return LAUNCHER_ERROR_GENERIC(); } return typeToRunAs; @@ -224,8 +313,70 @@ LauncherVoidResult LauncherRegistryInfo::DisableDueToFailure() { if (disposition.isErr()) { return LAUNCHER_ERROR_FROM_RESULT(disposition); } + LauncherVoidResult result = WriteBrowserStartTimestamp(0ULL); + if (result.isOk()) { + // Block commit when we disable the launcher. It could be allowed + // when the image timestamp is updated. + sAllowCommit = false; + } + return result; +} - return WriteStartTimestamp(ProcessType::Browser, Some(0ULL)); +LauncherVoidResult LauncherRegistryInfo::Commit() { + if (!sAllowCommit) { + Abort(); + return Ok(); + } + + LauncherResult disposition = Open(); + if (disposition.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(disposition); + } + + if (mLauncherTimestampToWrite.isSome()) { + LauncherVoidResult writeResult = + WriteLauncherStartTimestamp(mLauncherTimestampToWrite.value()); + if (writeResult.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(writeResult); + } + mLauncherTimestampToWrite = Nothing(); + } + + if (mBrowserTimestampToWrite.isSome()) { + LauncherVoidResult writeResult = + WriteBrowserStartTimestamp(mBrowserTimestampToWrite.value()); + if (writeResult.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(writeResult); + } + mBrowserTimestampToWrite = Nothing(); + } + + return Ok(); +} + +void LauncherRegistryInfo::Abort() { + mLauncherTimestampToWrite = mBrowserTimestampToWrite = Nothing(); +} + +LauncherRegistryInfo::EnabledState LauncherRegistryInfo::GetEnabledState( + const Maybe& aLauncherTs, const Maybe& aBrowserTs) { + if (aBrowserTs.isSome()) { + if (aLauncherTs.isSome()) { + if (aLauncherTs.value() < aBrowserTs.value()) { + // Both timestamps exist and the browser's timestamp is later. + return EnabledState::Enabled; + } + } else if (aBrowserTs.value() == 0ULL) { + // Only browser's timestamp exists and its value is 0. + return EnabledState::ForceDisabled; + } + } else if (aLauncherTs.isNothing()) { + // Neither timestamps exist. + return EnabledState::Enabled; + } + + // Everything else is FailDisabled. + return EnabledState::FailDisabled; } LauncherResult @@ -235,32 +386,20 @@ LauncherRegistryInfo::IsEnabled() { return LAUNCHER_ERROR_FROM_RESULT(disposition); } - LauncherResult launcherTimestamp = - GetStartTimestamp(ProcessType::Launcher); - - LauncherResult browserTimestamp = - GetStartTimestamp(ProcessType::Browser); - - // In this function, we'll explictly search for the ForceDisabled and - // Enabled conditions. Everything else is FailDisabled. - - bool isBrowserTimestampZero = - browserTimestamp.isOk() && browserTimestamp.inspect() == 0ULL; - - if (isBrowserTimestampZero && launcherTimestamp.isErr()) { - return EnabledState::ForceDisabled; + LauncherResult> lastLauncherTimestamp = + GetLauncherStartTimestamp(); + if (lastLauncherTimestamp.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(lastLauncherTimestamp); } - if (launcherTimestamp.isOk() && browserTimestamp.isOk() && - launcherTimestamp.inspect() < browserTimestamp.inspect()) { - return EnabledState::Enabled; + LauncherResult> lastBrowserTimestamp = + GetBrowserStartTimestamp(); + if (lastBrowserTimestamp.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(lastBrowserTimestamp); } - if (launcherTimestamp.isErr() && browserTimestamp.isErr()) { - return EnabledState::Enabled; - } - - return EnabledState::FailDisabled; + return GetEnabledState(lastLauncherTimestamp.inspect(), + lastBrowserTimestamp.inspect()); } LauncherResult LauncherRegistryInfo::IsTelemetryEnabled() { @@ -269,32 +408,40 @@ LauncherResult LauncherRegistryInfo::IsTelemetryEnabled() { return LAUNCHER_ERROR_FROM_RESULT(disposition); } - return GetTelemetrySetting(); -} - -LauncherResult LauncherRegistryInfo::ResolveValueName( - LauncherRegistryInfo::ProcessType aProcessType) { - if (aProcessType == ProcessType::Launcher) { - if (mLauncherValueName.empty()) { - mLauncherValueName.assign(mBinPath); - mLauncherValueName.append(kLauncherSuffix, - ArrayLength(kLauncherSuffix) - 1); - } - - return mLauncherValueName; - } else if (aProcessType == ProcessType::Browser) { - if (mBrowserValueName.empty()) { - mBrowserValueName.assign(mBinPath); - mBrowserValueName.append(kBrowserSuffix, ArrayLength(kBrowserSuffix) - 1); - } - - return mBrowserValueName; + LauncherResult> result = ReadRegistryValueData( + mRegKey, ResolveTelemetryValueName(), REG_DWORD); + if (result.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(result); } - return LAUNCHER_ERROR_GENERIC(); + if (result.inspect().isNothing()) { + // Value does not exist, treat as false + return false; + } + + return result.inspect().value() != 0; } -std::wstring LauncherRegistryInfo::ResolveImageTimestampValueName() { +const std::wstring& LauncherRegistryInfo::ResolveLauncherValueName() { + if (mLauncherValueName.empty()) { + mLauncherValueName.assign(mBinPath); + mLauncherValueName.append(kLauncherSuffix, + ArrayLength(kLauncherSuffix) - 1); + } + + return mLauncherValueName; +} + +const std::wstring& LauncherRegistryInfo::ResolveBrowserValueName() { + if (mBrowserValueName.empty()) { + mBrowserValueName.assign(mBinPath); + mBrowserValueName.append(kBrowserSuffix, ArrayLength(kBrowserSuffix) - 1); + } + + return mBrowserValueName; +} + +const std::wstring& LauncherRegistryInfo::ResolveImageTimestampValueName() { if (mImageValueName.empty()) { mImageValueName.assign(mBinPath); mImageValueName.append(kImageTimestampSuffix, @@ -304,7 +451,7 @@ std::wstring LauncherRegistryInfo::ResolveImageTimestampValueName() { return mImageValueName; } -std::wstring LauncherRegistryInfo::ResolveTelemetryValueName() { +const std::wstring& LauncherRegistryInfo::ResolveTelemetryValueName() { if (mTelemetryValueName.empty()) { mTelemetryValueName.assign(mBinPath); mTelemetryValueName.append(kTelemetrySuffix, @@ -314,83 +461,29 @@ std::wstring LauncherRegistryInfo::ResolveTelemetryValueName() { return mTelemetryValueName; } -LauncherVoidResult LauncherRegistryInfo::WriteStartTimestamp( - LauncherRegistryInfo::ProcessType aProcessType, - const Maybe& aValue) { - LauncherResult name = ResolveValueName(aProcessType); - if (name.isErr()) { - return LAUNCHER_ERROR_FROM_RESULT(name); - } - - ULARGE_INTEGER timestamp; - if (aValue.isSome()) { - timestamp.QuadPart = aValue.value(); - } else { - // We need to use QPC here because millisecond granularity is too coarse - // to properly measure the times involved. - if (!::QueryPerformanceCounter( - reinterpret_cast(×tamp))) { - return LAUNCHER_ERROR_FROM_LAST(); - } - } - - DWORD len = sizeof(timestamp); - LSTATUS result = - ::RegSetValueExW(mRegKey.get(), name.inspect().c_str(), 0, REG_QWORD, - reinterpret_cast(×tamp), len); - if (result != ERROR_SUCCESS) { - return LAUNCHER_ERROR_FROM_WIN32(result); - } - - return Ok(); +LauncherVoidResult LauncherRegistryInfo::WriteLauncherStartTimestamp( + uint64_t aValue) { + return WriteRegistryValueData(mRegKey, ResolveLauncherValueName(), REG_QWORD, + aValue); } -LauncherResult LauncherRegistryInfo::GetCurrentImageTimestamp() { - nt::PEHeaders headers(::GetModuleHandleW(nullptr)); - if (!headers) { - return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT); - } - - DWORD timestamp; - if (!headers.GetTimeStamp(timestamp)) { - return LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_DATA); - } - - return timestamp; +LauncherVoidResult LauncherRegistryInfo::WriteBrowserStartTimestamp( + uint64_t aValue) { + return WriteRegistryValueData(mRegKey, ResolveBrowserValueName(), REG_QWORD, + aValue); } LauncherVoidResult LauncherRegistryInfo::WriteImageTimestamp(DWORD aTimestamp) { - std::wstring imageTimestampValueName = ResolveImageTimestampValueName(); - - DWORD len = sizeof(aTimestamp); - LSTATUS result = - ::RegSetValueExW(mRegKey.get(), imageTimestampValueName.c_str(), 0, - REG_DWORD, reinterpret_cast(&aTimestamp), len); - if (result != ERROR_SUCCESS) { - return LAUNCHER_ERROR_FROM_WIN32(result); - } - - return Ok(); + return WriteRegistryValueData(mRegKey, ResolveImageTimestampValueName(), + REG_DWORD, aTimestamp); } -LauncherResult LauncherRegistryInfo::ClearStartTimestamp( - LauncherRegistryInfo::ProcessType aProcessType) { - LauncherResult timestampName = ResolveValueName(aProcessType); - if (timestampName.isErr()) { - return LAUNCHER_ERROR_FROM_RESULT(timestampName); - } +LauncherResult LauncherRegistryInfo::ClearLauncherStartTimestamp() { + return DeleteRegistryValueData(mRegKey, ResolveLauncherValueName()); +} - LSTATUS result = - ::RegDeleteValueW(mRegKey.get(), timestampName.inspect().c_str()); - if (result == ERROR_SUCCESS) { - return true; - } - - if (result == ERROR_FILE_NOT_FOUND) { - return false; - } - - return LAUNCHER_ERROR_FROM_WIN32(result); +LauncherResult LauncherRegistryInfo::ClearBrowserStartTimestamp() { + return DeleteRegistryValueData(mRegKey, ResolveBrowserValueName()); } LauncherVoidResult LauncherRegistryInfo::ClearStartTimestamps() { @@ -401,90 +494,40 @@ LauncherVoidResult LauncherRegistryInfo::ClearStartTimestamps() { return Ok(); } - LauncherResult clearedLauncherTimestamp = - ClearStartTimestamp(ProcessType::Launcher); + LauncherResult clearedLauncherTimestamp = ClearLauncherStartTimestamp(); if (clearedLauncherTimestamp.isErr()) { return LAUNCHER_ERROR_FROM_RESULT(clearedLauncherTimestamp); } - LauncherResult clearedBrowserTimestamp = - ClearStartTimestamp(ProcessType::Browser); + LauncherResult clearedBrowserTimestamp = ClearBrowserStartTimestamp(); if (clearedBrowserTimestamp.isErr()) { return LAUNCHER_ERROR_FROM_RESULT(clearedBrowserTimestamp); } + // Reset both timestamps to align with registry deletion + mLauncherTimestampToWrite = mBrowserTimestampToWrite = Nothing(); + + // Disablement is gone. Let's allow commit. + sAllowCommit = true; + return Ok(); } -LauncherResult LauncherRegistryInfo::GetSavedImageTimestamp() { - std::wstring imageTimestampValueName = ResolveImageTimestampValueName(); - - DWORD value; - DWORD valueLen = sizeof(value); - DWORD type; - LSTATUS result = ::RegQueryValueExW( - mRegKey.get(), imageTimestampValueName.c_str(), nullptr, &type, - reinterpret_cast(&value), &valueLen); - // NB: If the value does not exist, result == ERROR_FILE_NOT_FOUND - if (result != ERROR_SUCCESS) { - return LAUNCHER_ERROR_FROM_WIN32(result); - } - - if (type != REG_DWORD) { - return LAUNCHER_ERROR_FROM_WIN32(ERROR_DATATYPE_MISMATCH); - } - - return value; +LauncherResult> LauncherRegistryInfo::GetSavedImageTimestamp() { + return ReadRegistryValueData(mRegKey, ResolveImageTimestampValueName(), + REG_DWORD); } -LauncherResult LauncherRegistryInfo::GetTelemetrySetting() { - std::wstring telemetryValueName = ResolveTelemetryValueName(); - - DWORD value; - DWORD valueLen = sizeof(value); - DWORD type; - LSTATUS result = - ::RegQueryValueExW(mRegKey.get(), telemetryValueName.c_str(), nullptr, - &type, reinterpret_cast(&value), &valueLen); - if (result == ERROR_FILE_NOT_FOUND) { - // Value does not exist, treat as false - return false; - } - - if (result != ERROR_SUCCESS) { - return LAUNCHER_ERROR_FROM_WIN32(result); - } - - if (type != REG_DWORD) { - return LAUNCHER_ERROR_FROM_WIN32(ERROR_DATATYPE_MISMATCH); - } - - return value != 0; +LauncherResult> +LauncherRegistryInfo::GetLauncherStartTimestamp() { + return ReadRegistryValueData(mRegKey, ResolveLauncherValueName(), + REG_QWORD); } -LauncherResult LauncherRegistryInfo::GetStartTimestamp( - LauncherRegistryInfo::ProcessType aProcessType) { - LauncherResult name = ResolveValueName(aProcessType); - if (name.isErr()) { - return LAUNCHER_ERROR_FROM_RESULT(name); - } - - uint64_t value; - DWORD valueLen = sizeof(value); - DWORD type; - LSTATUS result = - ::RegQueryValueExW(mRegKey.get(), name.inspect().c_str(), nullptr, &type, - reinterpret_cast(&value), &valueLen); - // NB: If the value does not exist, result == ERROR_FILE_NOT_FOUND - if (result != ERROR_SUCCESS) { - return LAUNCHER_ERROR_FROM_WIN32(result); - } - - if (type != REG_QWORD) { - return LAUNCHER_ERROR_FROM_WIN32(ERROR_DATATYPE_MISMATCH); - } - - return value; +LauncherResult> +LauncherRegistryInfo::GetBrowserStartTimestamp() { + return ReadRegistryValueData(mRegKey, ResolveBrowserValueName(), + REG_QWORD); } } // namespace mozilla diff --git a/toolkit/xre/LauncherRegistryInfo.h b/toolkit/xre/LauncherRegistryInfo.h index 11a149d7a042..c8d015a4ced2 100644 --- a/toolkit/xre/LauncherRegistryInfo.h +++ b/toolkit/xre/LauncherRegistryInfo.h @@ -8,7 +8,6 @@ #define mozilla_LauncherRegistryInfo_h #include "mozilla/Maybe.h" -#include "mozilla/UniquePtr.h" #include "mozilla/WinHeaderOnlyUtils.h" #include "nsWindowsHelpers.h" @@ -25,10 +24,10 @@ class LauncherRegistryInfo final { public: enum class ProcessType { Launcher, Browser }; - enum class EnabledState : uint32_t { - Enabled = 0, - FailDisabled = 1, - ForceDisabled = 2, + enum class EnabledState { + Enabled, + FailDisabled, + ForceDisabled, }; enum class CheckOption { @@ -37,6 +36,7 @@ class LauncherRegistryInfo final { }; LauncherRegistryInfo() : mBinPath(GetFullBinaryPath().get()) {} + ~LauncherRegistryInfo() { Abort(); } LauncherVoidResult ReflectPrefToRegistry(const bool aEnable); LauncherResult IsEnabled(); @@ -46,27 +46,40 @@ class LauncherRegistryInfo final { const ProcessType aDesiredType, const CheckOption aOption = CheckOption::Default); LauncherVoidResult DisableDueToFailure(); + LauncherVoidResult Commit(); + void Abort(); private: enum class Disposition { CreatedNew, OpenedExisting }; private: - LauncherResult Open(); - LauncherVoidResult WriteStartTimestamp( - ProcessType aProcessType, const Maybe& aValue = Nothing()); - LauncherResult GetCurrentImageTimestamp(); - LauncherVoidResult WriteImageTimestamp(DWORD aTimestamp); - LauncherResult ClearStartTimestamp(ProcessType aProcessType); - LauncherVoidResult ClearStartTimestamps(); - LauncherResult GetSavedImageTimestamp(); - LauncherResult GetStartTimestamp(ProcessType aProcessType); - LauncherResult GetTelemetrySetting(); + // This flag is to prevent the disabled state from being accidentally + // re-enabled by another instance. + static bool sAllowCommit; - LauncherResult ResolveValueName(ProcessType aProcessType); - std::wstring ResolveImageTimestampValueName(); - std::wstring ResolveTelemetryValueName(); + static EnabledState GetEnabledState(const Maybe& aLauncherTs, + const Maybe& aBrowserTs); + + LauncherResult Open(); + LauncherVoidResult WriteLauncherStartTimestamp(uint64_t aValue); + LauncherVoidResult WriteBrowserStartTimestamp(uint64_t aValue); + LauncherVoidResult WriteImageTimestamp(DWORD aTimestamp); + LauncherResult ClearLauncherStartTimestamp(); + LauncherResult ClearBrowserStartTimestamp(); + LauncherVoidResult ClearStartTimestamps(); + LauncherResult> GetSavedImageTimestamp(); + LauncherResult> GetLauncherStartTimestamp(); + LauncherResult> GetBrowserStartTimestamp(); + + const std::wstring& ResolveLauncherValueName(); + const std::wstring& ResolveBrowserValueName(); + const std::wstring& ResolveImageTimestampValueName(); + const std::wstring& ResolveTelemetryValueName(); private: + Maybe mLauncherTimestampToWrite; + Maybe mBrowserTimestampToWrite; + nsAutoRegKey mRegKey; std::wstring mBinPath; std::wstring mImageValueName; diff --git a/toolkit/xre/test/win/TestLauncherRegistryInfo.cpp b/toolkit/xre/test/win/TestLauncherRegistryInfo.cpp index a79e930eca7f..1845720c4961 100644 --- a/toolkit/xre/test/win/TestLauncherRegistryInfo.cpp +++ b/toolkit/xre/test/win/TestLauncherRegistryInfo.cpp @@ -8,556 +8,469 @@ #include "mozilla/NativeNt.h" #include "mozilla/ScopeExit.h" #include "mozilla/Unused.h" -#include "CmdLineAndEnvUtils.h" #include "nsWindowsHelpers.h" #include "LauncherRegistryInfo.cpp" #include -const char kFailFmt[] = "TEST-FAILED | LauncherRegistryInfo | %s | %S\n"; +static const char kMsgStart[] = "TEST-FAILED | LauncherRegistryInfo | "; static const wchar_t kRegKeyPath[] = L"SOFTWARE\\" EXPAND_STRING_MACRO( MOZ_APP_VENDOR) L"\\" EXPAND_STRING_MACRO(MOZ_APP_BASENAME) L"\\Launcher"; static const wchar_t kBrowserSuffix[] = L"|Browser"; static const wchar_t kLauncherSuffix[] = L"|Launcher"; static const wchar_t kImageSuffix[] = L"|Image"; +static const wchar_t kTelemetrySuffix[] = L"|Telemetry"; static std::wstring gBrowserValue; static std::wstring gLauncherValue; static std::wstring gImageValue; +static std::wstring gTelemetryValue; static DWORD gMyImageTimestamp; -using QWordResult = mozilla::Result; -using DWordResult = mozilla::Result; -using VoidResult = mozilla::Result; - -#define RUN_TEST(fn) \ - if ((vr = fn()).isErr()) { \ - printf(kFailFmt, #fn, vr.unwrapErr().AsString().get()); \ - return 1; \ +#define RUN_TEST(result, fn) \ + if ((result = fn()).isErr()) { \ + const mozilla::LauncherError& err = result.inspectErr(); \ + printf("%s%s | %08lx (%s:%d)\n", kMsgStart, #fn, err.mError.AsHResult(), \ + err.mFile, err.mLine); \ + return 1; \ } -static QWordResult GetBrowserTimestamp() { - DWORD64 qword; - DWORD size = sizeof(qword); - LSTATUS status = - ::RegGetValueW(HKEY_CURRENT_USER, kRegKeyPath, gBrowserValue.c_str(), - RRF_RT_QWORD, nullptr, &qword, &size); - if (status == ERROR_SUCCESS) { - return qword; +#define EXPECT_COMMIT_IS_OK() \ + do { \ + mozilla::LauncherVoidResult vr2 = info.Commit(); \ + if (vr2.isErr()) { \ + return vr2; \ + } \ + } while (0) + +#define EXPECT_CHECK_RESULT_IS(desired, expected) \ + do { \ + mozilla::LauncherResult \ + result = info.Check(mozilla::LauncherRegistryInfo::desired); \ + if (result.isErr()) { \ + return LAUNCHER_ERROR_FROM_RESULT(result); \ + } \ + if (result.unwrap() != mozilla::LauncherRegistryInfo::expected) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_FAIL); \ + } \ + } while (0) + +#define EXPECT_ENABLED_STATE_IS(expected) \ + do { \ + mozilla::LauncherResult \ + enabled = info.IsEnabled(); \ + if (enabled.isErr()) { \ + return LAUNCHER_ERROR_FROM_RESULT(enabled); \ + } \ + if (enabled.unwrap() != mozilla::LauncherRegistryInfo::expected) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +#define EXPECT_TELEMETRY_IS_ENABLED(expected) \ + do { \ + mozilla::LauncherResult enabled = info.IsTelemetryEnabled(); \ + if (enabled.isErr()) { \ + return LAUNCHER_ERROR_FROM_RESULT(enabled); \ + } \ + if (enabled.unwrap() != expected) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +#define EXPECT_REG_DWORD_EXISTS_AND_EQ(name, expected) \ + do { \ + mozilla::LauncherResult> result = \ + ReadRegistryValueData(name, REG_DWORD); \ + if (result.isErr()) { \ + return LAUNCHER_ERROR_FROM_RESULT(result); \ + } \ + if (result.inspect().isNothing() || \ + result.inspect().value() != expected) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +#define EXPECT_REG_QWORD_EXISTS(name) \ + do { \ + mozilla::LauncherResult> result = \ + ReadRegistryValueData(name, REG_QWORD); \ + if (result.isErr()) { \ + return LAUNCHER_ERROR_FROM_RESULT(result); \ + } \ + if (result.inspect().isNothing()) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +#define EXPECT_REG_QWORD_EXISTS_AND_EQ(name, expected) \ + do { \ + mozilla::LauncherResult> result = \ + ReadRegistryValueData(name, REG_QWORD); \ + if (result.isErr()) { \ + return LAUNCHER_ERROR_FROM_RESULT(result); \ + } \ + if (result.inspect().isNothing() || \ + result.inspect().value() != expected) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +#define EXPECT_REG_DWORD_DOES_NOT_EXIST(name) \ + do { \ + mozilla::LauncherResult> result = \ + ReadRegistryValueData(name, REG_DWORD); \ + if (result.isErr()) { \ + return LAUNCHER_ERROR_FROM_RESULT(result); \ + } \ + if (result.inspect().isSome()) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +#define EXPECT_REG_QWORD_DOES_NOT_EXIST(name) \ + do { \ + mozilla::LauncherResult> result = \ + ReadRegistryValueData(name, REG_QWORD); \ + if (result.isErr()) { \ + return LAUNCHER_ERROR_FROM_RESULT(result); \ + } \ + if (result.inspect().isSome()) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +template +static mozilla::LauncherResult> ReadRegistryValueData( + const std::wstring& name, DWORD expectedType) { + T data; + DWORD dataLen = sizeof(data); + DWORD type; + LSTATUS status = ::RegGetValueW(HKEY_CURRENT_USER, kRegKeyPath, name.c_str(), + RRF_RT_ANY, &type, &data, &dataLen); + if (status == ERROR_FILE_NOT_FOUND) { + return mozilla::Maybe(); } - return mozilla::Err(mozilla::WindowsError::FromWin32Error(status)); + if (status != ERROR_SUCCESS) { + return LAUNCHER_ERROR_FROM_WIN32(status); + } + + if (type != expectedType) { + return LAUNCHER_ERROR_FROM_WIN32(ERROR_DATATYPE_MISMATCH); + } + + return mozilla::Some(data); } -static VoidResult SetBrowserTimestamp(DWORD64 aTimestamp) { - LSTATUS status = - ::RegSetKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, gBrowserValue.c_str(), - REG_QWORD, &aTimestamp, sizeof(aTimestamp)); - if (status == ERROR_SUCCESS) { - return mozilla::Ok(); +template +static mozilla::LauncherVoidResult WriteRegistryValueData( + const std::wstring& name, DWORD type, T data) { + LSTATUS status = ::RegSetKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, + name.c_str(), type, &data, sizeof(T)); + if (status != ERROR_SUCCESS) { + return LAUNCHER_ERROR_FROM_WIN32(status); } - return mozilla::Err(mozilla::WindowsError::FromWin32Error(status)); + return mozilla::Ok(); } -static VoidResult DeleteBrowserTimestamp() { - LSTATUS status = ::RegDeleteKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, - gBrowserValue.c_str()); +static mozilla::LauncherVoidResult DeleteRegistryValueData( + const std::wstring& name) { + LSTATUS status = + ::RegDeleteKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, name.c_str()); if (status == ERROR_SUCCESS || status == ERROR_FILE_NOT_FOUND) { return mozilla::Ok(); } - return mozilla::Err(mozilla::WindowsError::FromWin32Error(status)); + return LAUNCHER_ERROR_FROM_WIN32(status); } -static QWordResult GetLauncherTimestamp() { - DWORD64 qword; - DWORD size = sizeof(qword); - LSTATUS status = - ::RegGetValueW(HKEY_CURRENT_USER, kRegKeyPath, gLauncherValue.c_str(), - RRF_RT_QWORD, nullptr, &qword, &size); - if (status == ERROR_SUCCESS) { - return qword; - } - - return mozilla::Err(mozilla::WindowsError::FromWin32Error(status)); -} - -static VoidResult SetLauncherTimestamp(DWORD64 aTimestamp) { - LSTATUS status = - ::RegSetKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, gLauncherValue.c_str(), - REG_QWORD, &aTimestamp, sizeof(aTimestamp)); - if (status == ERROR_SUCCESS) { - return mozilla::Ok(); - } - - return mozilla::Err(mozilla::WindowsError::FromWin32Error(status)); -} - -static VoidResult DeleteLauncherTimestamp() { - LSTATUS status = ::RegDeleteKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, - gLauncherValue.c_str()); - if (status == ERROR_SUCCESS || status == ERROR_FILE_NOT_FOUND) { - return mozilla::Ok(); - } - - return mozilla::Err(mozilla::WindowsError::FromWin32Error(status)); -} - -static DWordResult GetImageTimestamp() { - DWORD dword; - DWORD size = sizeof(dword); - LSTATUS status = - ::RegGetValueW(HKEY_CURRENT_USER, kRegKeyPath, gImageValue.c_str(), - RRF_RT_DWORD, nullptr, &dword, &size); - if (status == ERROR_SUCCESS) { - return dword; - } - - return mozilla::Err(mozilla::WindowsError::FromWin32Error(status)); -} - -static VoidResult SetImageTimestamp(DWORD aTimestamp) { - LSTATUS status = - ::RegSetKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, gImageValue.c_str(), - REG_DWORD, &aTimestamp, sizeof(aTimestamp)); - if (status == ERROR_SUCCESS) { - return mozilla::Ok(); - } - - return mozilla::Err(mozilla::WindowsError::FromWin32Error(status)); -} - -static VoidResult DeleteImageTimestamp() { - LSTATUS status = - ::RegDeleteKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, gImageValue.c_str()); - if (status == ERROR_SUCCESS || status == ERROR_FILE_NOT_FOUND) { - return mozilla::Ok(); - } - - return mozilla::Err(mozilla::WindowsError::FromWin32Error(status)); -} - -static VoidResult DeleteAllTimestamps() { - VoidResult vr = DeleteBrowserTimestamp(); +static mozilla::LauncherVoidResult DeleteAllRegstryValues() { + // Unblock commit via ReflectPrefToRegistry + // (We need to set false, and then true to bypass the early return) + mozilla::LauncherRegistryInfo info; + mozilla::LauncherVoidResult vr = info.ReflectPrefToRegistry(false); + vr = info.ReflectPrefToRegistry(true); if (vr.isErr()) { return vr; } - vr = DeleteLauncherTimestamp(); + vr = DeleteRegistryValueData(gImageValue); if (vr.isErr()) { return vr; } - return DeleteImageTimestamp(); + vr = DeleteRegistryValueData(gLauncherValue); + if (vr.isErr()) { + return vr; + } + + vr = DeleteRegistryValueData(gBrowserValue); + if (vr.isErr()) { + return vr; + } + + return DeleteRegistryValueData(gTelemetryValue); } -static VoidResult SetupEnabledScenario() { +static mozilla::LauncherVoidResult SetupEnabledScenario() { // Reset the registry state to an enabled state. First, we delete all existing - // timestamps (if any). - VoidResult vr = DeleteAllTimestamps(); + // registry values (if any). + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); if (vr.isErr()) { return vr; } // Now we run Check(Launcher)... mozilla::LauncherRegistryInfo info; - mozilla::LauncherResult result = - info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher); - if (result.isErr()) { - return mozilla::Err(result.unwrapErr().mError); - } - - if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Launcher) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } - + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + EXPECT_COMMIT_IS_OK(); // ...and Check(Browser) - result = info.Check(mozilla::LauncherRegistryInfo::ProcessType::Browser); - if (result.isErr()) { - return mozilla::Err(result.unwrapErr().mError); - } - - if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED)); - } + EXPECT_CHECK_RESULT_IS(ProcessType::Browser, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); // By this point we are considered to be fully enabled. - return mozilla::Ok(); } -static VoidResult TestEmptyRegistry() { - VoidResult vr = DeleteAllTimestamps(); +static mozilla::LauncherVoidResult TestEmptyRegistry() { + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); if (vr.isErr()) { return vr; } mozilla::LauncherRegistryInfo info; - mozilla::LauncherResult result = - info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher); - if (result.isErr()) { - return mozilla::Err(result.unwrapErr().mError); - } - - if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Launcher) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + EXPECT_COMMIT_IS_OK(); // LauncherRegistryInfo should have created Launcher and Image values - QWordResult launcherTs = GetLauncherTimestamp(); - if (launcherTs.isErr()) { - return mozilla::Err(launcherTs.unwrapErr()); - } + EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp); + EXPECT_REG_QWORD_EXISTS(gLauncherValue); + EXPECT_REG_QWORD_DOES_NOT_EXIST(gBrowserValue); - DWordResult imageTs = GetImageTimestamp(); - if (imageTs.isErr()) { - return mozilla::Err(imageTs.unwrapErr()); - } - - if (imageTs.unwrap() != gMyImageTimestamp) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED)); - } - - QWordResult browserTs = GetBrowserTimestamp(); - if (browserTs.isOk()) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED)); - } - - if (browserTs.unwrapErr() != - mozilla::WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) { - return mozilla::Err(browserTs.unwrapErr()); - } + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); return mozilla::Ok(); } -// This test depends on the side effects from having just run TestEmptyRegistry -static VoidResult TestNormal() { - mozilla::LauncherRegistryInfo info; - mozilla::LauncherResult result = - info.Check(mozilla::LauncherRegistryInfo::ProcessType::Browser); - if (result.isErr()) { - return mozilla::Err(result.unwrapErr().mError); +static mozilla::LauncherVoidResult TestNormal() { + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); + if (vr.isErr()) { + return vr; } - - QWordResult launcherTs = GetLauncherTimestamp(); - if (launcherTs.isErr()) { - return mozilla::Err(launcherTs.unwrapErr()); + vr = WriteRegistryValueData(gImageValue, REG_DWORD, gMyImageTimestamp); + if (vr.isErr()) { + return vr; } - - QWordResult browserTs = GetBrowserTimestamp(); - if (browserTs.isErr()) { - return mozilla::Err(browserTs.unwrapErr()); - } - - if (browserTs.unwrap() < launcherTs.unwrap()) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } - - mozilla::LauncherResult enabled = - info.IsEnabled(); - if (enabled.isErr()) { - return mozilla::Err(enabled.unwrapErr().mError); - } - - if (enabled.unwrap() != - mozilla::LauncherRegistryInfo::EnabledState::Enabled) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED)); - } - - return mozilla::Ok(); -} - -// This test depends on the side effects from having just run TestNormal -static VoidResult TestBrowserNoLauncher() { - VoidResult vr = DeleteLauncherTimestamp(); + vr = WriteRegistryValueData(gLauncherValue, REG_QWORD, QPCNowRaw()); if (vr.isErr()) { return vr; } mozilla::LauncherRegistryInfo info; - mozilla::LauncherResult result = - info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher); - if (result.isErr()) { - return mozilla::Err(result.unwrapErr().mError); + EXPECT_CHECK_RESULT_IS(ProcessType::Browser, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); + + // Make sure the browser timestamp is newer than the launcher's + mozilla::LauncherResult> launcherTs = + ReadRegistryValueData(gLauncherValue, REG_QWORD); + if (launcherTs.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(launcherTs); + } + mozilla::LauncherResult> browserTs = + ReadRegistryValueData(gBrowserValue, REG_QWORD); + if (browserTs.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(browserTs); + } + if (launcherTs.inspect().isNothing() || browserTs.inspect().isNothing() || + browserTs.inspect().value() <= launcherTs.inspect().value()) { + return LAUNCHER_ERROR_FROM_HRESULT(E_FAIL); } - if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); + EXPECT_ENABLED_STATE_IS(EnabledState::Enabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestBrowserNoLauncher() { + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); + if (vr.isErr()) { + return vr; } + vr = DeleteRegistryValueData(gLauncherValue); + if (vr.isErr()) { + return vr; + } + + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); // Verify that we still don't have a launcher timestamp - QWordResult launcherTs = GetLauncherTimestamp(); - if (launcherTs.isOk()) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } - - if (launcherTs.unwrapErr() != - mozilla::WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) { - return mozilla::Err(launcherTs.unwrapErr()); - } - + EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue); // Verify that the browser timestamp is now zero - QWordResult browserTs = GetBrowserTimestamp(); - if (browserTs.isErr()) { - return mozilla::Err(browserTs.unwrapErr()); - } + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL); - if (browserTs.unwrap() != 0ULL) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } - - mozilla::LauncherResult enabled = - info.IsEnabled(); - if (enabled.isErr()) { - return mozilla::Err(enabled.unwrapErr().mError); - } - - if (enabled.unwrap() != - mozilla::LauncherRegistryInfo::EnabledState::ForceDisabled) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED)); - } + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); return mozilla::Ok(); } -static VoidResult TestLauncherNoBrowser() { - VoidResult vr = DeleteAllTimestamps(); +static mozilla::LauncherVoidResult TestLauncherNoBrowser() { + constexpr uint64_t launcherTs = 0x77777777; + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); if (vr.isErr()) { return vr; } - - vr = SetLauncherTimestamp(0x77777777); + vr = WriteRegistryValueData(gImageValue, REG_DWORD, gMyImageTimestamp); if (vr.isErr()) { return vr; } - - vr = SetImageTimestamp(gMyImageTimestamp); + vr = WriteRegistryValueData(gLauncherValue, REG_QWORD, launcherTs); if (vr.isErr()) { return vr; } mozilla::LauncherRegistryInfo info; - mozilla::LauncherResult result = - info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher); - if (result.isErr()) { - return mozilla::Err(result.unwrapErr().mError); - } + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); - if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } + // Launcher's timestamps is kept intact while browser's is set to 0. + EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL); - mozilla::LauncherResult enabled = - info.IsEnabled(); - if (enabled.isErr()) { - return mozilla::Err(enabled.unwrapErr().mError); - } - - if (enabled.unwrap() != - mozilla::LauncherRegistryInfo::EnabledState::FailDisabled) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED)); - } + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); return mozilla::Ok(); } -static VoidResult TestBrowserLessThanLauncher() { - VoidResult vr = SetLauncherTimestamp(0x77777777); +static mozilla::LauncherVoidResult TestBrowserLessThanLauncher() { + constexpr uint64_t launcherTs = 0x77777777, browserTs = 0x66666666; + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); if (vr.isErr()) { return vr; } - - vr = SetBrowserTimestamp(0ULL); + vr = WriteRegistryValueData(gImageValue, REG_DWORD, gMyImageTimestamp); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData(gLauncherValue, REG_QWORD, launcherTs); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData(gBrowserValue, REG_QWORD, browserTs); if (vr.isErr()) { return vr; } mozilla::LauncherRegistryInfo info; - mozilla::LauncherResult result = - info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher); - if (result.isErr()) { - return mozilla::Err(result.unwrapErr().mError); - } + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); - if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } + // Launcher's timestamps is kept intact while browser's is set to 0. + EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL); - mozilla::LauncherResult enabled = - info.IsEnabled(); - if (enabled.isErr()) { - return mozilla::Err(enabled.unwrapErr().mError); - } - - if (enabled.unwrap() != - mozilla::LauncherRegistryInfo::EnabledState::FailDisabled) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED)); - } + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); return mozilla::Ok(); } -static VoidResult TestImageTimestampChange() { +static mozilla::LauncherVoidResult TestImageTimestampChange() { // This should reset the timestamps and then essentially run like // TestEmptyRegistry - VoidResult vr = SetImageTimestamp(0x12345678); + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); if (vr.isErr()) { return vr; } - - vr = SetLauncherTimestamp(1ULL); + vr = WriteRegistryValueData(gImageValue, REG_DWORD, 0x12345678); if (vr.isErr()) { return vr; } - - vr = SetBrowserTimestamp(2ULL); + vr = WriteRegistryValueData(gLauncherValue, REG_QWORD, 1ULL); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData(gBrowserValue, REG_QWORD, 2ULL); if (vr.isErr()) { return vr; } mozilla::LauncherRegistryInfo info; - mozilla::LauncherResult result = - info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher); - if (result.isErr()) { - return mozilla::Err(result.unwrapErr().mError); - } + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + EXPECT_COMMIT_IS_OK(); - if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Launcher) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } - - QWordResult launcherTs = GetLauncherTimestamp(); - if (launcherTs.isErr()) { - return mozilla::Err(launcherTs.unwrapErr()); - } - - DWordResult imageTs = GetImageTimestamp(); - if (imageTs.isErr()) { - return mozilla::Err(imageTs.unwrapErr()); - } - - if (imageTs.unwrap() != gMyImageTimestamp) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } - - QWordResult browserTs = GetBrowserTimestamp(); - if (browserTs.isOk()) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED)); - } - - if (browserTs.unwrapErr() != - mozilla::WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) { - return mozilla::Err(browserTs.unwrapErr()); - } + EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp); + EXPECT_REG_QWORD_EXISTS(gLauncherValue); + EXPECT_REG_QWORD_DOES_NOT_EXIST(gBrowserValue); return mozilla::Ok(); } -static VoidResult TestImageTimestampChangeWhenDisabled() { - VoidResult vr = SetImageTimestamp(0x12345678); +static mozilla::LauncherVoidResult TestImageTimestampChangeWhenDisabled() { + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); if (vr.isErr()) { return vr; } - - vr = DeleteLauncherTimestamp(); + vr = WriteRegistryValueData(gImageValue, REG_DWORD, 0x12345678); if (vr.isErr()) { return vr; } - - vr = SetBrowserTimestamp(0ULL); + vr = WriteRegistryValueData(gBrowserValue, REG_QWORD, 0ULL); if (vr.isErr()) { return vr; } mozilla::LauncherRegistryInfo info; - mozilla::LauncherResult result = - info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher); - if (result.isErr()) { - return mozilla::Err(result.unwrapErr().mError); - } + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); - if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } + EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp); + EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0); - mozilla::LauncherResult enabled = - info.IsEnabled(); - if (enabled.isErr()) { - return mozilla::Err(enabled.unwrapErr().mError); - } - - if (enabled.unwrap() != - mozilla::LauncherRegistryInfo::EnabledState::ForceDisabled) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED)); - } + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); return mozilla::Ok(); } -static VoidResult TestDisableDueToFailure() { - VoidResult vr = SetupEnabledScenario(); +static mozilla::LauncherVoidResult TestDisableDueToFailure() { + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); if (vr.isErr()) { return vr; } // Check that we are indeed enabled. mozilla::LauncherRegistryInfo info; - mozilla::LauncherResult enabled = - info.IsEnabled(); - if (enabled.isErr()) { - return mozilla::Err(enabled.unwrapErr().mError); - } - - if (enabled.unwrap() != - mozilla::LauncherRegistryInfo::EnabledState::Enabled) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } + EXPECT_ENABLED_STATE_IS(EnabledState::Enabled); // Now call DisableDueToFailure mozilla::LauncherVoidResult lvr = info.DisableDueToFailure(); if (lvr.isErr()) { - return mozilla::Err(lvr.unwrapErr().mError); + return LAUNCHER_ERROR_FROM_RESULT(lvr); } // We should now be FailDisabled - enabled = info.IsEnabled(); - if (enabled.isErr()) { - return mozilla::Err(enabled.unwrapErr().mError); - } - - if (enabled.unwrap() != - mozilla::LauncherRegistryInfo::EnabledState::FailDisabled) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); // If we delete the launcher timestamp, IsEnabled should then return // ForceDisabled. - vr = DeleteLauncherTimestamp(); + vr = DeleteRegistryValueData(gLauncherValue); if (vr.isErr()) { return vr; } - - enabled = info.IsEnabled(); - if (enabled.isErr()) { - return mozilla::Err(enabled.unwrapErr().mError); - } - - if (enabled.unwrap() != - mozilla::LauncherRegistryInfo::EnabledState::ForceDisabled) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); return mozilla::Ok(); } -static VoidResult TestPrefReflection() { +static mozilla::LauncherVoidResult TestPrefReflection() { // Reset the registry to a known good state. - VoidResult vr = SetupEnabledScenario(); + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); if (vr.isErr()) { return vr; } @@ -566,79 +479,251 @@ static VoidResult TestPrefReflection() { mozilla::LauncherRegistryInfo info; mozilla::LauncherVoidResult reflectOk = info.ReflectPrefToRegistry(false); if (reflectOk.isErr()) { - return mozilla::Err(reflectOk.unwrapErr().mError); + return LAUNCHER_ERROR_FROM_RESULT(reflectOk); } // Launcher timestamp should be non-existent. - QWordResult launcherTs = GetLauncherTimestamp(); - if (launcherTs.isOk()) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED)); - } - - if (launcherTs.unwrapErr() != - mozilla::WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) { - return mozilla::Err(launcherTs.unwrapErr()); - } - + EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue); // Browser timestamp should be zero - QWordResult browserTs = GetBrowserTimestamp(); - if (browserTs.isErr()) { - return mozilla::Err(browserTs.unwrapErr()); - } - - if (browserTs.unwrap() != 0ULL) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } - + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL); // IsEnabled should give us ForceDisabled - mozilla::LauncherResult enabled = - info.IsEnabled(); - if (enabled.isErr()) { - return mozilla::Err(enabled.unwrapErr().mError); - } - - if (enabled.unwrap() != - mozilla::LauncherRegistryInfo::EnabledState::ForceDisabled) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); - } + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); // Now test to see what happens when the pref is set to ON. reflectOk = info.ReflectPrefToRegistry(true); if (reflectOk.isErr()) { - return mozilla::Err(reflectOk.unwrapErr().mError); + return LAUNCHER_ERROR_FROM_RESULT(reflectOk); } // Launcher and browser timestamps should be non-existent. - launcherTs = GetLauncherTimestamp(); - if (launcherTs.isOk()) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED)); - } - - if (launcherTs.unwrapErr() != - mozilla::WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) { - return mozilla::Err(launcherTs.unwrapErr()); - } - - browserTs = GetBrowserTimestamp(); - if (browserTs.isOk()) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED)); - } - - if (browserTs.unwrapErr() != - mozilla::WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) { - return mozilla::Err(browserTs.unwrapErr()); - } + EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue); + EXPECT_REG_QWORD_DOES_NOT_EXIST(gBrowserValue); // IsEnabled should give us Enabled. - enabled = info.IsEnabled(); - if (enabled.isErr()) { - return mozilla::Err(enabled.unwrapErr().mError); + EXPECT_ENABLED_STATE_IS(EnabledState::Enabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestTelemetryConfig() { + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); + if (vr.isErr()) { + return vr; } - if (enabled.unwrap() != - mozilla::LauncherRegistryInfo::EnabledState::Enabled) { - return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL)); + mozilla::LauncherRegistryInfo info; + EXPECT_TELEMETRY_IS_ENABLED(false); + + mozilla::LauncherVoidResult reflectOk = + info.ReflectTelemetryPrefToRegistry(false); + if (reflectOk.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(reflectOk); } + EXPECT_TELEMETRY_IS_ENABLED(false); + + reflectOk = info.ReflectTelemetryPrefToRegistry(true); + if (reflectOk.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(reflectOk); + } + EXPECT_TELEMETRY_IS_ENABLED(true); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestCommitAbort() { + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); + if (vr.isErr()) { + return vr; + } + + // Retrieve the current timestamps to compare later + mozilla::LauncherResult> launcherValue = + ReadRegistryValueData(gLauncherValue, REG_QWORD); + if (launcherValue.isErr() || launcherValue.inspect().isNothing()) { + return LAUNCHER_ERROR_FROM_RESULT(launcherValue); + } + mozilla::LauncherResult> browserValue = + ReadRegistryValueData(gBrowserValue, REG_QWORD); + if (browserValue.isErr() || browserValue.inspect().isNothing()) { + return LAUNCHER_ERROR_FROM_RESULT(browserValue); + } + uint64_t launcherTs = launcherValue.inspect().value(); + uint64_t browserTs = browserValue.inspect().value(); + + vr = []() -> mozilla::LauncherVoidResult { + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + // No commit + return mozilla::Ok(); + }(); + if (vr.isErr()) { + return vr; + } + + // Exiting the scope discards the change. + mozilla::LauncherRegistryInfo info; + EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, browserTs); + + // Commit -> Check -> Abort -> Commit + EXPECT_COMMIT_IS_OK(); + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + info.Abort(); + EXPECT_COMMIT_IS_OK(); + + // Nothing is changed. + EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, browserTs); + EXPECT_ENABLED_STATE_IS(EnabledState::Enabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestDisableDuringLauncherLaunch() { + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); + if (vr.isErr()) { + return vr; + } + + mozilla::LauncherResult> launcherTs = + ReadRegistryValueData(gLauncherValue, REG_QWORD); + if (launcherTs.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(launcherTs); + } + if (launcherTs.inspect().isNothing()) { + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); + } + + vr = []() -> mozilla::LauncherVoidResult { + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + + // Call DisableDueToFailure with a different instance + mozilla::LauncherVoidResult vr = []() -> mozilla::LauncherVoidResult { + mozilla::LauncherRegistryInfo info; + mozilla::LauncherVoidResult vr = info.DisableDueToFailure(); + if (vr.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(vr); + } + return mozilla::Ok(); + }(); + if (vr.isErr()) { + return vr; + } + + // Commit after disable. + EXPECT_COMMIT_IS_OK(); + + return mozilla::Ok(); + }(); + if (vr.isErr()) { + return vr; + } + + // Make sure we're still FailDisabled and the launcher's timestamp is not + // updated + mozilla::LauncherRegistryInfo info; + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs.inspect().value()); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestDisableDuringBrowserLaunch() { + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); + if (vr.isErr()) { + return vr; + } + + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + EXPECT_COMMIT_IS_OK(); + + vr = []() -> mozilla::LauncherVoidResult { + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Browser, ProcessType::Browser); + + // Call DisableDueToFailure with a different instance + mozilla::LauncherVoidResult vr = []() -> mozilla::LauncherVoidResult { + mozilla::LauncherRegistryInfo info; + mozilla::LauncherVoidResult vr = info.DisableDueToFailure(); + if (vr.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(vr); + } + return mozilla::Ok(); + }(); + if (vr.isErr()) { + return vr; + } + + // Commit after disable. + EXPECT_COMMIT_IS_OK(); + + return mozilla::Ok(); + }(); + if (vr.isErr()) { + return vr; + } + + // Make sure we're still FailDisabled + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestReEnable() { + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); + if (vr.isErr()) { + return vr; + } + + // Make FailDisabled + mozilla::LauncherRegistryInfo info; + vr = info.DisableDueToFailure(); + if (vr.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(vr); + } + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); + + // Attempt to launch when FailDisabled: Still be FailDisabled + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); + + // Change the timestamp + vr = WriteRegistryValueData(gImageValue, REG_DWORD, 0x12345678); + if (vr.isErr()) { + return vr; + } + + // Attempt to launch again: Launcher comes back + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + EXPECT_COMMIT_IS_OK(); + + // Make ForceDisabled + vr = info.ReflectPrefToRegistry(false); + if (vr.isErr()) { + return LAUNCHER_ERROR_FROM_RESULT(vr); + } + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); + + // Attempt to launch when ForceDisabled: Still be ForceDisabled + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); + + // Change the timestamp + vr = WriteRegistryValueData(gImageValue, REG_DWORD, 0x12345678); + if (vr.isErr()) { + return vr; + } + + // Attempt to launch again: Still be ForceDisabled + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); return mozilla::Ok(); } @@ -659,29 +744,34 @@ int main(int argc, char* argv[]) { gImageValue = fullPath.get(); gImageValue += kImageSuffix; - mozilla::nt::PEHeaders myHeaders(::GetModuleHandleW(nullptr)); - if (!myHeaders) { - return 1; - } + gTelemetryValue = fullPath.get(); + gTelemetryValue += kTelemetrySuffix; - if (!myHeaders.GetTimeStamp(gMyImageTimestamp)) { - return 1; - } + mozilla::LauncherResult timestamp = 0; + RUN_TEST(timestamp, GetCurrentImageTimestamp); + gMyImageTimestamp = timestamp.unwrap(); auto onExit = mozilla::MakeScopeExit( - []() { mozilla::Unused << DeleteAllTimestamps(); }); + []() { mozilla::Unused << DeleteAllRegstryValues(); }); - VoidResult vr = mozilla::Ok(); + mozilla::LauncherVoidResult vr = mozilla::Ok(); - RUN_TEST(TestEmptyRegistry); - RUN_TEST(TestNormal); - RUN_TEST(TestBrowserNoLauncher); - RUN_TEST(TestLauncherNoBrowser); - RUN_TEST(TestBrowserLessThanLauncher); - RUN_TEST(TestImageTimestampChange); - RUN_TEST(TestImageTimestampChangeWhenDisabled); - RUN_TEST(TestDisableDueToFailure); - RUN_TEST(TestPrefReflection); + // All testcases should call SetupEnabledScenario() or + // DeleteAllRegstryValues() to be order-independent + RUN_TEST(vr, TestEmptyRegistry); + RUN_TEST(vr, TestNormal); + RUN_TEST(vr, TestBrowserNoLauncher); + RUN_TEST(vr, TestLauncherNoBrowser); + RUN_TEST(vr, TestBrowserLessThanLauncher); + RUN_TEST(vr, TestImageTimestampChange); + RUN_TEST(vr, TestImageTimestampChangeWhenDisabled); + RUN_TEST(vr, TestDisableDueToFailure); + RUN_TEST(vr, TestPrefReflection); + RUN_TEST(vr, TestTelemetryConfig); + RUN_TEST(vr, TestCommitAbort); + RUN_TEST(vr, TestDisableDuringLauncherLaunch); + RUN_TEST(vr, TestDisableDuringBrowserLaunch); + RUN_TEST(vr, TestReEnable); return 0; }