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"
14 #include "WinTaskbar.h"
15 #include "WinMouseScrollHandler.h"
16 #include "nsWindowDefs.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"
39 #ifdef MOZ_BACKGROUNDTASKS
40 # include "mozilla/BackgroundTasks.h"
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
{
59 WinWakeLockListener() { MOZ_ASSERT(XRE_IsParentProcess()); }
62 ~WinWakeLockListener() {
63 ReleaseWakelockIfNeeded(PowerRequestDisplayRequired
);
64 ReleaseWakelockIfNeeded(PowerRequestExecutionRequired
);
67 void SetHandle(HANDLE aHandle
, POWER_REQUEST_TYPE aType
) {
69 case PowerRequestDisplayRequired
: {
70 if (!aHandle
&& mDisplayHandle
) {
71 CloseHandle(mDisplayHandle
);
73 mDisplayHandle
= aHandle
;
76 case PowerRequestExecutionRequired
: {
77 if (!aHandle
&& mNonDisplayHandle
) {
78 CloseHandle(mNonDisplayHandle
);
80 mNonDisplayHandle
= aHandle
;
84 MOZ_ASSERT_UNREACHABLE("Invalid request type");
89 HANDLE
GetHandle(POWER_REQUEST_TYPE aType
) const {
91 case PowerRequestDisplayRequired
:
92 return mDisplayHandle
;
93 case PowerRequestExecutionRequired
:
94 return mNonDisplayHandle
;
96 MOZ_ASSERT_UNREACHABLE("Invalid request type");
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
);
109 WAKE_LOCK_LOG("Failed to create handle for %s, error=%lu",
110 RequestTypeStr(aType
), GetLastError());
113 SetHandle(handle
, aType
);
117 LPWSTR
RequestTypeLPWSTR(POWER_REQUEST_TYPE aType
) const {
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
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 {
132 case PowerRequestDisplayRequired
:
133 return "display request";
134 case PowerRequestExecutionRequired
:
135 return "non-display request";
137 MOZ_ASSERT_UNREACHABLE("Invalid request type");
142 void RequestWakelockIfNeeded(POWER_REQUEST_TYPE aType
) {
143 if (GetHandle(aType
)) {
144 WAKE_LOCK_LOG("Already requested lock for %s", RequestTypeStr(aType
));
148 WAKE_LOCK_LOG("Prepare a wakelock for %s", RequestTypeStr(aType
));
149 HANDLE handle
= CreateHandle(aType
);
151 WAKE_LOCK_LOG("Failed due to no handle for %s", RequestTypeStr(aType
));
155 if (PowerSetRequest(handle
, aType
)) {
156 WAKE_LOCK_LOG("Requested %s lock", RequestTypeStr(aType
));
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
));
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());
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")) {
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");
195 requestLock
= aState
.EqualsASCII("locked-foreground");
198 if (isNonDisplayLock
) {
200 RequestWakelockIfNeeded(PowerRequestExecutionRequired
);
202 ReleaseWakelockIfNeeded(PowerRequestExecutionRequired
);
206 RequestWakelockIfNeeded(PowerRequestDisplayRequired
);
208 ReleaseWakelockIfNeeded(PowerRequestDisplayRequired
);
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
);
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
{
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.");
254 ~SingleNativeEventPump() {}
257 NS_IMPL_ISUPPORTS(SingleNativeEventPump
, nsIThreadObserver
)
260 SingleNativeEventPump::OnDispatchedEvent() { return NS_OK
; }
263 SingleNativeEventPump::OnProcessNextEvent(nsIThreadInternal
* aThread
,
266 bool gotMessage
= WinUtils::PeekMessage(&msg
, nullptr, 0, 0, PM_REMOVE
);
268 ::TranslateMessage(&msg
);
269 ::DispatchMessageW(&msg
);
275 SingleNativeEventPump::AfterProcessNextEvent(nsIThreadInternal
* aThread
,
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
289 UINT
nsAppShell::GetTaskbarButtonCreatedMessage() {
290 return sTaskbarButtonCreatedMsg
;
294 namespace crashreporter
{
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
,
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
) {
319 nsAppShell
* as
= reinterpret_cast<nsAppShell
*>(lParam
);
320 as
->NativeEventCallback();
321 --sOutstandingNativeEventCallbacks
;
325 LRESULT ret
= DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
326 eventLogger
.SetResult(ret
, false);
330 nsAppShell::~nsAppShell() {
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
337 SendMessage(mEventWnd
, WM_CLOSE
, 0, 0);
340 // Cancel any outstanding native event callbacks.
341 sOutstandingNativeEventCallbacks
= 0;
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");
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");
366 return nsBaseAppShell::Observe(aSubject
, aTopic
, aData
);
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().
379 // Last NTSTATUS, as provided by the TIB.
380 NTSTATUS ntStatus
= ~0;
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
);
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.)
429 // Number of atoms confirmed not in use.
431 // Number of atoms which gave errors when checked.
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.
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
) {
474 } else if (errState
== kInvalidAtom
) {
477 // Unexpected error-state.
479 retval
.lastErrorAtom
= atom
;
480 retval
.lastErrorState
= errState
;
489 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) && defined(_M_X64)
490 MOZ_NEVER_INLINE
__attribute__((naked
)) void EnableTrapFlag() {
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
;
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;
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
] =
549 state
.mData
->mUser32StepsAtErrorState
[state
.mErrorStates
] =
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
;
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");
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
);
606 sSingleStepStaticState
.mData
= nullptr;
607 return CSSD_ERROR_ADD_VECTORED_EXCEPTION_HANDLER
;
613 ::RemoveVectoredExceptionHandler(veh
);
614 sSingleStepStaticState
.mData
= nullptr;
616 aPostCollectionCallback();
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
);
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();
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
]];
671 wc
.lpfnWndProc
= EventWindowProc
;
674 wc
.hInstance
= module
;
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(
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)
719 // Retry with single-step data collection
721 auto cssdResult
= CollectSingleStepData(
722 [module
, &eventWnd
]() {
724 CreateWindowW(kWindowClass
, L
"nsAppShell:EventWindow", 0, 0, 0,
725 10, 10, HWND_MESSAGE
, nullptr, module
, nullptr);
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
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
);
748 nsresult
nsAppShell::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
763 if (XRE_UseNativeEventProcessing()) {
764 if (nsresult rv
= this->InitHiddenWindow(); NS_FAILED(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());
777 threadInt
->SetObserver(new SingleNativeEventPump());
781 if (XRE_IsParentProcess()) {
782 ScreenManager
& screenManager
= ScreenManager::GetSingleton();
783 if (gfxPlatform::IsHeadless()) {
784 screenManager
.SetHelper(mozilla::MakeUnique
<HeadlessScreenHelper
>());
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();
805 nsAppShell::Run(void) {
806 bool wantAudio
= true;
807 if (XRE_IsParentProcess()) {
808 #ifdef MOZ_BACKGROUNDTASKS
809 if (BackgroundTasks::IsBackgroundTaskMode()) {
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();
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) {
863 mNativeCallbackPending
= false;
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;
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");
909 gotMessage
= WinUtils::PeekMessage(&msg
, nullptr, 0, 0, PM_REMOVE
);
913 if (msg
.message
== WM_QUIT
) {
914 ::PostQuitMessage(msg
.wParam
);
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.
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
);
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();
993 nsresult
nsAppShell::AfterProcessNextEvent(nsIThreadInternal
* /* unused */,
995 if (!mMsgsToRepost
.empty()) {
996 for (MSG msg
: mMsgsToRepost
) {
997 ::PostMessageW(msg
.hwnd
, msg
.message
, msg
.wParam
, msg
.lParam
);
999 mMsgsToRepost
.clear();