Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / widget / windows / nsAppShell.cpp
blob5c55bfa205ae42a84bba75ddcfe45545ea688251
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/WindowsDiagnostics.h"
36 #include "mozilla/WindowsProcessMitigations.h"
38 #include <winternl.h>
40 #ifdef MOZ_BACKGROUNDTASKS
41 # include "mozilla/BackgroundTasks.h"
42 #endif
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 {
58 public:
59 NS_DECL_ISUPPORTS
60 WinWakeLockListener() { MOZ_ASSERT(XRE_IsParentProcess()); }
62 private:
63 ~WinWakeLockListener() {
64 ReleaseWakelockIfNeeded(PowerRequestDisplayRequired);
65 ReleaseWakelockIfNeeded(PowerRequestExecutionRequired);
68 void SetHandle(HANDLE aHandle, POWER_REQUEST_TYPE aType) {
69 switch (aType) {
70 case PowerRequestDisplayRequired: {
71 if (!aHandle && mDisplayHandle) {
72 CloseHandle(mDisplayHandle);
74 mDisplayHandle = aHandle;
75 return;
77 case PowerRequestExecutionRequired: {
78 if (!aHandle && mNonDisplayHandle) {
79 CloseHandle(mNonDisplayHandle);
81 mNonDisplayHandle = aHandle;
82 return;
84 default:
85 MOZ_ASSERT_UNREACHABLE("Invalid request type");
86 return;
90 HANDLE GetHandle(POWER_REQUEST_TYPE aType) const {
91 switch (aType) {
92 case PowerRequestDisplayRequired:
93 return mDisplayHandle;
94 case PowerRequestExecutionRequired:
95 return mNonDisplayHandle;
96 default:
97 MOZ_ASSERT_UNREACHABLE("Invalid request type");
98 return nullptr;
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);
109 if (!handle) {
110 WAKE_LOCK_LOG("Failed to create handle for %s, error=%lu",
111 RequestTypeStr(aType), GetLastError());
112 return nullptr;
114 SetHandle(handle, aType);
115 return handle;
118 LPWSTR RequestTypeLPWSTR(POWER_REQUEST_TYPE aType) const {
119 switch (aType) {
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
125 default:
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 {
132 switch (aType) {
133 case PowerRequestDisplayRequired:
134 return "display request";
135 case PowerRequestExecutionRequired:
136 return "non-display request";
137 default:
138 MOZ_ASSERT_UNREACHABLE("Invalid request type");
139 return "unknown";
143 void RequestWakelockIfNeeded(POWER_REQUEST_TYPE aType) {
144 if (GetHandle(aType)) {
145 WAKE_LOCK_LOG("Already requested lock for %s", RequestTypeStr(aType));
146 return;
149 WAKE_LOCK_LOG("Prepare a wakelock for %s", RequestTypeStr(aType));
150 HANDLE handle = CreateHandle(aType);
151 if (!handle) {
152 WAKE_LOCK_LOG("Failed due to no handle for %s", RequestTypeStr(aType));
153 return;
156 if (PowerSetRequest(handle, aType)) {
157 WAKE_LOCK_LOG("Requested %s lock", RequestTypeStr(aType));
158 } else {
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));
168 return;
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());
175 return;
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")) {
187 return NS_OK;
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");
195 } else {
196 requestLock = aState.EqualsASCII("locked-foreground");
199 if (isNonDisplayLock) {
200 if (requestLock) {
201 RequestWakelockIfNeeded(PowerRequestExecutionRequired);
202 } else {
203 ReleaseWakelockIfNeeded(PowerRequestExecutionRequired);
205 } else {
206 if (requestLock) {
207 RequestWakelockIfNeeded(PowerRequestDisplayRequired);
208 } else {
209 ReleaseWakelockIfNeeded(PowerRequestDisplayRequired);
212 return NS_OK;
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);
228 } else {
229 NS_WARNING(
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 {
245 public:
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.");
254 private:
255 ~SingleNativeEventPump() {}
258 NS_IMPL_ISUPPORTS(SingleNativeEventPump, nsIThreadObserver)
260 NS_IMETHODIMP
261 SingleNativeEventPump::OnDispatchedEvent() { return NS_OK; }
263 NS_IMETHODIMP
264 SingleNativeEventPump::OnProcessNextEvent(nsIThreadInternal* aThread,
265 bool aMayWait) {
266 MSG msg;
267 bool gotMessage = WinUtils::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE);
268 if (gotMessage) {
269 ::TranslateMessage(&msg);
270 ::DispatchMessageW(&msg);
272 return NS_OK;
275 NS_IMETHODIMP
276 SingleNativeEventPump::AfterProcessNextEvent(nsIThreadInternal* aThread,
277 bool aMayWait) {
278 return NS_OK;
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
289 /* static */
290 UINT nsAppShell::GetTaskbarButtonCreatedMessage() {
291 return sTaskbarButtonCreatedMsg;
294 namespace mozilla {
295 namespace crashreporter {
296 void LSPAnnotate();
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,
309 WPARAM wParam,
310 LPARAM lParam) {
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) {
317 return TRUE;
320 nsAppShell* as = reinterpret_cast<nsAppShell*>(lParam);
321 as->NativeEventCallback();
322 --sOutstandingNativeEventCallbacks;
323 return TRUE;
326 LRESULT ret = DefWindowProc(hwnd, uMsg, wParam, lParam);
327 eventLogger.SetResult(ret, false);
328 return ret;
331 nsAppShell::~nsAppShell() {
332 hal::Shutdown();
334 if (mEventWnd) {
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
337 // the UI thread.
338 SendMessage(mEventWnd, WM_CLOSE, 0, 0);
341 // Cancel any outstanding native event callbacks.
342 sOutstandingNativeEventCallbacks = 0;
345 NS_IMETHODIMP
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");
356 return NS_OK;
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");
363 return NS_OK;
367 return nsBaseAppShell::Observe(aSubject, aTopic, aData);
370 namespace {
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.)
376 UINT in_use = 0;
377 // Number of atoms confirmed not in use.
378 UINT free = 0;
379 // Number of atoms which gave errors when checked.
380 UINT errors = 0;
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.
414 WCHAR buf[2] = {};
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) {
421 ++retval.in_use;
422 } else if (errState == kInvalidAtom) {
423 ++retval.free;
424 } else {
425 // Unexpected error-state.
426 ++retval.errors;
427 retval.lastErrorAtom = atom;
428 retval.lastErrorState = errState;
432 return retval;
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
451 } // namespace
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);
479 if (_msgId) {
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!");
488 WNDCLASSW wc;
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]];
499 if (!_gciwRet) {
500 wc.style = 0;
501 wc.lpfnWndProc = EventWindowProc;
502 wc.cbClsExtra = 0;
503 wc.cbWndExtra = 0;
504 wc.hInstance = module;
505 wc.hIcon = nullptr;
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(
526 _windowClassAtom,
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();
543 HWND eventWnd =
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)
549 if (!eventWnd) {
550 // Retry with single-step data collection
551 WindowsDiagnosticsError rv = CollectUser32SingleStepData(
552 [module, &eventWnd]() {
553 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
567 // we can proceed
569 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED && _M_X64
571 MOZ_RELEASE_ASSERT(eventWnd, "CreateWindowW for EventWindow failed");
573 return eventWnd;
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;
592 } else {
593 mEventWnd = StaticCreateEventWindow();
596 mLastNativeEventScheduled = TimeStamp::NowLoRes();
598 NS_ENSURE_STATE(mEventWnd);
600 return NS_OK;
603 nsresult nsAppShell::Init() {
604 LSPAnnotate();
606 hal::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
617 // syscall lockdown.
618 if (XRE_UseNativeEventProcessing()) {
619 if (nsresult rv = this->InitEventWindow(); NS_FAILED(rv)) {
620 return 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());
631 if (threadInt) {
632 threadInt->SetObserver(new SingleNativeEventPump());
636 if (XRE_IsParentProcess()) {
637 ScreenManager& screenManager = ScreenManager::GetSingleton();
638 if (gfxPlatform::IsHeadless()) {
639 screenManager.SetHelper(mozilla::MakeUnique<HeadlessScreenHelper>());
640 } else {
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();
659 NS_IMETHODIMP
660 nsAppShell::Run(void) {
661 bool wantAudio = true;
662 if (XRE_IsParentProcess()) {
663 #ifdef MOZ_BACKGROUNDTASKS
664 if (BackgroundTasks::IsBackgroundTaskMode()) {
665 wantAudio = false;
667 #endif
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();
689 return rv;
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) {
717 OnDispatchedEvent();
718 mNativeCallbackPending = false;
719 } else {
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;
746 do {
747 MSG msg;
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");
763 if (!gotMessage) {
764 gotMessage = WinUtils::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE);
767 if (gotMessage) {
768 if (msg.message == WM_QUIT) {
769 ::PostQuitMessage(msg.wParam);
770 Exit();
771 } else {
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.
781 #if defined(_X86_)
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);
790 continue;
792 #endif
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();
845 return gotMessage;
848 nsresult nsAppShell::AfterProcessNextEvent(nsIThreadInternal* /* unused */,
849 bool /* unused */) {
850 if (!mMsgsToRepost.empty()) {
851 for (MSG msg : mMsgsToRepost) {
852 ::PostMessageW(msg.hwnd, msg.message, msg.wParam, msg.lParam);
854 mMsgsToRepost.clear();
856 return NS_OK;