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/WindowsDiagnostics.h"
36 #include "mozilla/WindowsProcessMitigations.h"
40 #ifdef MOZ_BACKGROUNDTASKS
41 # include "mozilla/BackgroundTasks.h"
44 #if defined(ACCESSIBILITY)
45 # include "mozilla/a11y/Compatibility.h"
46 # include "mozilla/a11y/Platform.h"
47 #endif // defined(ACCESSIBILITY)
49 using namespace mozilla
;
50 using namespace mozilla::widget
;
52 #define WAKE_LOCK_LOG(...) \
53 MOZ_LOG(gWinWakeLockLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
54 static mozilla::LazyLogModule
gWinWakeLockLog("WinWakeLock");
56 // This wakelock listener is used for Window7 and above.
57 class WinWakeLockListener final
: public nsIDOMMozWakeLockListener
{
60 WinWakeLockListener() { MOZ_ASSERT(XRE_IsParentProcess()); }
63 ~WinWakeLockListener() {
64 ReleaseWakelockIfNeeded(PowerRequestDisplayRequired
);
65 ReleaseWakelockIfNeeded(PowerRequestExecutionRequired
);
68 void SetHandle(HANDLE aHandle
, POWER_REQUEST_TYPE aType
) {
70 case PowerRequestDisplayRequired
: {
71 if (!aHandle
&& mDisplayHandle
) {
72 CloseHandle(mDisplayHandle
);
74 mDisplayHandle
= aHandle
;
77 case PowerRequestExecutionRequired
: {
78 if (!aHandle
&& mNonDisplayHandle
) {
79 CloseHandle(mNonDisplayHandle
);
81 mNonDisplayHandle
= aHandle
;
85 MOZ_ASSERT_UNREACHABLE("Invalid request type");
90 HANDLE
GetHandle(POWER_REQUEST_TYPE aType
) const {
92 case PowerRequestDisplayRequired
:
93 return mDisplayHandle
;
94 case PowerRequestExecutionRequired
:
95 return mNonDisplayHandle
;
97 MOZ_ASSERT_UNREACHABLE("Invalid request type");
102 HANDLE
CreateHandle(POWER_REQUEST_TYPE aType
) {
103 MOZ_ASSERT(!GetHandle(aType
));
104 REASON_CONTEXT context
= {0};
105 context
.Version
= POWER_REQUEST_CONTEXT_VERSION
;
106 context
.Flags
= POWER_REQUEST_CONTEXT_SIMPLE_STRING
;
107 context
.Reason
.SimpleReasonString
= RequestTypeLPWSTR(aType
);
108 HANDLE handle
= PowerCreateRequest(&context
);
110 WAKE_LOCK_LOG("Failed to create handle for %s, error=%lu",
111 RequestTypeStr(aType
), GetLastError());
114 SetHandle(handle
, aType
);
118 LPWSTR
RequestTypeLPWSTR(POWER_REQUEST_TYPE aType
) const {
120 case PowerRequestDisplayRequired
:
121 return const_cast<LPWSTR
>(L
"display request"); // -Wwritable-strings
122 case PowerRequestExecutionRequired
:
123 return const_cast<LPWSTR
>(
124 L
"non-display request"); // -Wwritable-strings
126 MOZ_ASSERT_UNREACHABLE("Invalid request type");
127 return const_cast<LPWSTR
>(L
"unknown"); // -Wwritable-strings
131 const char* RequestTypeStr(POWER_REQUEST_TYPE aType
) const {
133 case PowerRequestDisplayRequired
:
134 return "display request";
135 case PowerRequestExecutionRequired
:
136 return "non-display request";
138 MOZ_ASSERT_UNREACHABLE("Invalid request type");
143 void RequestWakelockIfNeeded(POWER_REQUEST_TYPE aType
) {
144 if (GetHandle(aType
)) {
145 WAKE_LOCK_LOG("Already requested lock for %s", RequestTypeStr(aType
));
149 WAKE_LOCK_LOG("Prepare a wakelock for %s", RequestTypeStr(aType
));
150 HANDLE handle
= CreateHandle(aType
);
152 WAKE_LOCK_LOG("Failed due to no handle for %s", RequestTypeStr(aType
));
156 if (PowerSetRequest(handle
, aType
)) {
157 WAKE_LOCK_LOG("Requested %s lock", RequestTypeStr(aType
));
159 WAKE_LOCK_LOG("Failed to request %s lock, error=%lu",
160 RequestTypeStr(aType
), GetLastError());
161 SetHandle(nullptr, aType
);
165 void ReleaseWakelockIfNeeded(POWER_REQUEST_TYPE aType
) {
166 if (!GetHandle(aType
)) {
167 WAKE_LOCK_LOG("Already released lock for %s", RequestTypeStr(aType
));
171 WAKE_LOCK_LOG("Prepare to release wakelock for %s", RequestTypeStr(aType
));
172 if (!PowerClearRequest(GetHandle(aType
), aType
)) {
173 WAKE_LOCK_LOG("Failed to release %s lock, error=%lu",
174 RequestTypeStr(aType
), GetLastError());
177 SetHandle(nullptr, aType
);
178 WAKE_LOCK_LOG("Released wakelock for %s", RequestTypeStr(aType
));
181 NS_IMETHOD
Callback(const nsAString
& aTopic
,
182 const nsAString
& aState
) override
{
183 WAKE_LOCK_LOG("topic=%s, state=%s", NS_ConvertUTF16toUTF8(aTopic
).get(),
184 NS_ConvertUTF16toUTF8(aState
).get());
185 if (!aTopic
.EqualsASCII("screen") && !aTopic
.EqualsASCII("audio-playing") &&
186 !aTopic
.EqualsASCII("video-playing")) {
190 const bool isNonDisplayLock
= aTopic
.EqualsASCII("audio-playing");
191 bool requestLock
= false;
192 if (isNonDisplayLock
) {
193 requestLock
= aState
.EqualsASCII("locked-foreground") ||
194 aState
.EqualsASCII("locked-background");
196 requestLock
= aState
.EqualsASCII("locked-foreground");
199 if (isNonDisplayLock
) {
201 RequestWakelockIfNeeded(PowerRequestExecutionRequired
);
203 ReleaseWakelockIfNeeded(PowerRequestExecutionRequired
);
207 RequestWakelockIfNeeded(PowerRequestDisplayRequired
);
209 ReleaseWakelockIfNeeded(PowerRequestDisplayRequired
);
215 // Handle would only exist when we request wakelock successfully.
216 HANDLE mDisplayHandle
= nullptr;
217 HANDLE mNonDisplayHandle
= nullptr;
219 NS_IMPL_ISUPPORTS(WinWakeLockListener
, nsIDOMMozWakeLockListener
)
220 StaticRefPtr
<nsIDOMMozWakeLockListener
> sWakeLockListener
;
222 static void AddScreenWakeLockListener() {
223 nsCOMPtr
<nsIPowerManagerService
> sPowerManagerService
=
224 do_GetService(POWERMANAGERSERVICE_CONTRACTID
);
225 if (sPowerManagerService
) {
226 sWakeLockListener
= new WinWakeLockListener();
227 sPowerManagerService
->AddWakeLockListener(sWakeLockListener
);
230 "Failed to retrieve PowerManagerService, wakelocks will be broken!");
234 static void RemoveScreenWakeLockListener() {
235 nsCOMPtr
<nsIPowerManagerService
> sPowerManagerService
=
236 do_GetService(POWERMANAGERSERVICE_CONTRACTID
);
237 if (sPowerManagerService
) {
238 sPowerManagerService
->RemoveWakeLockListener(sWakeLockListener
);
239 sPowerManagerService
= nullptr;
240 sWakeLockListener
= nullptr;
244 class SingleNativeEventPump final
: public nsIThreadObserver
{
246 NS_DECL_THREADSAFE_ISUPPORTS
247 NS_DECL_NSITHREADOBSERVER
249 SingleNativeEventPump() {
250 MOZ_ASSERT(!XRE_UseNativeEventProcessing(),
251 "Should only be used when not properly processing events.");
255 ~SingleNativeEventPump() {}
258 NS_IMPL_ISUPPORTS(SingleNativeEventPump
, nsIThreadObserver
)
261 SingleNativeEventPump::OnDispatchedEvent() { return NS_OK
; }
264 SingleNativeEventPump::OnProcessNextEvent(nsIThreadInternal
* aThread
,
267 bool gotMessage
= WinUtils::PeekMessage(&msg
, nullptr, 0, 0, PM_REMOVE
);
269 ::TranslateMessage(&msg
);
270 ::DispatchMessageW(&msg
);
276 SingleNativeEventPump::AfterProcessNextEvent(nsIThreadInternal
* aThread
,
281 // RegisterWindowMessage values
282 // Native event callback message
283 const wchar_t* kAppShellGeckoEventId
= L
"nsAppShell:EventID";
284 UINT sAppShellGeckoMsgId
= 0x10001; // initialize to invalid message ID
285 // Taskbar button creation message
286 const wchar_t* kTaskbarButtonEventId
= L
"TaskbarButtonCreated";
287 UINT sTaskbarButtonCreatedMsg
= 0x10002; // initialize to invalid message ID
290 UINT
nsAppShell::GetTaskbarButtonCreatedMessage() {
291 return sTaskbarButtonCreatedMsg
;
295 namespace crashreporter
{
297 } // namespace crashreporter
298 } // namespace mozilla
300 using mozilla::crashreporter::LSPAnnotate
;
302 //-------------------------------------------------------------------------
304 // Note that since we're on x86-ish processors here, ReleaseAcquire is the
305 // semantics that normal loads and stores would use anyway.
306 static Atomic
<size_t, ReleaseAcquire
> sOutstandingNativeEventCallbacks
;
308 /*static*/ LRESULT CALLBACK
nsAppShell::EventWindowProc(HWND hwnd
, UINT uMsg
,
311 NativeEventLogger
eventLogger("AppShell", hwnd
, uMsg
, wParam
, lParam
);
313 if (uMsg
== sAppShellGeckoMsgId
) {
314 // The app shell might have been destroyed between this message being
315 // posted and being executed, so be extra careful.
316 if (!sOutstandingNativeEventCallbacks
) {
320 nsAppShell
* as
= reinterpret_cast<nsAppShell
*>(lParam
);
321 as
->NativeEventCallback();
322 --sOutstandingNativeEventCallbacks
;
326 LRESULT ret
= DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
327 eventLogger
.SetResult(ret
, false);
331 nsAppShell::~nsAppShell() {
335 // DestroyWindow doesn't do anything when called from a non UI thread.
336 // Since mEventWnd was created on the UI thread, it must be destroyed on
338 SendMessage(mEventWnd
, WM_CLOSE
, 0, 0);
341 // Cancel any outstanding native event callbacks.
342 sOutstandingNativeEventCallbacks
= 0;
346 nsAppShell::Observe(nsISupports
* aSubject
, const char* aTopic
,
347 const char16_t
* aData
) {
348 if (XRE_IsParentProcess()) {
349 nsCOMPtr
<nsIObserverService
> obsServ(
350 mozilla::services::GetObserverService());
352 if (!strcmp(aTopic
, "sessionstore-restoring-on-startup")) {
353 nsWindow::SetIsRestoringSession(true);
354 // Now that we've handled the observer notification, we can remove it
355 obsServ
->RemoveObserver(this, "sessionstore-restoring-on-startup");
359 if (!strcmp(aTopic
, "sessionstore-windows-restored")) {
360 nsWindow::SetIsRestoringSession(false);
361 // Now that we've handled the observer notification, we can remove it
362 obsServ
->RemoveObserver(this, "sessionstore-windows-restored");
367 return nsBaseAppShell::Observe(aSubject
, aTopic
, aData
);
372 // Struct containing information about the user atom table. (See
373 // DiagnoseUserAtomTable(), below.)
374 struct AtomTableInformation
{
375 // Number of atoms in use. (Exactly 0x4000 == 16384, if all are.)
377 // Number of atoms confirmed not in use.
379 // Number of atoms which gave errors when checked.
382 // Last atom which gave an unexpected error...
383 UINT lastErrorAtom
= ~0u;
384 // ... and the error it gave.
385 WinErrorState lastErrorState
;
388 // Return a summary of the state of the atom table.
389 MOZ_NEVER_INLINE
static AtomTableInformation
DiagnoseUserAtomTable() {
390 // Restore error state on exit, for the sake of automated minidump analyses.
391 auto const _restoreErrState
=
392 mozilla::MakeScopeExit([oldErrState
= WinErrorState::Get()]() {
393 WinErrorState::Apply(oldErrState
);
396 AtomTableInformation retval
;
398 // Expected error-state on failure-return when the atom is assigned, but not
399 // enough space was provided for the full string.
400 constexpr WinErrorState kBufferTooSmall
= {
401 .error
= ERROR_INSUFFICIENT_BUFFER
,
402 .ntStatus
= ((NTSTATUS
)0xC0000023), // == STATUS_BUFFER_TOO_SMALL
404 // Expected error-state on failure-return when the atom is not assigned.
405 constexpr WinErrorState kInvalidAtom
= {
406 .error
= ERROR_INVALID_HANDLE
,
407 .ntStatus
= ((NTSTATUS
)STATUS_INVALID_HANDLE
),
410 // Iterate over only the dynamic portion of the atom table.
411 for (UINT atom
= 0xC000; atom
<= 0xFFFF; ++atom
) {
412 // The actual atom values are PII. Don't acquire them in their entirety, and
413 // don't keep more information about them than is needed.
415 // USE OF UNDOCUMENTED BEHAVIOR: The user atom table is shared by message
416 // names, window-class names, and clipboard-format names. Only the last has
417 // a documented getter-mechanism.
418 BOOL
const ok
= ::GetClipboardFormatNameW(atom
, buf
, 1);
419 WinErrorState
const errState
= WinErrorState::Get();
420 if (ok
|| errState
== kBufferTooSmall
) {
422 } else if (errState
== kInvalidAtom
) {
425 // Unexpected error-state.
427 retval
.lastErrorAtom
= atom
;
428 retval
.lastErrorState
= errState
;
435 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) && defined(_M_X64)
436 static constexpr int kMaxStepsUser32
= 0x1800;
437 static constexpr int kMaxErrorStatesUser32
= 0x200;
438 using User32SingleStepData
=
439 ModuleSingleStepData
<kMaxStepsUser32
, kMaxErrorStatesUser32
>;
441 template <typename CallbackToRun
, typename PostCollectionCallback
>
442 WindowsDiagnosticsError
CollectUser32SingleStepData(
443 CallbackToRun aCallbackToRun
,
444 PostCollectionCallback aPostCollectionCallback
) {
445 return CollectModuleSingleStepData
<kMaxStepsUser32
, kMaxErrorStatesUser32
>(
446 L
"user32.dll", std::move(aCallbackToRun
),
447 std::move(aPostCollectionCallback
));
449 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED && _M_X64
453 // Collect data for bug 1571516. We don't automatically send up `GetLastError`
454 // or `GetLastNtStatus` data for beta/release builds, so extract the relevant
455 // error values and store them on the stack, where they can be viewed in
456 // minidumps -- in fact, do so after each individual API call. This takes the
457 // form of various local variables whose initial character is an underscore,
458 // most of which are also marked [[maybe_unused]].
460 // We tag this function `[[clang::optnone]]` to prevent the compiler from
461 // eliding those values as _actually_ unused, as well as to generally simplify
462 // the haruspex's task once the minidumps are in. (As this function should be
463 // called at most once per process, the minor performance hit is not a concern.)
465 /* static */ [[clang::optnone
]] MOZ_NEVER_INLINE HWND
466 nsAppShell::StaticCreateEventWindow() {
467 // note the incoming error-state; this may be relevant to errors we get later
468 auto _initialErr
[[maybe_unused
]] = WinErrorState::Get();
469 // reset the error-state, to avoid ambiguity below
470 WinErrorState::Clear();
472 // Diagnostic variable. Only collected in the event of a failure in one of the
473 // functions that attempts to register an atom.
474 AtomTableInformation _atomTableInfo
[[maybe_unused
]];
476 // Attempt to register the window message. On failure, retain the initial
477 // value of `sAppShellGeckoMsgId`.
478 auto const _msgId
= ::RegisterWindowMessageW(kAppShellGeckoEventId
);
480 sAppShellGeckoMsgId
= _msgId
;
482 auto const _sAppShellGeckoMsgId
[[maybe_unused
]] = sAppShellGeckoMsgId
;
483 auto const _rwmErr
[[maybe_unused
]] = WinErrorState::Get();
484 if (!_msgId
) _atomTableInfo
= DiagnoseUserAtomTable();
485 NS_ASSERTION(sAppShellGeckoMsgId
,
486 "Could not register hidden window event message!");
489 HINSTANCE
const module
= GetModuleHandle(nullptr);
491 constexpr const wchar_t* kWindowClass
= L
"nsAppShell:EventWindowClass";
492 // (Undocumented behavior note: on success, this will specifically be the
493 // window-class atom. We don't rely on this.)
494 BOOL
const _gciwRet
= ::GetClassInfoW(module
, kWindowClass
, &wc
);
495 auto const _gciwErr
[[maybe_unused
]] = WinErrorState::Get();
496 WinErrorState::Clear();
498 WinErrorState _rcErr
[[maybe_unused
]];
501 wc
.lpfnWndProc
= EventWindowProc
;
504 wc
.hInstance
= module
;
506 wc
.hCursor
= nullptr;
507 wc
.hbrBackground
= (HBRUSH
) nullptr;
508 wc
.lpszMenuName
= (LPCWSTR
) nullptr;
509 wc
.lpszClassName
= kWindowClass
;
511 ATOM _windowClassAtom
= ::RegisterClassW(&wc
);
512 _rcErr
= WinErrorState::Get();
514 if (!_windowClassAtom
) _atomTableInfo
= DiagnoseUserAtomTable();
516 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) && defined(_M_X64)
517 if (!_windowClassAtom
) {
518 // Retry with single-step data collection
519 WindowsDiagnosticsError rv
= CollectUser32SingleStepData(
520 [&wc
, &_windowClassAtom
]() {
521 _windowClassAtom
= ::RegisterClassW(&wc
);
523 [&_windowClassAtom
](const User32SingleStepData
& aData
) {
524 // Crashing here gives access to the single step data on stack
525 MOZ_DIAGNOSTIC_ASSERT(
527 "RegisterClassW for EventWindowClass failed twice");
529 auto const _cssdErr
[[maybe_unused
]] = WinErrorState::Get();
530 MOZ_DIAGNOSTIC_ASSERT(
531 rv
== WindowsDiagnosticsError::None
,
532 "Failed to collect single step data for RegisterClassW");
533 // If we reach this point then somehow the single-stepped call succeeded
534 // and we can proceed
536 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED && _M_X64
538 MOZ_RELEASE_ASSERT(_windowClassAtom
,
539 "RegisterClassW for EventWindowClass failed");
540 WinErrorState::Clear();
544 CreateWindowW(kWindowClass
, L
"nsAppShell:EventWindow", 0, 0, 0, 10, 10,
545 HWND_MESSAGE
, nullptr, module
, nullptr);
546 auto const _cwErr
[[maybe_unused
]] = WinErrorState::Get();
548 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) && defined(_M_X64)
550 // Retry with single-step data collection
551 WindowsDiagnosticsError rv
= CollectUser32SingleStepData(
552 [module
, &eventWnd
]() {
554 CreateWindowW(kWindowClass
, L
"nsAppShell:EventWindow", 0, 0, 0,
555 10, 10, HWND_MESSAGE
, nullptr, module
, nullptr);
557 [&eventWnd
](const User32SingleStepData
& aData
) {
558 // Crashing here gives access to the single step data on stack
559 MOZ_DIAGNOSTIC_ASSERT(eventWnd
,
560 "CreateWindowW for EventWindow failed twice");
562 auto const _cssdErr
[[maybe_unused
]] = WinErrorState::Get();
563 MOZ_DIAGNOSTIC_ASSERT(
564 rv
== WindowsDiagnosticsError::None
,
565 "Failed to collect single step data for CreateWindowW");
566 // If we reach this point then somehow the single-stepped call succeeded and
569 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED && _M_X64
571 MOZ_RELEASE_ASSERT(eventWnd
, "CreateWindowW for EventWindow failed");
576 HWND
nsAppShell::sPrecachedEventWnd
{};
578 /* static */ bool nsAppShell::PrecacheEventWindow() {
579 MOZ_ASSERT(NS_IsMainThread());
580 MOZ_RELEASE_ASSERT(!sPrecachedEventWnd
);
582 sPrecachedEventWnd
= StaticCreateEventWindow();
583 return static_cast<bool>(sPrecachedEventWnd
);
586 nsresult
nsAppShell::InitEventWindow() {
587 MOZ_ASSERT(NS_IsMainThread());
589 if (sPrecachedEventWnd
) {
590 mEventWnd
= sPrecachedEventWnd
;
591 sPrecachedEventWnd
= nullptr;
593 mEventWnd
= StaticCreateEventWindow();
596 mLastNativeEventScheduled
= TimeStamp::NowLoRes();
598 NS_ENSURE_STATE(mEventWnd
);
603 nsresult
nsAppShell::Init() {
608 if (XRE_IsParentProcess()) {
609 sTaskbarButtonCreatedMsg
= ::RegisterWindowMessageW(kTaskbarButtonEventId
);
610 NS_ASSERTION(sTaskbarButtonCreatedMsg
,
611 "Could not register taskbar button creation message");
614 // The hidden message window is used for interrupting the processing of native
615 // events, so that we can process gecko events. Therefore, we only need it if
616 // we are processing native events. Disabling this is required for win32k
618 if (XRE_UseNativeEventProcessing()) {
619 if (nsresult rv
= this->InitEventWindow(); NS_FAILED(rv
)) {
622 } else if (XRE_IsContentProcess() && !IsWin32kLockedDown()) {
623 // We're not generally processing native events, but still using GDI and we
624 // still have some internal windows, e.g. from calling CoInitializeEx.
625 // So we use a class that will do a single event pump where previously we
626 // might have processed multiple events to make sure any occasional messages
627 // to these windows are processed. This also allows any internal Windows
628 // messages to be processed to ensure the GDI data remains fresh.
629 nsCOMPtr
<nsIThreadInternal
> threadInt
=
630 do_QueryInterface(NS_GetCurrentThread());
632 threadInt
->SetObserver(new SingleNativeEventPump());
636 if (XRE_IsParentProcess()) {
637 ScreenManager
& screenManager
= ScreenManager::GetSingleton();
638 if (gfxPlatform::IsHeadless()) {
639 screenManager
.SetHelper(mozilla::MakeUnique
<HeadlessScreenHelper
>());
641 screenManager
.SetHelper(mozilla::MakeUnique
<ScreenHelperWin
>());
642 ScreenHelperWin::RefreshScreens();
645 nsCOMPtr
<nsIObserverService
> obsServ(
646 mozilla::services::GetObserverService());
648 obsServ
->AddObserver(this, "sessionstore-restoring-on-startup", false);
649 obsServ
->AddObserver(this, "sessionstore-windows-restored", false);
652 if (!WinUtils::GetTimezoneName(mTimezoneName
)) {
653 NS_WARNING("Unable to get system timezone name, timezone may be invalid\n");
656 return nsBaseAppShell::Init();
660 nsAppShell::Run(void) {
661 bool wantAudio
= true;
662 if (XRE_IsParentProcess()) {
663 #ifdef MOZ_BACKGROUNDTASKS
664 if (BackgroundTasks::IsBackgroundTaskMode()) {
668 if (MOZ_LIKELY(wantAudio
)) {
669 mozilla::widget::StartAudioSession();
672 // Add an observer that disables the screen saver when requested by Gecko.
673 // For example when we're playing video in the foreground tab. Whole firefox
674 // only needs one wakelock instance, so we would only create one listener in
675 // chrome process to prevent requesting unnecessary wakelock.
676 AddScreenWakeLockListener();
679 nsresult rv
= nsBaseAppShell::Run();
681 if (XRE_IsParentProcess()) {
682 RemoveScreenWakeLockListener();
684 if (MOZ_LIKELY(wantAudio
)) {
685 mozilla::widget::StopAudioSession();
692 void nsAppShell::DoProcessMoreGeckoEvents() {
693 // Called by nsBaseAppShell's NativeEventCallback() after it has finished
694 // processing pending gecko events and there are still gecko events pending
695 // for the thread. (This can happen if NS_ProcessPendingEvents reached it's
696 // starvation timeout limit.) The default behavior in nsBaseAppShell is to
697 // call ScheduleNativeEventCallback to post a follow up native event callback
698 // message. This triggers an additional call to NativeEventCallback for more
699 // gecko event processing.
701 // There's a deadlock risk here with certain internal Windows modal loops. In
702 // our dispatch code, we prioritize messages so that input is handled first.
703 // However Windows modal dispatch loops often prioritize posted messages. If
704 // we find ourselves in a tight gecko timer loop where NS_ProcessPendingEvents
705 // takes longer than the timer duration, NS_HasPendingEvents(thread) will
706 // always be true. ScheduleNativeEventCallback will be called on every
707 // NativeEventCallback callback, and in a Windows modal dispatch loop, the
708 // callback message will be processed first -> input gets starved, dead lock.
710 // To avoid, don't post native callback messages from NativeEventCallback
711 // when we're in a modal loop. This gets us back into the Windows modal
712 // dispatch loop dispatching input messages. Once we drop out of the modal
713 // loop, we use mNativeCallbackPending to fire off a final NativeEventCallback
714 // if we need it, which insures NS_ProcessPendingEvents gets called and all
715 // gecko events get processed.
716 if (mEventloopNestingLevel
< 2) {
718 mNativeCallbackPending
= false;
720 mNativeCallbackPending
= true;
724 void nsAppShell::ScheduleNativeEventCallback() {
725 MOZ_ASSERT(mEventWnd
,
726 "We should have created mEventWnd in Init, if this is called.");
728 // Post a message to the hidden message window
729 ++sOutstandingNativeEventCallbacks
;
731 MutexAutoLock
lock(mLastNativeEventScheduledMutex
);
732 // Time stamp this event so we can detect cases where the event gets
733 // dropping in sub classes / modal loops we do not control.
734 mLastNativeEventScheduled
= TimeStamp::NowLoRes();
736 ::PostMessage(mEventWnd
, sAppShellGeckoMsgId
, 0,
737 reinterpret_cast<LPARAM
>(this));
740 bool nsAppShell::ProcessNextNativeEvent(bool mayWait
) {
741 // Notify ipc we are spinning a (possibly nested) gecko event loop.
742 mozilla::ipc::MessageChannel::NotifyGeckoEventDispatch();
744 bool gotMessage
= false;
749 // For avoiding deadlock between our process and plugin process by
750 // mouse wheel messages, we're handling actually when we receive one of
751 // following internal messages which is posted by native mouse wheel
752 // message handler. Any other events, especially native modifier key
753 // events, should not be handled between native message and posted
754 // internal message because it may make different modifier key state or
755 // mouse cursor position between them.
756 if (mozilla::widget::MouseScrollHandler::IsWaitingInternalMessage()) {
757 gotMessage
= WinUtils::PeekMessage(&msg
, nullptr, MOZ_WM_MOUSEWHEEL_FIRST
,
758 MOZ_WM_MOUSEWHEEL_LAST
, PM_REMOVE
);
759 NS_ASSERTION(gotMessage
,
760 "waiting internal wheel message, but it has not come");
764 gotMessage
= WinUtils::PeekMessage(&msg
, nullptr, 0, 0, PM_REMOVE
);
768 if (msg
.message
== WM_QUIT
) {
769 ::PostQuitMessage(msg
.wParam
);
772 // If we had UI activity we would be processing it now so we know we
773 // have either kUIActivity or kActivityNoUIAVail.
774 mozilla::BackgroundHangMonitor().NotifyActivity();
776 if (msg
.message
>= WM_KEYFIRST
&& msg
.message
<= WM_KEYLAST
&&
777 IMEHandler::ProcessRawKeyMessage(msg
)) {
778 continue; // the message is consumed.
782 // Store Printer dialog messages for reposting on x86, because on x86
783 // Windows 7 they are not processed by a window procedure, but are
784 // explicitly waited for in the winspool.drv code that will be further
785 // up the stack (winspool!WaitForCompletionMessage). These are
786 // undocumented Windows Message identifiers found in winspool.drv.
787 if (msg
.message
== 0x5b7a || msg
.message
== 0x5b7f ||
788 msg
.message
== 0x5b80 || msg
.message
== 0x5b81) {
789 mMsgsToRepost
.push_back(msg
);
794 // Windows documentation suggets that WM_SETTINGSCHANGE is the message
795 // to watch for timezone changes, but experimentation showed that it
796 // doesn't fire on changing the timezone, but that WM_TIMECHANGE does,
797 // even if there's no immediate effect on the clock (e.g., changing
798 // from Pacific Daylight at UTC-7 to Arizona at UTC-7).
799 if (msg
.message
== WM_TIMECHANGE
) {
800 // The message may not give us sufficient information to determine
801 // if the timezone changed, so keep track of it ourselves.
802 wchar_t systemTimezone
[128];
803 bool getSystemTimeSucceeded
=
804 WinUtils::GetTimezoneName(systemTimezone
);
805 if (getSystemTimeSucceeded
&& wcscmp(systemTimezone
, mTimezoneName
)) {
806 nsBaseAppShell::OnSystemTimezoneChange();
808 wcscpy_s(mTimezoneName
, 128, systemTimezone
);
812 ::TranslateMessage(&msg
);
813 ::DispatchMessageW(&msg
);
815 } else if (mayWait
) {
816 // Block and wait for any posted application message
817 mozilla::BackgroundHangMonitor().NotifyWait();
819 AUTO_PROFILER_LABEL("nsAppShell::ProcessNextNativeEvent::Wait", IDLE
);
820 WinUtils::WaitForMessage();
823 } while (!gotMessage
&& mayWait
);
825 // See DoProcessNextNativeEvent, mEventloopNestingLevel will be
826 // one when a modal loop unwinds.
827 if (mNativeCallbackPending
&& mEventloopNestingLevel
== 1)
828 DoProcessMoreGeckoEvents();
830 // Check for starved native callbacks. If we haven't processed one
831 // of these events in NATIVE_EVENT_STARVATION_LIMIT, fire one off.
832 static const mozilla::TimeDuration nativeEventStarvationLimit
=
833 mozilla::TimeDuration::FromSeconds(NATIVE_EVENT_STARVATION_LIMIT
);
835 TimeDuration timeSinceLastNativeEventScheduled
;
837 MutexAutoLock
lock(mLastNativeEventScheduledMutex
);
838 timeSinceLastNativeEventScheduled
=
839 TimeStamp::NowLoRes() - mLastNativeEventScheduled
;
841 if (timeSinceLastNativeEventScheduled
> nativeEventStarvationLimit
) {
842 ScheduleNativeEventCallback();
848 nsresult
nsAppShell::AfterProcessNextEvent(nsIThreadInternal
* /* unused */,
850 if (!mMsgsToRepost
.empty()) {
851 for (MSG msg
: mMsgsToRepost
) {
852 ::PostMessageW(msg
.hwnd
, msg
.message
, msg
.wParam
, msg
.lParam
);
854 mMsgsToRepost
.clear();