Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / widget / windows / nsAppShell.cpp
blob88627a0cca2d20041a96f264e65891b0500735c4
1 /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Attributes.h"
7 #include "mozilla/ScopeExit.h"
8 #include "mozilla/ipc/MessageChannel.h"
9 #include "mozilla/ipc/WindowsMessageLoop.h"
10 #include "nsAppShell.h"
11 #include "nsToolkit.h"
12 #include "nsThreadUtils.h"
13 #include "WinUtils.h"
14 #include "WinTaskbar.h"
15 #include "WinMouseScrollHandler.h"
16 #include "nsWindowDefs.h"
17 #include "nsWindow.h"
18 #include "nsString.h"
19 #include "WinIMEHandler.h"
20 #include "mozilla/widget/AudioSession.h"
21 #include "mozilla/BackgroundHangMonitor.h"
22 #include "mozilla/Hal.h"
23 #include "nsIDOMWakeLockListener.h"
24 #include "nsIPowerManagerService.h"
25 #include "mozilla/ProfilerLabels.h"
26 #include "mozilla/StaticPtr.h"
27 #include "nsTHashtable.h"
28 #include "nsHashKeys.h"
29 #include "nsComponentManagerUtils.h"
30 #include "ScreenHelperWin.h"
31 #include "HeadlessScreenHelper.h"
32 #include "mozilla/widget/ScreenManager.h"
33 #include "mozilla/Atomics.h"
34 #include "mozilla/NativeNt.h"
35 #include "mozilla/WindowsProcessMitigations.h"
37 #include <winternl.h>
39 #ifdef MOZ_BACKGROUNDTASKS
40 # include "mozilla/BackgroundTasks.h"
41 #endif
43 #if defined(ACCESSIBILITY)
44 # include "mozilla/a11y/Compatibility.h"
45 # include "mozilla/a11y/Platform.h"
46 #endif // defined(ACCESSIBILITY)
48 using namespace mozilla;
49 using namespace mozilla::widget;
51 #define WAKE_LOCK_LOG(...) \
52 MOZ_LOG(gWinWakeLockLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
53 static mozilla::LazyLogModule gWinWakeLockLog("WinWakeLock");
55 // This wakelock listener is used for Window7 and above.
56 class WinWakeLockListener final : public nsIDOMMozWakeLockListener {
57 public:
58 NS_DECL_ISUPPORTS
59 WinWakeLockListener() { MOZ_ASSERT(XRE_IsParentProcess()); }
61 private:
62 ~WinWakeLockListener() {
63 ReleaseWakelockIfNeeded(PowerRequestDisplayRequired);
64 ReleaseWakelockIfNeeded(PowerRequestExecutionRequired);
67 void SetHandle(HANDLE aHandle, POWER_REQUEST_TYPE aType) {
68 switch (aType) {
69 case PowerRequestDisplayRequired: {
70 if (!aHandle && mDisplayHandle) {
71 CloseHandle(mDisplayHandle);
73 mDisplayHandle = aHandle;
74 return;
76 case PowerRequestExecutionRequired: {
77 if (!aHandle && mNonDisplayHandle) {
78 CloseHandle(mNonDisplayHandle);
80 mNonDisplayHandle = aHandle;
81 return;
83 default:
84 MOZ_ASSERT_UNREACHABLE("Invalid request type");
85 return;
89 HANDLE GetHandle(POWER_REQUEST_TYPE aType) const {
90 switch (aType) {
91 case PowerRequestDisplayRequired:
92 return mDisplayHandle;
93 case PowerRequestExecutionRequired:
94 return mNonDisplayHandle;
95 default:
96 MOZ_ASSERT_UNREACHABLE("Invalid request type");
97 return nullptr;
101 HANDLE CreateHandle(POWER_REQUEST_TYPE aType) {
102 MOZ_ASSERT(!GetHandle(aType));
103 REASON_CONTEXT context = {0};
104 context.Version = POWER_REQUEST_CONTEXT_VERSION;
105 context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
106 context.Reason.SimpleReasonString = RequestTypeLPWSTR(aType);
107 HANDLE handle = PowerCreateRequest(&context);
108 if (!handle) {
109 WAKE_LOCK_LOG("Failed to create handle for %s, error=%lu",
110 RequestTypeStr(aType), GetLastError());
111 return nullptr;
113 SetHandle(handle, aType);
114 return handle;
117 LPWSTR RequestTypeLPWSTR(POWER_REQUEST_TYPE aType) const {
118 switch (aType) {
119 case PowerRequestDisplayRequired:
120 return const_cast<LPWSTR>(L"display request"); // -Wwritable-strings
121 case PowerRequestExecutionRequired:
122 return const_cast<LPWSTR>(
123 L"non-display request"); // -Wwritable-strings
124 default:
125 MOZ_ASSERT_UNREACHABLE("Invalid request type");
126 return const_cast<LPWSTR>(L"unknown"); // -Wwritable-strings
130 const char* RequestTypeStr(POWER_REQUEST_TYPE aType) const {
131 switch (aType) {
132 case PowerRequestDisplayRequired:
133 return "display request";
134 case PowerRequestExecutionRequired:
135 return "non-display request";
136 default:
137 MOZ_ASSERT_UNREACHABLE("Invalid request type");
138 return "unknown";
142 void RequestWakelockIfNeeded(POWER_REQUEST_TYPE aType) {
143 if (GetHandle(aType)) {
144 WAKE_LOCK_LOG("Already requested lock for %s", RequestTypeStr(aType));
145 return;
148 WAKE_LOCK_LOG("Prepare a wakelock for %s", RequestTypeStr(aType));
149 HANDLE handle = CreateHandle(aType);
150 if (!handle) {
151 WAKE_LOCK_LOG("Failed due to no handle for %s", RequestTypeStr(aType));
152 return;
155 if (PowerSetRequest(handle, aType)) {
156 WAKE_LOCK_LOG("Requested %s lock", RequestTypeStr(aType));
157 } else {
158 WAKE_LOCK_LOG("Failed to request %s lock, error=%lu",
159 RequestTypeStr(aType), GetLastError());
160 SetHandle(nullptr, aType);
164 void ReleaseWakelockIfNeeded(POWER_REQUEST_TYPE aType) {
165 if (!GetHandle(aType)) {
166 WAKE_LOCK_LOG("Already released lock for %s", RequestTypeStr(aType));
167 return;
170 WAKE_LOCK_LOG("Prepare to release wakelock for %s", RequestTypeStr(aType));
171 if (!PowerClearRequest(GetHandle(aType), aType)) {
172 WAKE_LOCK_LOG("Failed to release %s lock, error=%lu",
173 RequestTypeStr(aType), GetLastError());
174 return;
176 SetHandle(nullptr, aType);
177 WAKE_LOCK_LOG("Released wakelock for %s", RequestTypeStr(aType));
180 NS_IMETHOD Callback(const nsAString& aTopic,
181 const nsAString& aState) override {
182 WAKE_LOCK_LOG("topic=%s, state=%s", NS_ConvertUTF16toUTF8(aTopic).get(),
183 NS_ConvertUTF16toUTF8(aState).get());
184 if (!aTopic.EqualsASCII("screen") && !aTopic.EqualsASCII("audio-playing") &&
185 !aTopic.EqualsASCII("video-playing")) {
186 return NS_OK;
189 const bool isNonDisplayLock = aTopic.EqualsASCII("audio-playing");
190 bool requestLock = false;
191 if (isNonDisplayLock) {
192 requestLock = aState.EqualsASCII("locked-foreground") ||
193 aState.EqualsASCII("locked-background");
194 } else {
195 requestLock = aState.EqualsASCII("locked-foreground");
198 if (isNonDisplayLock) {
199 if (requestLock) {
200 RequestWakelockIfNeeded(PowerRequestExecutionRequired);
201 } else {
202 ReleaseWakelockIfNeeded(PowerRequestExecutionRequired);
204 } else {
205 if (requestLock) {
206 RequestWakelockIfNeeded(PowerRequestDisplayRequired);
207 } else {
208 ReleaseWakelockIfNeeded(PowerRequestDisplayRequired);
211 return NS_OK;
214 // Handle would only exist when we request wakelock successfully.
215 HANDLE mDisplayHandle = nullptr;
216 HANDLE mNonDisplayHandle = nullptr;
218 NS_IMPL_ISUPPORTS(WinWakeLockListener, nsIDOMMozWakeLockListener)
219 StaticRefPtr<nsIDOMMozWakeLockListener> sWakeLockListener;
221 static void AddScreenWakeLockListener() {
222 nsCOMPtr<nsIPowerManagerService> sPowerManagerService =
223 do_GetService(POWERMANAGERSERVICE_CONTRACTID);
224 if (sPowerManagerService) {
225 sWakeLockListener = new WinWakeLockListener();
226 sPowerManagerService->AddWakeLockListener(sWakeLockListener);
227 } else {
228 NS_WARNING(
229 "Failed to retrieve PowerManagerService, wakelocks will be broken!");
233 static void RemoveScreenWakeLockListener() {
234 nsCOMPtr<nsIPowerManagerService> sPowerManagerService =
235 do_GetService(POWERMANAGERSERVICE_CONTRACTID);
236 if (sPowerManagerService) {
237 sPowerManagerService->RemoveWakeLockListener(sWakeLockListener);
238 sPowerManagerService = nullptr;
239 sWakeLockListener = nullptr;
243 class SingleNativeEventPump final : public nsIThreadObserver {
244 public:
245 NS_DECL_THREADSAFE_ISUPPORTS
246 NS_DECL_NSITHREADOBSERVER
248 SingleNativeEventPump() {
249 MOZ_ASSERT(!XRE_UseNativeEventProcessing(),
250 "Should only be used when not properly processing events.");
253 private:
254 ~SingleNativeEventPump() {}
257 NS_IMPL_ISUPPORTS(SingleNativeEventPump, nsIThreadObserver)
259 NS_IMETHODIMP
260 SingleNativeEventPump::OnDispatchedEvent() { return NS_OK; }
262 NS_IMETHODIMP
263 SingleNativeEventPump::OnProcessNextEvent(nsIThreadInternal* aThread,
264 bool aMayWait) {
265 MSG msg;
266 bool gotMessage = WinUtils::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE);
267 if (gotMessage) {
268 ::TranslateMessage(&msg);
269 ::DispatchMessageW(&msg);
271 return NS_OK;
274 NS_IMETHODIMP
275 SingleNativeEventPump::AfterProcessNextEvent(nsIThreadInternal* aThread,
276 bool aMayWait) {
277 return NS_OK;
280 // RegisterWindowMessage values
281 // Native event callback message
282 const wchar_t* kAppShellGeckoEventId = L"nsAppShell:EventID";
283 UINT sAppShellGeckoMsgId = 0x10001; // initialize to invalid message ID
284 // Taskbar button creation message
285 const wchar_t* kTaskbarButtonEventId = L"TaskbarButtonCreated";
286 UINT sTaskbarButtonCreatedMsg = 0x10002; // initialize to invalid message ID
288 /* static */
289 UINT nsAppShell::GetTaskbarButtonCreatedMessage() {
290 return sTaskbarButtonCreatedMsg;
293 namespace mozilla {
294 namespace crashreporter {
295 void LSPAnnotate();
296 } // namespace crashreporter
297 } // namespace mozilla
299 using mozilla::crashreporter::LSPAnnotate;
301 //-------------------------------------------------------------------------
303 // Note that since we're on x86-ish processors here, ReleaseAcquire is the
304 // semantics that normal loads and stores would use anyway.
305 static Atomic<size_t, ReleaseAcquire> sOutstandingNativeEventCallbacks;
307 /*static*/ LRESULT CALLBACK nsAppShell::EventWindowProc(HWND hwnd, UINT uMsg,
308 WPARAM wParam,
309 LPARAM lParam) {
310 NativeEventLogger eventLogger("AppShell", hwnd, uMsg, wParam, lParam);
312 if (uMsg == sAppShellGeckoMsgId) {
313 // The app shell might have been destroyed between this message being
314 // posted and being executed, so be extra careful.
315 if (!sOutstandingNativeEventCallbacks) {
316 return TRUE;
319 nsAppShell* as = reinterpret_cast<nsAppShell*>(lParam);
320 as->NativeEventCallback();
321 --sOutstandingNativeEventCallbacks;
322 return TRUE;
325 LRESULT ret = DefWindowProc(hwnd, uMsg, wParam, lParam);
326 eventLogger.SetResult(ret, false);
327 return ret;
330 nsAppShell::~nsAppShell() {
331 hal::Shutdown();
333 if (mEventWnd) {
334 // DestroyWindow doesn't do anything when called from a non UI thread.
335 // Since mEventWnd was created on the UI thread, it must be destroyed on
336 // the UI thread.
337 SendMessage(mEventWnd, WM_CLOSE, 0, 0);
340 // Cancel any outstanding native event callbacks.
341 sOutstandingNativeEventCallbacks = 0;
344 NS_IMETHODIMP
345 nsAppShell::Observe(nsISupports* aSubject, const char* aTopic,
346 const char16_t* aData) {
347 if (XRE_IsParentProcess()) {
348 nsCOMPtr<nsIObserverService> obsServ(
349 mozilla::services::GetObserverService());
351 if (!strcmp(aTopic, "sessionstore-restoring-on-startup")) {
352 nsWindow::SetIsRestoringSession(true);
353 // Now that we've handled the observer notification, we can remove it
354 obsServ->RemoveObserver(this, "sessionstore-restoring-on-startup");
355 return NS_OK;
358 if (!strcmp(aTopic, "sessionstore-windows-restored")) {
359 nsWindow::SetIsRestoringSession(false);
360 // Now that we've handled the observer notification, we can remove it
361 obsServ->RemoveObserver(this, "sessionstore-windows-restored");
362 return NS_OK;
366 return nsBaseAppShell::Observe(aSubject, aTopic, aData);
369 namespace {
371 // Struct storing the visible, loggable error-state of a Windows thread.
372 // Approximately `std:pair(::GetLastError(), ::RtlGetLastNtStatus())`.
374 // Uses sentinel values rather than a proper `Maybe` type to simplify
375 // minidump-analysis.
376 struct WinErrorState {
377 // Last error, as provided by ::GetLastError().
378 DWORD error = ~0;
379 // Last NTSTATUS, as provided by the TIB.
380 NTSTATUS ntStatus = ~0;
382 private:
383 // per WINE et al.; stable since NT 3.51
384 constexpr static size_t kLastNtStatusOffset =
385 sizeof(size_t) == 8 ? 0x1250 : 0xbf4;
387 static void SetLastNtStatus(NTSTATUS status) {
388 auto* teb = ::NtCurrentTeb();
389 *reinterpret_cast<NTSTATUS*>(reinterpret_cast<char*>(teb) +
390 kLastNtStatusOffset) = status;
393 static NTSTATUS GetLastNtStatus() {
394 auto const* teb = ::NtCurrentTeb();
395 return *reinterpret_cast<NTSTATUS const*>(
396 reinterpret_cast<char const*>(teb) + kLastNtStatusOffset);
399 public:
400 // Restore (or just set) the error state of the current thread.
401 static void Apply(WinErrorState const& state) {
402 SetLastNtStatus(state.ntStatus);
403 ::SetLastError(state.error);
406 // Clear the error-state of the current thread.
407 static void Clear() { Apply({.error = 0, .ntStatus = 0}); }
409 // Get the error-state of the current thread.
410 static WinErrorState Get() {
411 return WinErrorState{
412 .error = ::GetLastError(),
413 .ntStatus = GetLastNtStatus(),
417 bool operator==(WinErrorState const& that) const {
418 return this->error == that.error && this->ntStatus == that.ntStatus;
421 bool operator!=(WinErrorState const& that) const { return !operator==(that); }
424 // Struct containing information about the user atom table. (See
425 // DiagnoseUserAtomTable(), below.)
426 struct AtomTableInformation {
427 // Number of atoms in use. (Exactly 0x4000 == 16384, if all are.)
428 UINT in_use = 0;
429 // Number of atoms confirmed not in use.
430 UINT free = 0;
431 // Number of atoms which gave errors when checked.
432 UINT errors = 0;
434 // Last atom which gave an unexpected error...
435 UINT lastErrorAtom = ~0u;
436 // ... and the error it gave.
437 WinErrorState lastErrorState;
440 // Return a summary of the state of the atom table.
441 MOZ_NEVER_INLINE static AtomTableInformation DiagnoseUserAtomTable() {
442 // Restore error state on exit, for the sake of automated minidump analyses.
443 auto const _restoreErrState =
444 mozilla::MakeScopeExit([oldErrState = WinErrorState::Get()]() {
445 WinErrorState::Apply(oldErrState);
448 AtomTableInformation retval;
450 // Expected error-state on failure-return when the atom is assigned, but not
451 // enough space was provided for the full string.
452 constexpr WinErrorState kBufferTooSmall = {
453 .error = ERROR_INSUFFICIENT_BUFFER,
454 .ntStatus = ((NTSTATUS)0xC0000023), // == STATUS_BUFFER_TOO_SMALL
456 // Expected error-state on failure-return when the atom is not assigned.
457 constexpr WinErrorState kInvalidAtom = {
458 .error = ERROR_INVALID_HANDLE,
459 .ntStatus = ((NTSTATUS)STATUS_INVALID_HANDLE),
462 // Iterate over only the dynamic portion of the atom table.
463 for (UINT atom = 0xC000; atom <= 0xFFFF; ++atom) {
464 // The actual atom values are PII. Don't acquire them in their entirety, and
465 // don't keep more information about them than is needed.
466 WCHAR buf[2] = {};
467 // USE OF UNDOCUMENTED BEHAVIOR: The user atom table is shared by message
468 // names, window-class names, and clipboard-format names. Only the last has
469 // a documented getter-mechanism.
470 BOOL const ok = ::GetClipboardFormatNameW(atom, buf, 1);
471 WinErrorState const errState = WinErrorState::Get();
472 if (ok || errState == kBufferTooSmall) {
473 ++retval.in_use;
474 } else if (errState == kInvalidAtom) {
475 ++retval.free;
476 } else {
477 // Unexpected error-state.
478 ++retval.errors;
479 retval.lastErrorAtom = atom;
480 retval.lastErrorState = errState;
484 return retval;
487 } // namespace
489 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) && defined(_M_X64)
490 MOZ_NEVER_INLINE __attribute__((naked)) void EnableTrapFlag() {
491 asm volatile(
492 "pushfq;"
493 "orw $0x100,(%rsp);"
494 "popfq;"
495 "retq;");
498 MOZ_NEVER_INLINE __attribute__((naked)) void DisableTrapFlag() {
499 asm volatile("retq;");
502 # define SSD_MAX_USER32_STEPS 0x1800
503 # define SSD_MAX_ERROR_STATES 0x200
504 struct SingleStepData {
505 uint32_t mUser32StepsLog[SSD_MAX_USER32_STEPS]{};
506 WinErrorState mErrorStatesLog[SSD_MAX_ERROR_STATES];
507 uint16_t mUser32StepsAtErrorState[SSD_MAX_ERROR_STATES]{};
510 struct SingleStepStaticState {
511 SingleStepData* mData{};
512 uintptr_t mUser32Start{};
513 uintptr_t mUser32End{};
514 uint32_t mUser32Steps{};
515 uint32_t mErrorStates{};
516 WinErrorState mLastRecordedErrorState;
518 constexpr void Reset() { *this = SingleStepStaticState{}; }
521 static SingleStepStaticState sSingleStepStaticState{};
523 LONG SingleStepExceptionHandler(_EXCEPTION_POINTERS* aExceptionInfo) {
524 auto& state = sSingleStepStaticState;
525 if (state.mData &&
526 aExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) {
527 auto instructionPointer = aExceptionInfo->ContextRecord->Rip;
528 if (instructionPointer == reinterpret_cast<uintptr_t>(&DisableTrapFlag)) {
529 // Stop handling any exception in this handler
530 state.mData = nullptr;
531 } else {
532 // Record data for the current step, if in user32
533 if (state.mUser32Start <= instructionPointer &&
534 instructionPointer < state.mUser32End) {
535 // We record the instruction pointer
536 if (state.mUser32Steps < SSD_MAX_USER32_STEPS) {
537 state.mData->mUser32StepsLog[state.mUser32Steps] =
538 static_cast<uint32_t>(instructionPointer - state.mUser32Start);
541 // We record changes in the error state
542 auto currentErrorState{WinErrorState::Get()};
543 if (currentErrorState != state.mLastRecordedErrorState) {
544 state.mLastRecordedErrorState = currentErrorState;
546 if (state.mErrorStates < SSD_MAX_ERROR_STATES) {
547 state.mData->mErrorStatesLog[state.mErrorStates] =
548 currentErrorState;
549 state.mData->mUser32StepsAtErrorState[state.mErrorStates] =
550 state.mUser32Steps;
553 ++state.mErrorStates;
556 ++state.mUser32Steps;
559 // Continue single-stepping
560 aExceptionInfo->ContextRecord->EFlags |= 0x100;
562 return EXCEPTION_CONTINUE_EXECUTION;
564 return EXCEPTION_CONTINUE_SEARCH;
567 enum CSSD_RESULT {
568 CSSD_SUCCESS = 0,
569 CSSD_ERROR_DEBUGGER_PRESENT = 1,
570 CSSD_ERROR_GET_MODULE_HANDLE = 2,
571 CSSD_ERROR_PARSING_USER32 = 3,
572 CSSD_ERROR_ADD_VECTORED_EXCEPTION_HANDLER = 4,
575 template <typename CallbackToRun, typename PostCollectionCallback>
576 [[clang::optnone]] MOZ_NEVER_INLINE CSSD_RESULT
577 CollectSingleStepData(CallbackToRun aCallbackToRun,
578 PostCollectionCallback aPostCollectionCallback) {
579 if (::IsDebuggerPresent()) {
580 return CSSD_ERROR_DEBUGGER_PRESENT;
583 MOZ_DIAGNOSTIC_ASSERT(!sSingleStepStaticState.mData,
584 "Single-stepping is already active");
585 HANDLE user32 = ::GetModuleHandleW(L"user32.dll");
586 if (!user32) {
587 return CSSD_ERROR_GET_MODULE_HANDLE;
590 nt::PEHeaders user32Headers{user32};
591 auto bounds = user32Headers.GetBounds();
592 if (bounds.isNothing()) {
593 return CSSD_ERROR_PARSING_USER32;
596 SingleStepData singleStepData{};
598 sSingleStepStaticState.Reset();
599 sSingleStepStaticState.mUser32Start =
600 reinterpret_cast<uintptr_t>(bounds.ref().begin().get());
601 sSingleStepStaticState.mUser32End =
602 reinterpret_cast<uintptr_t>(bounds.ref().end().get());
603 sSingleStepStaticState.mData = &singleStepData;
604 auto veh = ::AddVectoredExceptionHandler(TRUE, SingleStepExceptionHandler);
605 if (!veh) {
606 sSingleStepStaticState.mData = nullptr;
607 return CSSD_ERROR_ADD_VECTORED_EXCEPTION_HANDLER;
610 EnableTrapFlag();
611 aCallbackToRun();
612 DisableTrapFlag();
613 ::RemoveVectoredExceptionHandler(veh);
614 sSingleStepStaticState.mData = nullptr;
616 aPostCollectionCallback();
618 return CSSD_SUCCESS;
620 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED && _M_X64
622 // Collect data for bug 1571516. We don't automatically send up `GetLastError`
623 // or `GetLastNtStatus` data for beta/release builds, so extract the relevant
624 // error values and store them on the stack, where they can be viewed in
625 // minidumps -- in fact, do so after each individual API call. This takes the
626 // form of various local variables whose initial character is an underscore,
627 // most of which are also marked [[maybe_unused]].
629 // We tag this function `[[clang::optnone]]` to prevent the compiler from
630 // eliding those values as _actually_ unused, as well as to generally simplify
631 // the haruspex's task once the minidumps are in. (As this function should be
632 // called at most once per process, the minor performance hit is not a concern.)
634 [[clang::optnone]] MOZ_NEVER_INLINE nsresult nsAppShell::InitHiddenWindow() {
635 // note the incoming error-state; this may be relevant to errors we get later
636 auto _initialErr [[maybe_unused]] = WinErrorState::Get();
637 // reset the error-state, to avoid ambiguity below
638 WinErrorState::Clear();
640 // Diagnostic variable. Only collected in the event of a failure in one of the
641 // functions that attempts to register an atom.
642 AtomTableInformation _atomTableInfo [[maybe_unused]];
644 // Attempt to register the window message. On failure, retain the initial
645 // value of `sAppShellGeckoMsgId`.
646 auto const _msgId = ::RegisterWindowMessageW(kAppShellGeckoEventId);
647 if (_msgId) {
648 sAppShellGeckoMsgId = _msgId;
650 auto const _sAppShellGeckoMsgId [[maybe_unused]] = sAppShellGeckoMsgId;
651 auto const _rwmErr [[maybe_unused]] = WinErrorState::Get();
652 if (!_msgId) _atomTableInfo = DiagnoseUserAtomTable();
653 NS_ASSERTION(sAppShellGeckoMsgId,
654 "Could not register hidden window event message!");
656 mLastNativeEventScheduled = TimeStamp::NowLoRes();
658 WNDCLASSW wc;
659 HINSTANCE const module = GetModuleHandle(nullptr);
661 constexpr const wchar_t* kWindowClass = L"nsAppShell:EventWindowClass";
662 // (Undocumented behavior note: on success, this will specifically be the
663 // window-class atom. We don't rely on this.)
664 BOOL const _gciwRet = ::GetClassInfoW(module, kWindowClass, &wc);
665 auto const _gciwErr [[maybe_unused]] = WinErrorState::Get();
666 WinErrorState::Clear();
668 WinErrorState _rcErr [[maybe_unused]];
669 if (!_gciwRet) {
670 wc.style = 0;
671 wc.lpfnWndProc = EventWindowProc;
672 wc.cbClsExtra = 0;
673 wc.cbWndExtra = 0;
674 wc.hInstance = module;
675 wc.hIcon = nullptr;
676 wc.hCursor = nullptr;
677 wc.hbrBackground = (HBRUSH) nullptr;
678 wc.lpszMenuName = (LPCWSTR) nullptr;
679 wc.lpszClassName = kWindowClass;
681 ATOM _windowClassAtom = ::RegisterClassW(&wc);
682 _rcErr = WinErrorState::Get();
684 if (!_windowClassAtom) _atomTableInfo = DiagnoseUserAtomTable();
686 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) && defined(_M_X64)
687 if (!_windowClassAtom) {
688 // Retry with single-step data collection
689 auto cssdResult = CollectSingleStepData(
690 [&wc, &_windowClassAtom]() {
691 _windowClassAtom = ::RegisterClassW(&wc);
693 [&_windowClassAtom]() {
694 // Crashing here gives access to the single step data on stack
695 MOZ_DIAGNOSTIC_ASSERT(
696 _windowClassAtom,
697 "RegisterClassW for EventWindowClass failed twice");
699 auto const _cssdErr [[maybe_unused]] = WinErrorState::Get();
700 MOZ_DIAGNOSTIC_ASSERT(
701 cssdResult == CSSD_SUCCESS,
702 "Failed to collect single step data for RegisterClassW");
703 // If we reach this point then somehow the single-stepped call succeeded
704 // and we can proceed
706 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED && _M_X64
708 MOZ_DIAGNOSTIC_ASSERT(_windowClassAtom,
709 "RegisterClassW for EventWindowClass failed");
710 WinErrorState::Clear();
713 mEventWnd = CreateWindowW(kWindowClass, L"nsAppShell:EventWindow", 0, 0, 0,
714 10, 10, HWND_MESSAGE, nullptr, module, nullptr);
715 auto const _cwErr [[maybe_unused]] = WinErrorState::Get();
717 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) && defined(_M_X64)
718 if (!mEventWnd) {
719 // Retry with single-step data collection
720 HWND eventWnd{};
721 auto cssdResult = CollectSingleStepData(
722 [module, &eventWnd]() {
723 eventWnd =
724 CreateWindowW(kWindowClass, L"nsAppShell:EventWindow", 0, 0, 0,
725 10, 10, HWND_MESSAGE, nullptr, module, nullptr);
727 [&eventWnd]() {
728 // Crashing here gives access to the single step data on stack
729 MOZ_DIAGNOSTIC_ASSERT(eventWnd,
730 "CreateWindowW for EventWindow failed twice");
732 auto const _cssdErr [[maybe_unused]] = WinErrorState::Get();
733 MOZ_DIAGNOSTIC_ASSERT(
734 cssdResult == CSSD_SUCCESS,
735 "Failed to collect single step data for CreateWindowW");
736 // If we reach this point then somehow the single-stepped call succeeded and
737 // we can proceed
738 mEventWnd = eventWnd;
740 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED && _M_X64
742 MOZ_DIAGNOSTIC_ASSERT(mEventWnd, "CreateWindowW for EventWindow failed");
743 NS_ENSURE_STATE(mEventWnd);
745 return NS_OK;
748 nsresult nsAppShell::Init() {
749 LSPAnnotate();
751 hal::Init();
753 if (XRE_IsParentProcess()) {
754 sTaskbarButtonCreatedMsg = ::RegisterWindowMessageW(kTaskbarButtonEventId);
755 NS_ASSERTION(sTaskbarButtonCreatedMsg,
756 "Could not register taskbar button creation message");
759 // The hidden message window is used for interrupting the processing of native
760 // events, so that we can process gecko events. Therefore, we only need it if
761 // we are processing native events. Disabling this is required for win32k
762 // syscall lockdown.
763 if (XRE_UseNativeEventProcessing()) {
764 if (nsresult rv = this->InitHiddenWindow(); NS_FAILED(rv)) {
765 return rv;
767 } else if (XRE_IsContentProcess() && !IsWin32kLockedDown()) {
768 // We're not generally processing native events, but still using GDI and we
769 // still have some internal windows, e.g. from calling CoInitializeEx.
770 // So we use a class that will do a single event pump where previously we
771 // might have processed multiple events to make sure any occasional messages
772 // to these windows are processed. This also allows any internal Windows
773 // messages to be processed to ensure the GDI data remains fresh.
774 nsCOMPtr<nsIThreadInternal> threadInt =
775 do_QueryInterface(NS_GetCurrentThread());
776 if (threadInt) {
777 threadInt->SetObserver(new SingleNativeEventPump());
781 if (XRE_IsParentProcess()) {
782 ScreenManager& screenManager = ScreenManager::GetSingleton();
783 if (gfxPlatform::IsHeadless()) {
784 screenManager.SetHelper(mozilla::MakeUnique<HeadlessScreenHelper>());
785 } else {
786 screenManager.SetHelper(mozilla::MakeUnique<ScreenHelperWin>());
787 ScreenHelperWin::RefreshScreens();
790 nsCOMPtr<nsIObserverService> obsServ(
791 mozilla::services::GetObserverService());
793 obsServ->AddObserver(this, "sessionstore-restoring-on-startup", false);
794 obsServ->AddObserver(this, "sessionstore-windows-restored", false);
797 if (!WinUtils::GetTimezoneName(mTimezoneName)) {
798 NS_WARNING("Unable to get system timezone name, timezone may be invalid\n");
801 return nsBaseAppShell::Init();
804 NS_IMETHODIMP
805 nsAppShell::Run(void) {
806 bool wantAudio = true;
807 if (XRE_IsParentProcess()) {
808 #ifdef MOZ_BACKGROUNDTASKS
809 if (BackgroundTasks::IsBackgroundTaskMode()) {
810 wantAudio = false;
812 #endif
813 if (MOZ_LIKELY(wantAudio)) {
814 mozilla::widget::StartAudioSession();
817 // Add an observer that disables the screen saver when requested by Gecko.
818 // For example when we're playing video in the foreground tab. Whole firefox
819 // only needs one wakelock instance, so we would only create one listener in
820 // chrome process to prevent requesting unnecessary wakelock.
821 AddScreenWakeLockListener();
824 nsresult rv = nsBaseAppShell::Run();
826 if (XRE_IsParentProcess()) {
827 RemoveScreenWakeLockListener();
829 if (MOZ_LIKELY(wantAudio)) {
830 mozilla::widget::StopAudioSession();
834 return rv;
837 void nsAppShell::DoProcessMoreGeckoEvents() {
838 // Called by nsBaseAppShell's NativeEventCallback() after it has finished
839 // processing pending gecko events and there are still gecko events pending
840 // for the thread. (This can happen if NS_ProcessPendingEvents reached it's
841 // starvation timeout limit.) The default behavior in nsBaseAppShell is to
842 // call ScheduleNativeEventCallback to post a follow up native event callback
843 // message. This triggers an additional call to NativeEventCallback for more
844 // gecko event processing.
846 // There's a deadlock risk here with certain internal Windows modal loops. In
847 // our dispatch code, we prioritize messages so that input is handled first.
848 // However Windows modal dispatch loops often prioritize posted messages. If
849 // we find ourselves in a tight gecko timer loop where NS_ProcessPendingEvents
850 // takes longer than the timer duration, NS_HasPendingEvents(thread) will
851 // always be true. ScheduleNativeEventCallback will be called on every
852 // NativeEventCallback callback, and in a Windows modal dispatch loop, the
853 // callback message will be processed first -> input gets starved, dead lock.
855 // To avoid, don't post native callback messages from NativeEventCallback
856 // when we're in a modal loop. This gets us back into the Windows modal
857 // dispatch loop dispatching input messages. Once we drop out of the modal
858 // loop, we use mNativeCallbackPending to fire off a final NativeEventCallback
859 // if we need it, which insures NS_ProcessPendingEvents gets called and all
860 // gecko events get processed.
861 if (mEventloopNestingLevel < 2) {
862 OnDispatchedEvent();
863 mNativeCallbackPending = false;
864 } else {
865 mNativeCallbackPending = true;
869 void nsAppShell::ScheduleNativeEventCallback() {
870 MOZ_ASSERT(mEventWnd,
871 "We should have created mEventWnd in Init, if this is called.");
873 // Post a message to the hidden message window
874 ++sOutstandingNativeEventCallbacks;
876 MutexAutoLock lock(mLastNativeEventScheduledMutex);
877 // Time stamp this event so we can detect cases where the event gets
878 // dropping in sub classes / modal loops we do not control.
879 mLastNativeEventScheduled = TimeStamp::NowLoRes();
881 ::PostMessage(mEventWnd, sAppShellGeckoMsgId, 0,
882 reinterpret_cast<LPARAM>(this));
885 bool nsAppShell::ProcessNextNativeEvent(bool mayWait) {
886 // Notify ipc we are spinning a (possibly nested) gecko event loop.
887 mozilla::ipc::MessageChannel::NotifyGeckoEventDispatch();
889 bool gotMessage = false;
891 do {
892 MSG msg;
894 // For avoiding deadlock between our process and plugin process by
895 // mouse wheel messages, we're handling actually when we receive one of
896 // following internal messages which is posted by native mouse wheel
897 // message handler. Any other events, especially native modifier key
898 // events, should not be handled between native message and posted
899 // internal message because it may make different modifier key state or
900 // mouse cursor position between them.
901 if (mozilla::widget::MouseScrollHandler::IsWaitingInternalMessage()) {
902 gotMessage = WinUtils::PeekMessage(&msg, nullptr, MOZ_WM_MOUSEWHEEL_FIRST,
903 MOZ_WM_MOUSEWHEEL_LAST, PM_REMOVE);
904 NS_ASSERTION(gotMessage,
905 "waiting internal wheel message, but it has not come");
908 if (!gotMessage) {
909 gotMessage = WinUtils::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE);
912 if (gotMessage) {
913 if (msg.message == WM_QUIT) {
914 ::PostQuitMessage(msg.wParam);
915 Exit();
916 } else {
917 // If we had UI activity we would be processing it now so we know we
918 // have either kUIActivity or kActivityNoUIAVail.
919 mozilla::BackgroundHangMonitor().NotifyActivity();
921 if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST &&
922 IMEHandler::ProcessRawKeyMessage(msg)) {
923 continue; // the message is consumed.
926 #if defined(_X86_)
927 // Store Printer dialog messages for reposting on x86, because on x86
928 // Windows 7 they are not processed by a window procedure, but are
929 // explicitly waited for in the winspool.drv code that will be further
930 // up the stack (winspool!WaitForCompletionMessage). These are
931 // undocumented Windows Message identifiers found in winspool.drv.
932 if (msg.message == 0x5b7a || msg.message == 0x5b7f ||
933 msg.message == 0x5b80 || msg.message == 0x5b81) {
934 mMsgsToRepost.push_back(msg);
935 continue;
937 #endif
939 // Windows documentation suggets that WM_SETTINGSCHANGE is the message
940 // to watch for timezone changes, but experimentation showed that it
941 // doesn't fire on changing the timezone, but that WM_TIMECHANGE does,
942 // even if there's no immediate effect on the clock (e.g., changing
943 // from Pacific Daylight at UTC-7 to Arizona at UTC-7).
944 if (msg.message == WM_TIMECHANGE) {
945 // The message may not give us sufficient information to determine
946 // if the timezone changed, so keep track of it ourselves.
947 wchar_t systemTimezone[128];
948 bool getSystemTimeSucceeded =
949 WinUtils::GetTimezoneName(systemTimezone);
950 if (getSystemTimeSucceeded && wcscmp(systemTimezone, mTimezoneName)) {
951 nsBaseAppShell::OnSystemTimezoneChange();
953 wcscpy_s(mTimezoneName, 128, systemTimezone);
957 ::TranslateMessage(&msg);
958 ::DispatchMessageW(&msg);
960 } else if (mayWait) {
961 // Block and wait for any posted application message
962 mozilla::BackgroundHangMonitor().NotifyWait();
964 AUTO_PROFILER_LABEL("nsAppShell::ProcessNextNativeEvent::Wait", IDLE);
965 WinUtils::WaitForMessage();
968 } while (!gotMessage && mayWait);
970 // See DoProcessNextNativeEvent, mEventloopNestingLevel will be
971 // one when a modal loop unwinds.
972 if (mNativeCallbackPending && mEventloopNestingLevel == 1)
973 DoProcessMoreGeckoEvents();
975 // Check for starved native callbacks. If we haven't processed one
976 // of these events in NATIVE_EVENT_STARVATION_LIMIT, fire one off.
977 static const mozilla::TimeDuration nativeEventStarvationLimit =
978 mozilla::TimeDuration::FromSeconds(NATIVE_EVENT_STARVATION_LIMIT);
980 TimeDuration timeSinceLastNativeEventScheduled;
982 MutexAutoLock lock(mLastNativeEventScheduledMutex);
983 timeSinceLastNativeEventScheduled =
984 TimeStamp::NowLoRes() - mLastNativeEventScheduled;
986 if (timeSinceLastNativeEventScheduled > nativeEventStarvationLimit) {
987 ScheduleNativeEventCallback();
990 return gotMessage;
993 nsresult nsAppShell::AfterProcessNextEvent(nsIThreadInternal* /* unused */,
994 bool /* unused */) {
995 if (!mMsgsToRepost.empty()) {
996 for (MSG msg : mMsgsToRepost) {
997 ::PostMessageW(msg.hwnd, msg.message, msg.wParam, msg.lParam);
999 mMsgsToRepost.clear();
1001 return NS_OK;