1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
6
7 #define MOZ_USE_LAUNCHER_ERROR
8
9 #include "mozilla/LauncherRegistryInfo.h"
10 #include "mozilla/NativeNt.h"
11 #include "mozilla/ScopeExit.h"
12 #include "mozilla/Unused.h"
13 #include "nsWindowsHelpers.h"
14
15 #include "LauncherRegistryInfo.cpp"
16
17 #include <string>
18
19 static const char kMsgStart[] = "TEST-FAILED | LauncherRegistryInfo | ";
20
21 static const wchar_t kRegKeyPath[] = L"SOFTWARE\\" EXPAND_STRING_MACRO(
22 MOZ_APP_VENDOR) L"\\" EXPAND_STRING_MACRO(MOZ_APP_BASENAME) L"\\Launcher";
23 static const wchar_t kBrowserSuffix[] = L"|Browser";
24 static const wchar_t kLauncherSuffix[] = L"|Launcher";
25 static const wchar_t kImageSuffix[] = L"|Image";
26 static const wchar_t kTelemetrySuffix[] = L"|Telemetry";
27
28 static std::wstring gBrowserValue;
29 static std::wstring gLauncherValue;
30 static std::wstring gImageValue;
31 static std::wstring gTelemetryValue;
32
33 static DWORD gMyImageTimestamp;
34
35 #define RUN_TEST(result, fn) \
36 if ((result = fn()).isErr()) { \
37 const mozilla::LauncherError& err = result.inspectErr(); \
38 printf("%s%s | %08lx (%s:%d)\n", kMsgStart, #fn, err.mError.AsHResult(), \
39 err.mFile, err.mLine); \
40 return 1; \
41 }
42
43 #define EXPECT_COMMIT_IS_OK() \
44 do { \
45 mozilla::LauncherVoidResult vr2 = info.Commit(); \
46 if (vr2.isErr()) { \
47 return vr2; \
48 } \
49 } while (0)
50
51 #define EXPECT_CHECK_RESULT_IS(desired, expected) \
52 do { \
53 mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> \
54 result = info.Check(mozilla::LauncherRegistryInfo::desired); \
55 if (result.isErr()) { \
56 return result.propagateErr(); \
57 } \
58 if (result.unwrap() != mozilla::LauncherRegistryInfo::expected) { \
59 return LAUNCHER_ERROR_FROM_HRESULT(E_FAIL); \
60 } \
61 } while (0)
62
63 #define EXPECT_ENABLED_STATE_IS(expected) \
64 do { \
65 mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> \
66 enabled = info.IsEnabled(); \
67 if (enabled.isErr()) { \
68 return enabled.propagateErr(); \
69 } \
70 if (enabled.unwrap() != mozilla::LauncherRegistryInfo::expected) { \
71 return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \
72 } \
73 } while (0)
74
75 #define EXPECT_TELEMETRY_IS_ENABLED(expected) \
76 do { \
77 mozilla::LauncherResult<bool> enabled = info.IsTelemetryEnabled(); \
78 if (enabled.isErr()) { \
79 return enabled.propagateErr(); \
80 } \
81 if (enabled.unwrap() != expected) { \
82 return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \
83 } \
84 } while (0)
85
86 #define EXPECT_REG_DWORD_EXISTS_AND_EQ(name, expected) \
87 do { \
88 mozilla::LauncherResult<mozilla::Maybe<DWORD>> result = \
89 ReadRegistryValueData<DWORD>(name, REG_DWORD); \
90 if (result.isErr()) { \
91 return result.propagateErr(); \
92 } \
93 if (result.inspect().isNothing() || \
94 result.inspect().value() != expected) { \
95 return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \
96 } \
97 } while (0)
98
99 #define EXPECT_REG_QWORD_EXISTS(name) \
100 do { \
101 mozilla::LauncherResult<mozilla::Maybe<uint64_t>> result = \
102 ReadRegistryValueData<uint64_t>(name, REG_QWORD); \
103 if (result.isErr()) { \
104 return result.propagateErr(); \
105 } \
106 if (result.inspect().isNothing()) { \
107 return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \
108 } \
109 } while (0)
110
111 #define EXPECT_REG_QWORD_EXISTS_AND_EQ(name, expected) \
112 do { \
113 mozilla::LauncherResult<mozilla::Maybe<uint64_t>> result = \
114 ReadRegistryValueData<uint64_t>(name, REG_QWORD); \
115 if (result.isErr()) { \
116 return result.propagateErr(); \
117 } \
118 if (result.inspect().isNothing() || \
119 result.inspect().value() != expected) { \
120 return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \
121 } \
122 } while (0)
123
124 #define EXPECT_REG_DWORD_DOES_NOT_EXIST(name) \
125 do { \
126 mozilla::LauncherResult<mozilla::Maybe<DWORD>> result = \
127 ReadRegistryValueData<DWORD>(name, REG_DWORD); \
128 if (result.isErr()) { \
129 return result.propagateErr(); \
130 } \
131 if (result.inspect().isSome()) { \
132 return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \
133 } \
134 } while (0)
135
136 #define EXPECT_REG_QWORD_DOES_NOT_EXIST(name) \
137 do { \
138 mozilla::LauncherResult<mozilla::Maybe<uint64_t>> result = \
139 ReadRegistryValueData<uint64_t>(name, REG_QWORD); \
140 if (result.isErr()) { \
141 return result.propagateErr(); \
142 } \
143 if (result.inspect().isSome()) { \
144 return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \
145 } \
146 } while (0)
147
148 template <typename T>
ReadRegistryValueData(const std::wstring & name,DWORD expectedType)149 static mozilla::LauncherResult<mozilla::Maybe<T>> ReadRegistryValueData(
150 const std::wstring& name, DWORD expectedType) {
151 T data;
152 DWORD dataLen = sizeof(data);
153 DWORD type;
154 LSTATUS status = ::RegGetValueW(HKEY_CURRENT_USER, kRegKeyPath, name.c_str(),
155 RRF_RT_ANY, &type, &data, &dataLen);
156 if (status == ERROR_FILE_NOT_FOUND) {
157 return mozilla::Maybe<T>();
158 }
159
160 if (status != ERROR_SUCCESS) {
161 return LAUNCHER_ERROR_FROM_WIN32(status);
162 }
163
164 if (type != expectedType) {
165 return LAUNCHER_ERROR_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
166 }
167
168 return mozilla::Some(data);
169 }
170
171 template <typename T>
WriteRegistryValueData(const std::wstring & name,DWORD type,T data)172 static mozilla::LauncherVoidResult WriteRegistryValueData(
173 const std::wstring& name, DWORD type, T data) {
174 LSTATUS status = ::RegSetKeyValueW(HKEY_CURRENT_USER, kRegKeyPath,
175 name.c_str(), type, &data, sizeof(T));
176 if (status != ERROR_SUCCESS) {
177 return LAUNCHER_ERROR_FROM_WIN32(status);
178 }
179
180 return mozilla::Ok();
181 }
182
DeleteRegistryValueData(const std::wstring & name)183 static mozilla::LauncherVoidResult DeleteRegistryValueData(
184 const std::wstring& name) {
185 LSTATUS status =
186 ::RegDeleteKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, name.c_str());
187 if (status == ERROR_SUCCESS || status == ERROR_FILE_NOT_FOUND) {
188 return mozilla::Ok();
189 }
190
191 return LAUNCHER_ERROR_FROM_WIN32(status);
192 }
193
DeleteAllRegstryValues()194 static mozilla::LauncherVoidResult DeleteAllRegstryValues() {
195 // Unblock commit via ReflectPrefToRegistry
196 // (We need to set false, and then true to bypass the early return)
197 mozilla::LauncherRegistryInfo info;
198 mozilla::LauncherVoidResult vr = info.ReflectPrefToRegistry(false);
199 vr = info.ReflectPrefToRegistry(true);
200 if (vr.isErr()) {
201 return vr;
202 }
203
204 vr = DeleteRegistryValueData(gImageValue);
205 if (vr.isErr()) {
206 return vr;
207 }
208
209 vr = DeleteRegistryValueData(gLauncherValue);
210 if (vr.isErr()) {
211 return vr;
212 }
213
214 vr = DeleteRegistryValueData(gBrowserValue);
215 if (vr.isErr()) {
216 return vr;
217 }
218
219 return DeleteRegistryValueData(gTelemetryValue);
220 }
221
SetupEnabledScenario()222 static mozilla::LauncherVoidResult SetupEnabledScenario() {
223 // Reset the registry state to an enabled state. First, we delete all existing
224 // registry values (if any).
225 mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
226 if (vr.isErr()) {
227 return vr;
228 }
229
230 // Now we run Check(Launcher)...
231 mozilla::LauncherRegistryInfo info;
232 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
233 EXPECT_COMMIT_IS_OK();
234 // ...and Check(Browser)
235 EXPECT_CHECK_RESULT_IS(ProcessType::Browser, ProcessType::Browser);
236 EXPECT_COMMIT_IS_OK();
237
238 // By this point we are considered to be fully enabled.
239 return mozilla::Ok();
240 }
241
TestEmptyRegistry()242 static mozilla::LauncherVoidResult TestEmptyRegistry() {
243 mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
244 if (vr.isErr()) {
245 return vr;
246 }
247
248 mozilla::LauncherRegistryInfo info;
249 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
250 EXPECT_COMMIT_IS_OK();
251
252 // LauncherRegistryInfo should have created Launcher and Image values
253 EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp);
254 EXPECT_REG_QWORD_EXISTS(gLauncherValue);
255 EXPECT_REG_QWORD_DOES_NOT_EXIST(gBrowserValue);
256
257 EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
258
259 return mozilla::Ok();
260 }
261
TestNormal()262 static mozilla::LauncherVoidResult TestNormal() {
263 mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
264 if (vr.isErr()) {
265 return vr;
266 }
267 vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, gMyImageTimestamp);
268 if (vr.isErr()) {
269 return vr;
270 }
271 vr = WriteRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD, QPCNowRaw());
272 if (vr.isErr()) {
273 return vr;
274 }
275
276 mozilla::LauncherRegistryInfo info;
277 EXPECT_CHECK_RESULT_IS(ProcessType::Browser, ProcessType::Browser);
278 EXPECT_COMMIT_IS_OK();
279
280 // Make sure the browser timestamp is newer than the launcher's
281 mozilla::LauncherResult<mozilla::Maybe<uint64_t>> launcherTs =
282 ReadRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD);
283 if (launcherTs.isErr()) {
284 return launcherTs.propagateErr();
285 }
286 mozilla::LauncherResult<mozilla::Maybe<uint64_t>> browserTs =
287 ReadRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD);
288 if (browserTs.isErr()) {
289 return browserTs.propagateErr();
290 }
291 if (launcherTs.inspect().isNothing() || browserTs.inspect().isNothing() ||
292 browserTs.inspect().value() <= launcherTs.inspect().value()) {
293 return LAUNCHER_ERROR_FROM_HRESULT(E_FAIL);
294 }
295
296 EXPECT_ENABLED_STATE_IS(EnabledState::Enabled);
297
298 return mozilla::Ok();
299 }
300
TestBrowserNoLauncher()301 static mozilla::LauncherVoidResult TestBrowserNoLauncher() {
302 mozilla::LauncherVoidResult vr = SetupEnabledScenario();
303 if (vr.isErr()) {
304 return vr;
305 }
306 vr = DeleteRegistryValueData(gLauncherValue);
307 if (vr.isErr()) {
308 return vr;
309 }
310
311 mozilla::LauncherRegistryInfo info;
312 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
313 EXPECT_COMMIT_IS_OK();
314
315 // Verify that we still don't have a launcher timestamp
316 EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue);
317 // Verify that the browser timestamp is now zero
318 EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL);
319
320 EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
321
322 return mozilla::Ok();
323 }
324
TestLauncherNoBrowser()325 static mozilla::LauncherVoidResult TestLauncherNoBrowser() {
326 constexpr uint64_t launcherTs = 0x77777777;
327 mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
328 if (vr.isErr()) {
329 return vr;
330 }
331 vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, gMyImageTimestamp);
332 if (vr.isErr()) {
333 return vr;
334 }
335 vr = WriteRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD, launcherTs);
336 if (vr.isErr()) {
337 return vr;
338 }
339
340 mozilla::LauncherRegistryInfo info;
341 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
342 EXPECT_COMMIT_IS_OK();
343
344 // Launcher's timestamps is kept intact while browser's is set to 0.
345 EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs);
346 EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL);
347
348 EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
349
350 return mozilla::Ok();
351 }
352
TestBrowserLessThanLauncher()353 static mozilla::LauncherVoidResult TestBrowserLessThanLauncher() {
354 constexpr uint64_t launcherTs = 0x77777777, browserTs = 0x66666666;
355 mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
356 if (vr.isErr()) {
357 return vr;
358 }
359 vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, gMyImageTimestamp);
360 if (vr.isErr()) {
361 return vr;
362 }
363 vr = WriteRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD, launcherTs);
364 if (vr.isErr()) {
365 return vr;
366 }
367 vr = WriteRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD, browserTs);
368 if (vr.isErr()) {
369 return vr;
370 }
371
372 mozilla::LauncherRegistryInfo info;
373 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
374 EXPECT_COMMIT_IS_OK();
375
376 // Launcher's timestamps is kept intact while browser's is set to 0.
377 EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs);
378 EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL);
379
380 EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
381
382 return mozilla::Ok();
383 }
384
TestImageTimestampChange()385 static mozilla::LauncherVoidResult TestImageTimestampChange() {
386 // This should reset the timestamps and then essentially run like
387 // TestEmptyRegistry
388 mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
389 if (vr.isErr()) {
390 return vr;
391 }
392 vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, 0x12345678);
393 if (vr.isErr()) {
394 return vr;
395 }
396 vr = WriteRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD, 1ULL);
397 if (vr.isErr()) {
398 return vr;
399 }
400 vr = WriteRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD, 2ULL);
401 if (vr.isErr()) {
402 return vr;
403 }
404
405 mozilla::LauncherRegistryInfo info;
406 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
407 EXPECT_COMMIT_IS_OK();
408
409 EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp);
410 EXPECT_REG_QWORD_EXISTS(gLauncherValue);
411 EXPECT_REG_QWORD_DOES_NOT_EXIST(gBrowserValue);
412
413 return mozilla::Ok();
414 }
415
TestImageTimestampChangeWhenDisabled()416 static mozilla::LauncherVoidResult TestImageTimestampChangeWhenDisabled() {
417 mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
418 if (vr.isErr()) {
419 return vr;
420 }
421 vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, 0x12345678);
422 if (vr.isErr()) {
423 return vr;
424 }
425 vr = WriteRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD, 0ULL);
426 if (vr.isErr()) {
427 return vr;
428 }
429
430 mozilla::LauncherRegistryInfo info;
431 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
432 EXPECT_COMMIT_IS_OK();
433
434 EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp);
435 EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue);
436 EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0);
437
438 EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
439
440 return mozilla::Ok();
441 }
442
TestDisableDueToFailure()443 static mozilla::LauncherVoidResult TestDisableDueToFailure() {
444 mozilla::LauncherVoidResult vr = SetupEnabledScenario();
445 if (vr.isErr()) {
446 return vr;
447 }
448
449 // Check that we are indeed enabled.
450 mozilla::LauncherRegistryInfo info;
451 EXPECT_ENABLED_STATE_IS(EnabledState::Enabled);
452
453 // Now call DisableDueToFailure
454 mozilla::LauncherVoidResult lvr = info.DisableDueToFailure();
455 if (lvr.isErr()) {
456 return lvr.propagateErr();
457 }
458
459 // We should now be FailDisabled
460 EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
461
462 // If we delete the launcher timestamp, IsEnabled should then return
463 // ForceDisabled.
464 vr = DeleteRegistryValueData(gLauncherValue);
465 if (vr.isErr()) {
466 return vr;
467 }
468 EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
469
470 return mozilla::Ok();
471 }
472
TestPrefReflection()473 static mozilla::LauncherVoidResult TestPrefReflection() {
474 // Reset the registry to a known good state.
475 mozilla::LauncherVoidResult vr = SetupEnabledScenario();
476 if (vr.isErr()) {
477 return vr;
478 }
479
480 // Let's see what happens when we flip the pref to OFF.
481 mozilla::LauncherRegistryInfo info;
482 mozilla::LauncherVoidResult reflectOk = info.ReflectPrefToRegistry(false);
483 if (reflectOk.isErr()) {
484 return reflectOk.propagateErr();
485 }
486
487 // Launcher timestamp should be non-existent.
488 EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue);
489 // Browser timestamp should be zero
490 EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL);
491 // IsEnabled should give us ForceDisabled
492 EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
493
494 // Now test to see what happens when the pref is set to ON.
495 reflectOk = info.ReflectPrefToRegistry(true);
496 if (reflectOk.isErr()) {
497 return reflectOk.propagateErr();
498 }
499
500 // Launcher and browser timestamps should be non-existent.
501 EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue);
502 EXPECT_REG_QWORD_DOES_NOT_EXIST(gBrowserValue);
503
504 // IsEnabled should give us Enabled.
505 EXPECT_ENABLED_STATE_IS(EnabledState::Enabled);
506
507 return mozilla::Ok();
508 }
509
TestTelemetryConfig()510 static mozilla::LauncherVoidResult TestTelemetryConfig() {
511 mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
512 if (vr.isErr()) {
513 return vr;
514 }
515
516 mozilla::LauncherRegistryInfo info;
517 EXPECT_TELEMETRY_IS_ENABLED(false);
518
519 mozilla::LauncherVoidResult reflectOk =
520 info.ReflectTelemetryPrefToRegistry(false);
521 if (reflectOk.isErr()) {
522 return reflectOk.propagateErr();
523 }
524 EXPECT_TELEMETRY_IS_ENABLED(false);
525
526 reflectOk = info.ReflectTelemetryPrefToRegistry(true);
527 if (reflectOk.isErr()) {
528 return reflectOk.propagateErr();
529 }
530 EXPECT_TELEMETRY_IS_ENABLED(true);
531
532 return mozilla::Ok();
533 }
534
TestCommitAbort()535 static mozilla::LauncherVoidResult TestCommitAbort() {
536 mozilla::LauncherVoidResult vr = SetupEnabledScenario();
537 if (vr.isErr()) {
538 return vr;
539 }
540
541 // Retrieve the current timestamps to compare later
542 mozilla::LauncherResult<mozilla::Maybe<uint64_t>> launcherValue =
543 ReadRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD);
544 if (launcherValue.isErr() || launcherValue.inspect().isNothing()) {
545 return launcherValue.propagateErr();
546 }
547 mozilla::LauncherResult<mozilla::Maybe<uint64_t>> browserValue =
548 ReadRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD);
549 if (browserValue.isErr() || browserValue.inspect().isNothing()) {
550 return browserValue.propagateErr();
551 }
552 uint64_t launcherTs = launcherValue.inspect().value();
553 uint64_t browserTs = browserValue.inspect().value();
554
555 vr = []() -> mozilla::LauncherVoidResult {
556 mozilla::LauncherRegistryInfo info;
557 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
558 // No commit
559 return mozilla::Ok();
560 }();
561 if (vr.isErr()) {
562 return vr;
563 }
564
565 // Exiting the scope discards the change.
566 mozilla::LauncherRegistryInfo info;
567 EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp);
568 EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs);
569 EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, browserTs);
570
571 // Commit -> Check -> Abort -> Commit
572 EXPECT_COMMIT_IS_OK();
573 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
574 info.Abort();
575 EXPECT_COMMIT_IS_OK();
576
577 // Nothing is changed.
578 EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp);
579 EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs);
580 EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, browserTs);
581 EXPECT_ENABLED_STATE_IS(EnabledState::Enabled);
582
583 return mozilla::Ok();
584 }
585
TestDisableDuringLauncherLaunch()586 static mozilla::LauncherVoidResult TestDisableDuringLauncherLaunch() {
587 mozilla::LauncherVoidResult vr = SetupEnabledScenario();
588 if (vr.isErr()) {
589 return vr;
590 }
591
592 mozilla::LauncherResult<mozilla::Maybe<uint64_t>> launcherTs =
593 ReadRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD);
594 if (launcherTs.isErr()) {
595 return launcherTs.propagateErr();
596 }
597 if (launcherTs.inspect().isNothing()) {
598 return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED);
599 }
600
601 vr = []() -> mozilla::LauncherVoidResult {
602 mozilla::LauncherRegistryInfo info;
603 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
604
605 // Call DisableDueToFailure with a different instance
606 mozilla::LauncherVoidResult vr = []() -> mozilla::LauncherVoidResult {
607 mozilla::LauncherRegistryInfo info;
608 mozilla::LauncherVoidResult vr = info.DisableDueToFailure();
609 if (vr.isErr()) {
610 return vr.propagateErr();
611 }
612 return mozilla::Ok();
613 }();
614 if (vr.isErr()) {
615 return vr;
616 }
617
618 // Commit after disable.
619 EXPECT_COMMIT_IS_OK();
620
621 return mozilla::Ok();
622 }();
623 if (vr.isErr()) {
624 return vr;
625 }
626
627 // Make sure we're still FailDisabled and the launcher's timestamp is not
628 // updated
629 mozilla::LauncherRegistryInfo info;
630 EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
631 EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs.inspect().value());
632
633 return mozilla::Ok();
634 }
635
TestDisableDuringBrowserLaunch()636 static mozilla::LauncherVoidResult TestDisableDuringBrowserLaunch() {
637 mozilla::LauncherVoidResult vr = SetupEnabledScenario();
638 if (vr.isErr()) {
639 return vr;
640 }
641
642 mozilla::LauncherRegistryInfo info;
643 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
644 EXPECT_COMMIT_IS_OK();
645
646 vr = []() -> mozilla::LauncherVoidResult {
647 mozilla::LauncherRegistryInfo info;
648 EXPECT_CHECK_RESULT_IS(ProcessType::Browser, ProcessType::Browser);
649
650 // Call DisableDueToFailure with a different instance
651 mozilla::LauncherVoidResult vr = []() -> mozilla::LauncherVoidResult {
652 mozilla::LauncherRegistryInfo info;
653 mozilla::LauncherVoidResult vr = info.DisableDueToFailure();
654 if (vr.isErr()) {
655 return vr.propagateErr();
656 }
657 return mozilla::Ok();
658 }();
659 if (vr.isErr()) {
660 return vr;
661 }
662
663 // Commit after disable.
664 EXPECT_COMMIT_IS_OK();
665
666 return mozilla::Ok();
667 }();
668 if (vr.isErr()) {
669 return vr;
670 }
671
672 // Make sure we're still FailDisabled
673 EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
674
675 return mozilla::Ok();
676 }
677
TestReEnable()678 static mozilla::LauncherVoidResult TestReEnable() {
679 mozilla::LauncherVoidResult vr = SetupEnabledScenario();
680 if (vr.isErr()) {
681 return vr;
682 }
683
684 // Make FailDisabled
685 mozilla::LauncherRegistryInfo info;
686 vr = info.DisableDueToFailure();
687 if (vr.isErr()) {
688 return vr.propagateErr();
689 }
690 EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
691
692 // Attempt to launch when FailDisabled: Still be FailDisabled
693 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
694 EXPECT_COMMIT_IS_OK();
695 EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
696
697 // Change the timestamp
698 vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, 0x12345678);
699 if (vr.isErr()) {
700 return vr;
701 }
702
703 // Attempt to launch again: Launcher comes back
704 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
705 EXPECT_COMMIT_IS_OK();
706
707 // Make ForceDisabled
708 vr = info.ReflectPrefToRegistry(false);
709 if (vr.isErr()) {
710 return vr.propagateErr();
711 }
712 EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
713
714 // Attempt to launch when ForceDisabled: Still be ForceDisabled
715 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
716 EXPECT_COMMIT_IS_OK();
717 EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
718
719 // Change the timestamp
720 vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, 0x12345678);
721 if (vr.isErr()) {
722 return vr;
723 }
724
725 // Attempt to launch again: Still be ForceDisabled
726 EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
727 EXPECT_COMMIT_IS_OK();
728 EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
729
730 return mozilla::Ok();
731 }
732
main(int argc,char * argv[])733 int main(int argc, char* argv[]) {
734 auto fullPath = mozilla::GetFullBinaryPath();
735 if (!fullPath) {
736 return 1;
737 }
738
739 // Global setup for all tests
740 gBrowserValue = fullPath.get();
741 gBrowserValue += kBrowserSuffix;
742
743 gLauncherValue = fullPath.get();
744 gLauncherValue += kLauncherSuffix;
745
746 gImageValue = fullPath.get();
747 gImageValue += kImageSuffix;
748
749 gTelemetryValue = fullPath.get();
750 gTelemetryValue += kTelemetrySuffix;
751
752 mozilla::LauncherResult<DWORD> timestamp = 0;
753 RUN_TEST(timestamp, GetCurrentImageTimestamp);
754 gMyImageTimestamp = timestamp.unwrap();
755
756 auto onExit = mozilla::MakeScopeExit(
757 []() { mozilla::Unused << DeleteAllRegstryValues(); });
758
759 mozilla::LauncherVoidResult vr = mozilla::Ok();
760
761 // All testcases should call SetupEnabledScenario() or
762 // DeleteAllRegstryValues() to be order-independent
763 RUN_TEST(vr, TestEmptyRegistry);
764 RUN_TEST(vr, TestNormal);
765 RUN_TEST(vr, TestBrowserNoLauncher);
766 RUN_TEST(vr, TestLauncherNoBrowser);
767 RUN_TEST(vr, TestBrowserLessThanLauncher);
768 RUN_TEST(vr, TestImageTimestampChange);
769 RUN_TEST(vr, TestImageTimestampChangeWhenDisabled);
770 RUN_TEST(vr, TestDisableDueToFailure);
771 RUN_TEST(vr, TestPrefReflection);
772 RUN_TEST(vr, TestTelemetryConfig);
773 RUN_TEST(vr, TestCommitAbort);
774 RUN_TEST(vr, TestDisableDuringLauncherLaunch);
775 RUN_TEST(vr, TestDisableDuringBrowserLaunch);
776 RUN_TEST(vr, TestReEnable);
777
778 return 0;
779 }
780