mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Bug 1511078: Add LauncherRegistryInfo as a temporary mechanism for runtime disabling of launcher process; r=mhowell
Differential Revision: https://phabricator.services.mozilla.com/D15756 --HG-- rename : browser/app/winlauncher/NativeNt.h => mozglue/misc/NativeNt.h rename : browser/app/winlauncher/test/TestNativeNt.cpp => mozglue/tests/TestNativeNt.cpp rename : browser/app/winlauncher/LauncherResult.h => toolkit/xre/LauncherResult.h extra : moz-landing-system : lando
This commit is contained in:
parent
5215f57b1d
commit
99ef8003dd
@ -4,10 +4,10 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "NativeNt.h"
|
||||
#include "nsWindowsDllInterceptor.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/NativeNt.h"
|
||||
#include "mozilla/Types.h"
|
||||
#include "mozilla/WindowsDllBlocklist.h"
|
||||
#include "mozilla/WinHeaderOnlyUtils.h"
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "LauncherResult.h"
|
||||
#include "mozilla/LauncherResult.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -6,11 +6,18 @@
|
||||
|
||||
#include "ErrorHandler.h"
|
||||
|
||||
#include "mozilla/LauncherRegistryInfo.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void HandleLauncherError(const LauncherError& aError) {
|
||||
// This is a placeholder error handler. We'll add telemetry and a fallback
|
||||
// error log in future revisions.
|
||||
|
||||
LauncherRegistryInfo regInfo;
|
||||
Unused << regInfo.DisableDueToFailure();
|
||||
|
||||
WindowsError::UniqueString msg = aError.mError.AsString();
|
||||
if (!msg) {
|
||||
return;
|
||||
|
@ -8,8 +8,7 @@
|
||||
#define mozilla_ErrorHandler_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
#include "LauncherResult.h"
|
||||
#include "mozilla/LauncherResult.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -8,12 +8,11 @@
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/CmdLineAndEnvUtils.h"
|
||||
#include "mozilla/LauncherResult.h"
|
||||
#include "mozilla/mscom/COMApartmentRegion.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
|
||||
#include "LauncherResult.h"
|
||||
|
||||
// For _bstr_t and _variant_t
|
||||
#include <comdef.h>
|
||||
#include <comutil.h>
|
||||
|
@ -8,7 +8,7 @@
|
||||
#define mozilla_LaunchUnelevated_h
|
||||
|
||||
#include "LauncherProcessWin.h"
|
||||
#include "LauncherResult.h"
|
||||
#include "mozilla/LauncherResult.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include "mozilla/CmdLineAndEnvUtils.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/DynamicallyLinkedFunctionPtr.h"
|
||||
#include "mozilla/LauncherRegistryInfo.h"
|
||||
#include "mozilla/LauncherResult.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/SafeMode.h"
|
||||
#include "mozilla/Sprintf.h" // For printf_stderr
|
||||
@ -26,7 +28,6 @@
|
||||
|
||||
#include "DllBlocklistWin.h"
|
||||
#include "ErrorHandler.h"
|
||||
#include "LauncherResult.h"
|
||||
#include "LaunchUnelevated.h"
|
||||
#include "ProcThreadAttributes.h"
|
||||
|
||||
@ -38,7 +39,7 @@
|
||||
* At this point the child process has been created in a suspended state. Any
|
||||
* additional startup work (eg, blocklist setup) should go here.
|
||||
*
|
||||
* @return true if browser startup should proceed, otherwise false.
|
||||
* @return Ok if browser startup should proceed
|
||||
*/
|
||||
static mozilla::LauncherVoidResult PostCreationSetup(HANDLE aChildProcess,
|
||||
HANDLE aChildMainThread,
|
||||
@ -144,16 +145,14 @@ static void MaybeBreakForBrowserDebugging() {
|
||||
::Sleep(pauseLenMs);
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
bool RunAsLauncherProcess(int& argc, wchar_t** argv) {
|
||||
static bool DoLauncherProcessChecks(int& argc, wchar_t** argv) {
|
||||
// NB: We run all tests in this function instead of returning early in order
|
||||
// to ensure that all side effects take place, such as clearing environment
|
||||
// variables.
|
||||
bool result = false;
|
||||
|
||||
#if defined(MOZ_LAUNCHER_PROCESS)
|
||||
LauncherResult<bool> isSame = IsSameBinaryAsParentProcess();
|
||||
mozilla::LauncherResult<bool> isSame = mozilla::IsSameBinaryAsParentProcess();
|
||||
if (isSame.isOk()) {
|
||||
result = !isSame.unwrap();
|
||||
} else {
|
||||
@ -166,17 +165,47 @@ bool RunAsLauncherProcess(int& argc, wchar_t** argv) {
|
||||
result = true;
|
||||
}
|
||||
|
||||
result |=
|
||||
CheckArg(argc, argv, L"launcher", static_cast<const wchar_t**>(nullptr),
|
||||
CheckArgFlag::RemoveArg) == ARG_FOUND;
|
||||
result |= mozilla::CheckArg(
|
||||
argc, argv, L"launcher", static_cast<const wchar_t**>(nullptr),
|
||||
mozilla::CheckArgFlag::RemoveArg) == mozilla::ARG_FOUND;
|
||||
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
bool RunAsLauncherProcess(int& argc, wchar_t** argv) {
|
||||
LauncherRegistryInfo::ProcessType desiredType =
|
||||
DoLauncherProcessChecks(argc, argv)
|
||||
? LauncherRegistryInfo::ProcessType::Launcher
|
||||
: LauncherRegistryInfo::ProcessType::Browser;
|
||||
|
||||
// If we're looking at browser, return fast when we're a child process.
|
||||
if (desiredType == LauncherRegistryInfo::ProcessType::Browser &&
|
||||
mozilla::CheckArg(argc, argv, L"contentproc",
|
||||
static_cast<const wchar_t**>(nullptr),
|
||||
mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LauncherRegistryInfo regInfo;
|
||||
LauncherResult<LauncherRegistryInfo::ProcessType> runAsType =
|
||||
regInfo.Check(desiredType);
|
||||
|
||||
if (runAsType.isErr()) {
|
||||
HandleLauncherError(runAsType);
|
||||
// If there is an error, we should always fall back to returning false
|
||||
// for safety's sake.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (runAsType.unwrap() == LauncherRegistryInfo::ProcessType::Browser) {
|
||||
// In this case, we will be proceeding to run as the browser.
|
||||
// We should check MOZ_DEBUG_BROWSER_* env vars.
|
||||
MaybeBreakForBrowserDebugging();
|
||||
}
|
||||
|
||||
return result;
|
||||
return runAsType.unwrap() == LauncherRegistryInfo::ProcessType::Launcher;
|
||||
}
|
||||
|
||||
int LauncherMain(int argc, wchar_t* argv[]) {
|
||||
|
@ -7,8 +7,8 @@
|
||||
#ifndef mozilla_SameBinary_h
|
||||
#define mozilla_SameBinary_h
|
||||
|
||||
#include "LauncherResult.h"
|
||||
#include "NativeNt.h"
|
||||
#include "mozilla/LauncherResult.h"
|
||||
#include "mozilla/NativeNt.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -9,6 +9,7 @@ Library('winlauncher')
|
||||
FORCE_STATIC_LIB = True
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'/toolkit/xre/LauncherRegistryInfo.cpp',
|
||||
'DllBlocklistWin.cpp',
|
||||
'ErrorHandler.cpp',
|
||||
'LauncherProcessWin.cpp',
|
||||
@ -25,6 +26,9 @@ TEST_DIRS += [
|
||||
'test',
|
||||
]
|
||||
|
||||
for var in ('MOZ_APP_BASENAME', 'MOZ_APP_VENDOR'):
|
||||
DEFINES[var] = '"%s"' % CONFIG[var]
|
||||
|
||||
DisableStlWrapping()
|
||||
|
||||
if CONFIG['CC_TYPE'] == 'clang-cl':
|
||||
|
@ -4,13 +4,13 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "NativeNt.h"
|
||||
#include "SameBinary.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/CmdLineAndEnvUtils.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/NativeNt.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include "mozilla/WinHeaderOnlyUtils.h"
|
||||
|
@ -8,7 +8,6 @@ DisableStlWrapping()
|
||||
|
||||
GeckoCppUnitTests(
|
||||
[
|
||||
'TestNativeNt',
|
||||
'TestSameBinary',
|
||||
],
|
||||
linkage=None
|
||||
|
@ -7,11 +7,6 @@
|
||||
#ifndef mozilla_NativeNt_h
|
||||
#define mozilla_NativeNt_h
|
||||
|
||||
#if defined(MOZILLA_INTERNAL_API)
|
||||
#error \
|
||||
"This header is for initial process initialization. You don't want to be including this here."
|
||||
#endif // defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
#include <winnt.h>
|
||||
@ -19,8 +14,12 @@
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/LauncherResult.h"
|
||||
|
||||
#include "LauncherResult.h"
|
||||
// The declarations within this #if block are intended to be used for initial
|
||||
// process initialization ONLY. You probably don't want to be using these in
|
||||
// normal Gecko code!
|
||||
#if !defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
extern "C" {
|
||||
|
||||
@ -80,9 +79,13 @@ VOID NTAPI RtlReleaseSRWLockExclusive(PSRWLOCK aLock);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // !defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
namespace mozilla {
|
||||
namespace nt {
|
||||
|
||||
#if !defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
struct MemorySectionNameBuf : public _MEMORY_SECTION_NAME {
|
||||
MemorySectionNameBuf() {
|
||||
mSectionFileName.Length = 0;
|
||||
@ -202,6 +205,8 @@ inline void GetLeafName(PUNICODE_STRING aDestString,
|
||||
aDestString->MaximumLength = aDestString->Length;
|
||||
}
|
||||
|
||||
#endif // !defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
inline char EnsureLowerCaseASCII(char aChar) {
|
||||
if (aChar >= 'A' && aChar <= 'Z') {
|
||||
aChar -= 'A' - 'a';
|
@ -38,6 +38,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
]
|
||||
EXPORTS.mozilla += [
|
||||
'DynamicallyLinkedFunctionPtr.h',
|
||||
'NativeNt.h',
|
||||
'WindowsMapRemoteView.h',
|
||||
]
|
||||
EXPORTS.mozilla.interceptor += [
|
||||
|
@ -4,7 +4,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "NativeNt.h"
|
||||
#include "mozilla/NativeNt.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@ -38,7 +38,7 @@ const char kFailFmt[] =
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::nt;
|
||||
|
||||
extern "C" int wmain(int argc, wchar_t* argv[]) {
|
||||
int main(int argc, char* argv[]) {
|
||||
UNICODE_STRING normal;
|
||||
::RtlInitUnicodeString(&normal, kNormal);
|
||||
|
@ -15,7 +15,14 @@ CppUnitTests([
|
||||
])
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
GeckoCppUnitTests([
|
||||
'TestNativeNt',
|
||||
], linkage=None)
|
||||
TEST_DIRS += [
|
||||
'interceptor',
|
||||
'gtest',
|
||||
]
|
||||
OS_LIBS += [
|
||||
'mincore',
|
||||
'ntdll',
|
||||
]
|
||||
|
@ -359,11 +359,7 @@ inline UniquePtr<wchar_t[]> MakeCommandLine(int argc, wchar_t** argv,
|
||||
return s;
|
||||
}
|
||||
|
||||
inline bool SetArgv0ToFullBinaryPath(wchar_t* aArgv[]) {
|
||||
if (!aArgv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline UniquePtr<wchar_t[]> GetFullBinaryPath() {
|
||||
DWORD bufLen = MAX_PATH;
|
||||
mozilla::UniquePtr<wchar_t[]> buf;
|
||||
DWORD retLen;
|
||||
@ -372,7 +368,7 @@ inline bool SetArgv0ToFullBinaryPath(wchar_t* aArgv[]) {
|
||||
buf = mozilla::MakeUnique<wchar_t[]>(bufLen);
|
||||
retLen = ::GetModuleFileNameW(nullptr, buf.get(), bufLen);
|
||||
if (!retLen) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (retLen == bufLen && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
@ -388,8 +384,21 @@ inline bool SetArgv0ToFullBinaryPath(wchar_t* aArgv[]) {
|
||||
|
||||
// Since we're likely to have a bunch of unused space in buf, let's reallocate
|
||||
// a string to the actual size of the file name.
|
||||
auto newArgv_0 = mozilla::MakeUnique<wchar_t[]>(retLen);
|
||||
if (wcscpy_s(newArgv_0.get(), retLen, buf.get())) {
|
||||
auto result = mozilla::MakeUnique<wchar_t[]>(retLen);
|
||||
if (wcscpy_s(result.get(), retLen, buf.get())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool SetArgv0ToFullBinaryPath(wchar_t* aArgv[]) {
|
||||
if (!aArgv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UniquePtr<wchar_t[]> newArgv_0(GetFullBinaryPath());
|
||||
if (!newArgv_0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
409
toolkit/xre/LauncherRegistryInfo.cpp
Normal file
409
toolkit/xre/LauncherRegistryInfo.cpp
Normal file
@ -0,0 +1,409 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "LauncherRegistryInfo.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/CmdLineAndEnvUtils.h"
|
||||
#include "mozilla/NativeNt.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#define EXPAND_STRING_MACRO2(t) t
|
||||
#define EXPAND_STRING_MACRO(t) EXPAND_STRING_MACRO2(t)
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
const wchar_t LauncherRegistryInfo::kLauncherSubKeyPath[] =
|
||||
L"SOFTWARE\\" EXPAND_STRING_MACRO(MOZ_APP_VENDOR) L"\\" EXPAND_STRING_MACRO(
|
||||
MOZ_APP_BASENAME) L"\\Launcher";
|
||||
const wchar_t LauncherRegistryInfo::kLauncherSuffix[] = L"|Launcher";
|
||||
const wchar_t LauncherRegistryInfo::kBrowserSuffix[] = L"|Browser";
|
||||
const wchar_t LauncherRegistryInfo::kImageTimestampSuffix[] = L"|Image";
|
||||
|
||||
LauncherResult<LauncherRegistryInfo::Disposition> LauncherRegistryInfo::Open() {
|
||||
if (!!mRegKey) {
|
||||
return Disposition::OpenedExisting;
|
||||
}
|
||||
|
||||
DWORD disposition;
|
||||
HKEY rawKey;
|
||||
LSTATUS result = ::RegCreateKeyExW(
|
||||
HKEY_CURRENT_USER, kLauncherSubKeyPath, 0, nullptr,
|
||||
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &rawKey, &disposition);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
return LAUNCHER_ERROR_FROM_WIN32(result);
|
||||
}
|
||||
|
||||
mRegKey.own(rawKey);
|
||||
|
||||
switch (disposition) {
|
||||
case REG_CREATED_NEW_KEY:
|
||||
return Disposition::CreatedNew;
|
||||
case REG_OPENED_EXISTING_KEY:
|
||||
return Disposition::OpenedExisting;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid disposition from RegCreateKeyExW");
|
||||
return LAUNCHER_ERROR_GENERIC();
|
||||
}
|
||||
|
||||
LauncherVoidResult LauncherRegistryInfo::ReflectPrefToRegistry(
|
||||
const bool aEnable) {
|
||||
LauncherResult<Disposition> disposition = Open();
|
||||
if (disposition.isErr()) {
|
||||
return LAUNCHER_ERROR_FROM_RESULT(disposition);
|
||||
}
|
||||
|
||||
// Always delete the launcher timestamp
|
||||
LauncherResult<bool> clearedLauncherTimestamp =
|
||||
ClearStartTimestamp(ProcessType::Launcher);
|
||||
MOZ_ASSERT(clearedLauncherTimestamp.isOk());
|
||||
if (clearedLauncherTimestamp.isErr()) {
|
||||
return LAUNCHER_ERROR_FROM_RESULT(clearedLauncherTimestamp);
|
||||
}
|
||||
|
||||
if (!aEnable) {
|
||||
// Set the browser timestamp to 0 to indicate force-disabled
|
||||
return WriteStartTimestamp(ProcessType::Browser, Some(0ULL));
|
||||
}
|
||||
|
||||
// Otherwise we delete the browser timestamp to start over fresh
|
||||
LauncherResult<bool> clearedBrowserTimestamp =
|
||||
ClearStartTimestamp(ProcessType::Browser);
|
||||
MOZ_ASSERT(clearedBrowserTimestamp.isOk());
|
||||
if (clearedBrowserTimestamp.isErr()) {
|
||||
return LAUNCHER_ERROR_FROM_RESULT(clearedBrowserTimestamp);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
LauncherResult<LauncherRegistryInfo::ProcessType> LauncherRegistryInfo::Check(
|
||||
const ProcessType aDesiredType) {
|
||||
LauncherResult<Disposition> disposition = Open();
|
||||
if (disposition.isErr()) {
|
||||
return LAUNCHER_ERROR_FROM_RESULT(disposition);
|
||||
}
|
||||
|
||||
LauncherResult<DWORD> ourImageTimestamp = GetCurrentImageTimestamp();
|
||||
if (ourImageTimestamp.isErr()) {
|
||||
return LAUNCHER_ERROR_FROM_RESULT(ourImageTimestamp);
|
||||
}
|
||||
|
||||
LauncherResult<DWORD> savedImageTimestamp = GetSavedImageTimestamp();
|
||||
if (savedImageTimestamp.isErr() &&
|
||||
savedImageTimestamp.unwrapErr() !=
|
||||
WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) {
|
||||
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.unwrap() != ourImageTimestamp.unwrap()) {
|
||||
LauncherVoidResult clearResult = ClearStartTimestamps();
|
||||
if (clearResult.isErr()) {
|
||||
return LAUNCHER_ERROR_FROM_RESULT(clearResult);
|
||||
}
|
||||
|
||||
LauncherVoidResult writeResult =
|
||||
WriteImageTimestamp(ourImageTimestamp.unwrap());
|
||||
if (writeResult.isErr()) {
|
||||
return LAUNCHER_ERROR_FROM_RESULT(writeResult);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessType typeToRunAs = aDesiredType;
|
||||
|
||||
if (disposition.unwrap() == 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;
|
||||
}
|
||||
|
||||
LauncherResult<uint64_t> lastLauncherTimestamp =
|
||||
GetStartTimestamp(ProcessType::Launcher);
|
||||
bool haveLauncherTs = lastLauncherTimestamp.isOk();
|
||||
|
||||
LauncherResult<uint64_t> lastBrowserTimestamp =
|
||||
GetStartTimestamp(ProcessType::Browser);
|
||||
bool haveBrowserTs = lastBrowserTimestamp.isOk();
|
||||
|
||||
if (haveLauncherTs != haveBrowserTs) {
|
||||
// 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) {
|
||||
// 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.unwrap() < lastBrowserTimestamp.unwrap();
|
||||
if (!areTimestampsOk) {
|
||||
typeToRunAs = ProcessType::Browser;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If we have neither timestamp, then we should try running as suggested
|
||||
// by |aDesiredType|.
|
||||
// We shouldn't really have this scenario unless we're going to be running
|
||||
// as the launcher process.
|
||||
MOZ_ASSERT(typeToRunAs == ProcessType::Launcher);
|
||||
// No change to typeToRunAs
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return typeToRunAs;
|
||||
}
|
||||
|
||||
LauncherVoidResult LauncherRegistryInfo::DisableDueToFailure() {
|
||||
LauncherResult<Disposition> disposition = Open();
|
||||
if (disposition.isErr()) {
|
||||
return LAUNCHER_ERROR_FROM_RESULT(disposition);
|
||||
}
|
||||
|
||||
return WriteStartTimestamp(ProcessType::Browser, Some(0ULL));
|
||||
}
|
||||
|
||||
LauncherResult<LauncherRegistryInfo::EnabledState>
|
||||
LauncherRegistryInfo::IsEnabled() {
|
||||
LauncherResult<Disposition> disposition = Open();
|
||||
if (disposition.isErr()) {
|
||||
return LAUNCHER_ERROR_FROM_RESULT(disposition);
|
||||
}
|
||||
|
||||
LauncherResult<uint64_t> launcherTimestamp =
|
||||
GetStartTimestamp(ProcessType::Launcher);
|
||||
|
||||
LauncherResult<uint64_t> 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.unwrap() == 0ULL;
|
||||
|
||||
if (isBrowserTimestampZero && launcherTimestamp.isErr()) {
|
||||
return EnabledState::ForceDisabled;
|
||||
}
|
||||
|
||||
if (launcherTimestamp.isOk() && browserTimestamp.isOk() &&
|
||||
launcherTimestamp.unwrap() < browserTimestamp.unwrap()) {
|
||||
return EnabledState::Enabled;
|
||||
}
|
||||
|
||||
if (launcherTimestamp.isErr() && browserTimestamp.isErr()) {
|
||||
return EnabledState::Enabled;
|
||||
}
|
||||
|
||||
return EnabledState::FailDisabled;
|
||||
}
|
||||
|
||||
LauncherResult<std::wstring> 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;
|
||||
}
|
||||
|
||||
return LAUNCHER_ERROR_GENERIC();
|
||||
}
|
||||
|
||||
std::wstring LauncherRegistryInfo::ResolveImageTimestampValueName() {
|
||||
if (mImageValueName.empty()) {
|
||||
mImageValueName.assign(mBinPath);
|
||||
mImageValueName.append(kImageTimestampSuffix,
|
||||
ArrayLength(kImageTimestampSuffix) - 1);
|
||||
}
|
||||
|
||||
return mImageValueName;
|
||||
}
|
||||
|
||||
LauncherVoidResult LauncherRegistryInfo::WriteStartTimestamp(
|
||||
LauncherRegistryInfo::ProcessType aProcessType, const Maybe<uint64_t>& aValue) {
|
||||
LauncherResult<std::wstring> 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<LARGE_INTEGER*>(×tamp))) {
|
||||
return LAUNCHER_ERROR_FROM_LAST();
|
||||
}
|
||||
}
|
||||
|
||||
DWORD len = sizeof(timestamp);
|
||||
LSTATUS result =
|
||||
::RegSetValueExW(mRegKey.get(), name.unwrap().c_str(), 0, REG_QWORD,
|
||||
reinterpret_cast<PBYTE>(×tamp), len);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
return LAUNCHER_ERROR_FROM_WIN32(result);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
LauncherResult<DWORD> 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::WriteImageTimestamp(DWORD aTimestamp) {
|
||||
std::wstring imageTimestampValueName = ResolveImageTimestampValueName();
|
||||
|
||||
DWORD len = sizeof(aTimestamp);
|
||||
LSTATUS result =
|
||||
::RegSetValueExW(mRegKey.get(), imageTimestampValueName.c_str(), 0,
|
||||
REG_DWORD, reinterpret_cast<PBYTE>(&aTimestamp), len);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
return LAUNCHER_ERROR_FROM_WIN32(result);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
LauncherResult<bool> LauncherRegistryInfo::ClearStartTimestamp(
|
||||
LauncherRegistryInfo::ProcessType aProcessType) {
|
||||
LauncherResult<std::wstring> timestampName = ResolveValueName(aProcessType);
|
||||
if (timestampName.isErr()) {
|
||||
return LAUNCHER_ERROR_FROM_RESULT(timestampName);
|
||||
}
|
||||
|
||||
LSTATUS result =
|
||||
::RegDeleteValueW(mRegKey.get(), timestampName.unwrap().c_str());
|
||||
if (result == ERROR_SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (result == ERROR_FILE_NOT_FOUND) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return LAUNCHER_ERROR_FROM_WIN32(result);
|
||||
}
|
||||
|
||||
LauncherVoidResult LauncherRegistryInfo::ClearStartTimestamps() {
|
||||
LauncherResult<uint64_t> lastBrowserTimestamp =
|
||||
GetStartTimestamp(ProcessType::Browser);
|
||||
if (lastBrowserTimestamp.isOk() && lastBrowserTimestamp.unwrap() == 0ULL) {
|
||||
// Only proceed when the browser timestamp is non-zero
|
||||
return Ok();
|
||||
}
|
||||
|
||||
LauncherResult<bool> clearedLauncherTimestamp =
|
||||
ClearStartTimestamp(ProcessType::Launcher);
|
||||
if (clearedLauncherTimestamp.isErr()) {
|
||||
return LAUNCHER_ERROR_FROM_RESULT(clearedLauncherTimestamp);
|
||||
}
|
||||
|
||||
LauncherResult<bool> clearedBrowserTimestamp =
|
||||
ClearStartTimestamp(ProcessType::Browser);
|
||||
if (clearedBrowserTimestamp.isErr()) {
|
||||
return LAUNCHER_ERROR_FROM_RESULT(clearedBrowserTimestamp);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
LauncherResult<DWORD> 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<PBYTE>(&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<uint64_t> LauncherRegistryInfo::GetStartTimestamp(
|
||||
LauncherRegistryInfo::ProcessType aProcessType) {
|
||||
LauncherResult<std::wstring> 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.unwrap().c_str(), nullptr, &type,
|
||||
reinterpret_cast<PBYTE>(&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;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
74
toolkit/xre/LauncherRegistryInfo.h
Normal file
74
toolkit/xre/LauncherRegistryInfo.h
Normal file
@ -0,0 +1,74 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_LauncherRegistryInfo_h
|
||||
#define mozilla_LauncherRegistryInfo_h
|
||||
|
||||
#include "mozilla/CmdLineAndEnvUtils.h"
|
||||
#include "mozilla/LauncherResult.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* We use std::wstring here because this code must be usable within both the
|
||||
* launcher process and Gecko itself.
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class LauncherRegistryInfo final {
|
||||
public:
|
||||
enum class ProcessType { Launcher, Browser };
|
||||
|
||||
enum class EnabledState {
|
||||
Enabled,
|
||||
FailDisabled,
|
||||
ForceDisabled,
|
||||
};
|
||||
|
||||
LauncherRegistryInfo() : mBinPath(GetFullBinaryPath().get()) {}
|
||||
|
||||
LauncherVoidResult ReflectPrefToRegistry(const bool aEnable);
|
||||
LauncherResult<EnabledState> IsEnabled();
|
||||
LauncherResult<ProcessType> Check(const ProcessType aDesiredType);
|
||||
LauncherVoidResult DisableDueToFailure();
|
||||
|
||||
private:
|
||||
enum class Disposition { CreatedNew, OpenedExisting };
|
||||
|
||||
private:
|
||||
LauncherResult<Disposition> Open();
|
||||
LauncherVoidResult WriteStartTimestamp(ProcessType aProcessType,
|
||||
const Maybe<uint64_t>& aValue = Nothing());
|
||||
LauncherResult<DWORD> GetCurrentImageTimestamp();
|
||||
LauncherVoidResult WriteImageTimestamp(DWORD aTimestamp);
|
||||
LauncherResult<bool> ClearStartTimestamp(ProcessType aProcessType);
|
||||
LauncherVoidResult ClearStartTimestamps();
|
||||
LauncherResult<DWORD> GetSavedImageTimestamp();
|
||||
LauncherResult<uint64_t> GetStartTimestamp(ProcessType aProcessType);
|
||||
|
||||
LauncherResult<std::wstring> ResolveValueName(ProcessType aProcessType);
|
||||
std::wstring ResolveImageTimestampValueName();
|
||||
|
||||
private:
|
||||
nsAutoRegKey mRegKey;
|
||||
std::wstring mBinPath;
|
||||
std::wstring mImageValueName;
|
||||
std::wstring mBrowserValueName;
|
||||
std::wstring mLauncherValueName;
|
||||
|
||||
static const wchar_t kLauncherSubKeyPath[];
|
||||
static const wchar_t kLauncherSuffix[];
|
||||
static const wchar_t kBrowserSuffix[];
|
||||
static const wchar_t kImageTimestampSuffix[];
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_LauncherRegistryInfo_h
|
@ -12,6 +12,13 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#if defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
template <typename T>
|
||||
using LauncherResult = WindowsErrorResult<T>;
|
||||
|
||||
#else
|
||||
|
||||
struct LauncherError {
|
||||
LauncherError(const char* aFile, int aLine, WindowsError aWin32Error)
|
||||
: mFile(aFile), mLine(aLine), mError(aWin32Error) {}
|
||||
@ -19,15 +26,48 @@ struct LauncherError {
|
||||
const char* mFile;
|
||||
int mLine;
|
||||
WindowsError mError;
|
||||
|
||||
bool operator==(const LauncherError& aOther) const {
|
||||
return mError == aOther.mError;
|
||||
}
|
||||
|
||||
bool operator!=(const LauncherError& aOther) const {
|
||||
return mError != aOther.mError;
|
||||
}
|
||||
|
||||
bool operator==(const WindowsError& aOther) const { return mError == aOther; }
|
||||
|
||||
bool operator!=(const WindowsError& aOther) const { return mError != aOther; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using LauncherResult = Result<T, LauncherError>;
|
||||
|
||||
using LauncherVoidResult = Result<Ok, LauncherError>;
|
||||
#endif // defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
using LauncherVoidResult = LauncherResult<Ok>;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#if defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
#define LAUNCHER_ERROR_GENERIC() \
|
||||
::mozilla::Err(::mozilla::WindowsError::CreateGeneric())
|
||||
|
||||
#define LAUNCHER_ERROR_FROM_WIN32(err) \
|
||||
::mozilla::Err(::mozilla::WindowsError::FromWin32Error(err))
|
||||
|
||||
#define LAUNCHER_ERROR_FROM_LAST() \
|
||||
::mozilla::Err(::mozilla::WindowsError::FromLastError())
|
||||
|
||||
#define LAUNCHER_ERROR_FROM_NTSTATUS(ntstatus) \
|
||||
::mozilla::Err(::mozilla::WindowsError::FromNtStatus(ntstatus))
|
||||
|
||||
#define LAUNCHER_ERROR_FROM_HRESULT(hresult) \
|
||||
::mozilla::Err(::mozilla::WindowsError::FromHResult(hresult))
|
||||
|
||||
#else
|
||||
|
||||
#define LAUNCHER_ERROR_GENERIC() \
|
||||
::mozilla::Err(::mozilla::LauncherError( \
|
||||
__FILE__, __LINE__, ::mozilla::WindowsError::CreateGeneric()))
|
||||
@ -38,8 +78,7 @@ using LauncherVoidResult = Result<Ok, LauncherError>;
|
||||
|
||||
#define LAUNCHER_ERROR_FROM_LAST() \
|
||||
::mozilla::Err(::mozilla::LauncherError( \
|
||||
__FILE__, __LINE__, \
|
||||
::mozilla::WindowsError::FromWin32Error(::GetLastError())))
|
||||
__FILE__, __LINE__, ::mozilla::WindowsError::FromLastError()))
|
||||
|
||||
#define LAUNCHER_ERROR_FROM_NTSTATUS(ntstatus) \
|
||||
::mozilla::Err(::mozilla::LauncherError( \
|
||||
@ -49,12 +88,14 @@ using LauncherVoidResult = Result<Ok, LauncherError>;
|
||||
::mozilla::Err(::mozilla::LauncherError( \
|
||||
__FILE__, __LINE__, ::mozilla::WindowsError::FromHResult(hresult)))
|
||||
|
||||
// This macro enables moving of a mozilla::LauncherError from a
|
||||
// mozilla::LauncherResult<Foo> into a mozilla::LauncherResult<Bar>
|
||||
#define LAUNCHER_ERROR_FROM_RESULT(result) ::mozilla::Err(result.unwrapErr())
|
||||
|
||||
// This macro wraps the supplied WindowsError with a LauncherError
|
||||
#define LAUNCHER_ERROR_FROM_MOZ_WINDOWS_ERROR(err) \
|
||||
::mozilla::Err(::mozilla::LauncherError(__FILE__, __LINE__, err))
|
||||
|
||||
#endif // defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
// This macro enables moving of a mozilla::LauncherError from a
|
||||
// mozilla::LauncherResult<Foo> into a mozilla::LauncherResult<Bar>
|
||||
#define LAUNCHER_ERROR_FROM_RESULT(result) ::mozilla::Err(result.unwrapErr())
|
||||
|
||||
#endif // mozilla_LauncherResult_h
|
@ -59,6 +59,14 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
LOCAL_INCLUDES += [
|
||||
'../components/printingui',
|
||||
]
|
||||
if CONFIG['MOZ_LAUNCHER_PROCESS']:
|
||||
EXPORTS.mozilla += [
|
||||
'LauncherRegistryInfo.h',
|
||||
'LauncherResult.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'LauncherRegistryInfo.cpp',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
UNIFIED_SOURCES += [
|
||||
'MacApplicationDelegate.mm',
|
||||
@ -156,7 +164,8 @@ if CONFIG['MOZ_X11']:
|
||||
DEFINES['USE_GLX_TEST'] = True
|
||||
|
||||
for var in ('MOZ_APP_NAME', 'MOZ_APP_BASENAME', 'MOZ_APP_DISPLAYNAME',
|
||||
'MOZ_APP_VERSION', 'OS_TARGET', 'MOZ_WIDGET_TOOLKIT'):
|
||||
'MOZ_APP_VENDOR', 'MOZ_APP_VERSION', 'OS_TARGET',
|
||||
'MOZ_WIDGET_TOOLKIT'):
|
||||
DEFINES[var] = '"%s"' % CONFIG[var]
|
||||
|
||||
if CONFIG['MOZ_UPDATER'] and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
|
||||
|
694
toolkit/xre/test/win/TestLauncherRegistryInfo.cpp
Normal file
694
toolkit/xre/test/win/TestLauncherRegistryInfo.cpp
Normal file
@ -0,0 +1,694 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/LauncherRegistryInfo.h"
|
||||
#include "mozilla/NativeNt.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "CmdLineAndEnvUtils.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
|
||||
#include "LauncherRegistryInfo.cpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
const char kFailFmt[] = "TEST-FAILED | LauncherRegistryInfo | %s | %S\n";
|
||||
|
||||
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 std::wstring gBrowserValue;
|
||||
static std::wstring gLauncherValue;
|
||||
static std::wstring gImageValue;
|
||||
|
||||
static DWORD gMyImageTimestamp;
|
||||
|
||||
using QWordResult = mozilla::Result<DWORD64, mozilla::WindowsError>;
|
||||
using DWordResult = mozilla::Result<DWORD, mozilla::WindowsError>;
|
||||
using VoidResult = mozilla::Result<mozilla::Ok, mozilla::WindowsError>;
|
||||
|
||||
#define RUN_TEST(fn) \
|
||||
if ((vr = fn()).isErr()) { \
|
||||
printf(kFailFmt, #fn, vr.unwrapErr().AsString().get()); \
|
||||
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;
|
||||
}
|
||||
|
||||
return mozilla::Err(mozilla::WindowsError::FromWin32Error(status));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
return mozilla::Err(mozilla::WindowsError::FromWin32Error(status));
|
||||
}
|
||||
|
||||
static VoidResult DeleteBrowserTimestamp() {
|
||||
LSTATUS status = ::RegDeleteKeyValueW(HKEY_CURRENT_USER, kRegKeyPath,
|
||||
gBrowserValue.c_str());
|
||||
if (status == ERROR_SUCCESS || status == ERROR_FILE_NOT_FOUND) {
|
||||
return mozilla::Ok();
|
||||
}
|
||||
|
||||
return mozilla::Err(mozilla::WindowsError::FromWin32Error(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();
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
vr = DeleteLauncherTimestamp();
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
return DeleteImageTimestamp();
|
||||
}
|
||||
|
||||
static VoidResult SetupEnabledScenario() {
|
||||
// Reset the registry state to an enabled state. First, we delete all existing
|
||||
// timestamps (if any).
|
||||
VoidResult vr = DeleteAllTimestamps();
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
// Now we run Check(Launcher)...
|
||||
mozilla::LauncherRegistryInfo info;
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> 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));
|
||||
}
|
||||
|
||||
// ...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));
|
||||
}
|
||||
|
||||
// By this point we are considered to be fully enabled.
|
||||
|
||||
return mozilla::Ok();
|
||||
}
|
||||
|
||||
static VoidResult TestEmptyRegistry() {
|
||||
VoidResult vr = DeleteAllTimestamps();
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
mozilla::LauncherRegistryInfo info;
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> 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));
|
||||
}
|
||||
|
||||
// LauncherRegistryInfo should have created Launcher and Image values
|
||||
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_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());
|
||||
}
|
||||
|
||||
return mozilla::Ok();
|
||||
}
|
||||
|
||||
// This test depends on the side effects from having just run TestEmptyRegistry
|
||||
static VoidResult TestNormal() {
|
||||
mozilla::LauncherRegistryInfo info;
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> result =
|
||||
info.Check(mozilla::LauncherRegistryInfo::ProcessType::Browser);
|
||||
if (result.isErr()) {
|
||||
return mozilla::Err(result.unwrapErr().mError);
|
||||
}
|
||||
|
||||
QWordResult launcherTs = GetLauncherTimestamp();
|
||||
if (launcherTs.isErr()) {
|
||||
return mozilla::Err(launcherTs.unwrapErr());
|
||||
}
|
||||
|
||||
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<mozilla::LauncherRegistryInfo::EnabledState> 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();
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
mozilla::LauncherRegistryInfo info;
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> result =
|
||||
info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher);
|
||||
if (result.isErr()) {
|
||||
return mozilla::Err(result.unwrapErr().mError);
|
||||
}
|
||||
|
||||
if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) {
|
||||
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
// Verify that the browser timestamp is now zero
|
||||
QWordResult browserTs = GetBrowserTimestamp();
|
||||
if (browserTs.isErr()) {
|
||||
return mozilla::Err(browserTs.unwrapErr());
|
||||
}
|
||||
|
||||
if (browserTs.unwrap() != 0ULL) {
|
||||
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
|
||||
}
|
||||
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> 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));
|
||||
}
|
||||
|
||||
return mozilla::Ok();
|
||||
}
|
||||
|
||||
static VoidResult TestLauncherNoBrowser() {
|
||||
VoidResult vr = DeleteAllTimestamps();
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
vr = SetLauncherTimestamp(0x77777777);
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
vr = SetImageTimestamp(gMyImageTimestamp);
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
mozilla::LauncherRegistryInfo info;
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> result =
|
||||
info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher);
|
||||
if (result.isErr()) {
|
||||
return mozilla::Err(result.unwrapErr().mError);
|
||||
}
|
||||
|
||||
if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) {
|
||||
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
|
||||
}
|
||||
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> 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));
|
||||
}
|
||||
|
||||
return mozilla::Ok();
|
||||
}
|
||||
|
||||
static VoidResult TestBrowserLessThanLauncher() {
|
||||
VoidResult vr = SetLauncherTimestamp(0x77777777);
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
vr = SetBrowserTimestamp(0ULL);
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
mozilla::LauncherRegistryInfo info;
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> result =
|
||||
info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher);
|
||||
if (result.isErr()) {
|
||||
return mozilla::Err(result.unwrapErr().mError);
|
||||
}
|
||||
|
||||
if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) {
|
||||
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
|
||||
}
|
||||
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> 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));
|
||||
}
|
||||
|
||||
return mozilla::Ok();
|
||||
}
|
||||
|
||||
static VoidResult TestImageTimestampChange() {
|
||||
// This should reset the timestamps and then essentially run like
|
||||
// TestEmptyRegistry
|
||||
VoidResult vr = SetImageTimestamp(0x12345678);
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
vr = SetLauncherTimestamp(1ULL);
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
vr = SetBrowserTimestamp(2ULL);
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
mozilla::LauncherRegistryInfo info;
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> 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));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
return mozilla::Ok();
|
||||
}
|
||||
|
||||
static VoidResult TestImageTimestampChangeWhenDisabled() {
|
||||
VoidResult vr = SetImageTimestamp(0x12345678);
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
vr = DeleteLauncherTimestamp();
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
vr = SetBrowserTimestamp(0ULL);
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
mozilla::LauncherRegistryInfo info;
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> result =
|
||||
info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher);
|
||||
if (result.isErr()) {
|
||||
return mozilla::Err(result.unwrapErr().mError);
|
||||
}
|
||||
|
||||
if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) {
|
||||
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
|
||||
}
|
||||
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> 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));
|
||||
}
|
||||
|
||||
return mozilla::Ok();
|
||||
}
|
||||
|
||||
static VoidResult TestDisableDueToFailure() {
|
||||
VoidResult vr = SetupEnabledScenario();
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
// Check that we are indeed enabled.
|
||||
mozilla::LauncherRegistryInfo info;
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> 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));
|
||||
}
|
||||
|
||||
// Now call DisableDueToFailure
|
||||
mozilla::LauncherVoidResult lvr = info.DisableDueToFailure();
|
||||
if (lvr.isErr()) {
|
||||
return mozilla::Err(lvr.unwrapErr().mError);
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
// If we delete the launcher timestamp, IsEnabled should then return
|
||||
// ForceDisabled.
|
||||
vr = DeleteLauncherTimestamp();
|
||||
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));
|
||||
}
|
||||
|
||||
return mozilla::Ok();
|
||||
}
|
||||
|
||||
static VoidResult TestPrefReflection() {
|
||||
// Reset the registry to a known good state.
|
||||
VoidResult vr = SetupEnabledScenario();
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
// First, test to see what happens when the pref is set to ON.
|
||||
mozilla::LauncherRegistryInfo info;
|
||||
mozilla::LauncherVoidResult reflectOk = info.ReflectPrefToRegistry(true);
|
||||
if (reflectOk.isErr()) {
|
||||
return mozilla::Err(reflectOk.unwrapErr().mError);
|
||||
}
|
||||
|
||||
// Launcher and browser timestamps 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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// IsEnabled should give us Enabled.
|
||||
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> 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));
|
||||
}
|
||||
|
||||
// Okay, so far so good. Let's reset the board and see what happens when we
|
||||
// flip the pref to OFF.
|
||||
vr = SetupEnabledScenario();
|
||||
if (vr.isErr()) {
|
||||
return vr;
|
||||
}
|
||||
|
||||
reflectOk = info.ReflectPrefToRegistry(false);
|
||||
if (reflectOk.isErr()) {
|
||||
return mozilla::Err(reflectOk.unwrapErr().mError);
|
||||
}
|
||||
|
||||
// Launcher timestamp 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());
|
||||
}
|
||||
|
||||
// Browser timestamp should be zero
|
||||
browserTs = GetBrowserTimestamp();
|
||||
if (browserTs.isErr()) {
|
||||
return mozilla::Err(browserTs.unwrapErr());
|
||||
}
|
||||
|
||||
if (browserTs.unwrap() != 0ULL) {
|
||||
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
|
||||
}
|
||||
|
||||
// IsEnabled should give us ForceDisabled
|
||||
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));
|
||||
}
|
||||
|
||||
return mozilla::Ok();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
auto fullPath = mozilla::GetFullBinaryPath();
|
||||
if (!fullPath) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Global setup for all tests
|
||||
gBrowserValue = fullPath.get();
|
||||
gBrowserValue += kBrowserSuffix;
|
||||
|
||||
gLauncherValue = fullPath.get();
|
||||
gLauncherValue += kLauncherSuffix;
|
||||
|
||||
gImageValue = fullPath.get();
|
||||
gImageValue += kImageSuffix;
|
||||
|
||||
mozilla::nt::PEHeaders myHeaders(::GetModuleHandleW(nullptr));
|
||||
if (!myHeaders) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!myHeaders.GetTimeStamp(gMyImageTimestamp)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto onExit = mozilla::MakeScopeExit([]() {
|
||||
mozilla::Unused << DeleteAllTimestamps();
|
||||
});
|
||||
|
||||
VoidResult 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);
|
||||
|
||||
return 0;
|
||||
}
|
@ -4,9 +4,13 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
SimplePrograms([
|
||||
'TestXREMakeCommandLineWin',
|
||||
])
|
||||
GeckoCppUnitTests(
|
||||
[
|
||||
'TestLauncherRegistryInfo',
|
||||
'TestXREMakeCommandLineWin',
|
||||
],
|
||||
linkage=None
|
||||
)
|
||||
|
||||
DEFINES['NS_NO_XPCOM'] = True
|
||||
|
||||
@ -24,5 +28,9 @@ OS_LIBS += [
|
||||
'ws2_32',
|
||||
]
|
||||
|
||||
# Needed for TestLauncherRegistryInfo
|
||||
for var in ('MOZ_APP_BASENAME', 'MOZ_APP_VENDOR'):
|
||||
DEFINES[var] = '"%s"' % CONFIG[var]
|
||||
|
||||
if CONFIG['CC_TYPE'] == 'clang-cl':
|
||||
AllowCompilerWarnings() # workaround for bug 1090497
|
||||
|
@ -149,6 +149,14 @@ class WindowsError final {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
bool operator==(const WindowsError& aOther) const {
|
||||
return mHResult == aOther.mHResult;
|
||||
}
|
||||
|
||||
bool operator!=(const WindowsError& aOther) const {
|
||||
return mHResult != aOther.mHResult;
|
||||
}
|
||||
|
||||
static DWORD NtStatusToWin32Error(NTSTATUS aNtStatus) {
|
||||
static const DynamicallyLinkedFunctionPtr<decltype(&RtlNtStatusToDosError)>
|
||||
pRtlNtStatusToDosError(L"ntdll.dll", "RtlNtStatusToDosError");
|
||||
|
Loading…
Reference in New Issue
Block a user