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/ipc/MessageChannel.h"
7 #include "nsAppShell.h"
9 #include "nsThreadUtils.h"
11 #include "WinTaskbar.h"
12 #include "WinMouseScrollHandler.h"
13 #include "nsWindowDefs.h"
15 #include "WinIMEHandler.h"
16 #include "mozilla/widget/AudioSession.h"
17 #include "mozilla/HangMonitor.h"
19 using namespace mozilla
;
20 using namespace mozilla::widget
;
24 // Native event callback message.
25 UINT sAppShellGeckoMsgId
= RegisterWindowMessageW(L
"nsAppShell:EventID");
28 const wchar_t* kTaskbarButtonEventId
= L
"TaskbarButtonCreated";
29 UINT sTaskbarButtonCreatedMsg
;
32 UINT
nsAppShell::GetTaskbarButtonCreatedMessage() {
33 return sTaskbarButtonCreatedMsg
;
37 namespace crashreporter
{
39 } // namespace crashreporter
40 } // namespace mozilla
42 using mozilla::crashreporter::LSPAnnotate
;
44 //-------------------------------------------------------------------------
46 /*static*/ LRESULT CALLBACK
47 nsAppShell::EventWindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
49 if (uMsg
== sAppShellGeckoMsgId
) {
50 nsAppShell
*as
= reinterpret_cast<nsAppShell
*>(lParam
);
51 as
->NativeEventCallback();
55 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
58 nsAppShell::~nsAppShell()
61 // DestroyWindow doesn't do anything when called from a non UI thread.
62 // Since mEventWnd was created on the UI thread, it must be destroyed on
64 SendMessage(mEventWnd
, WM_CLOSE
, 0, 0);
71 #ifdef MOZ_CRASHREPORTER
75 mLastNativeEventScheduled
= TimeStamp::NowLoRes();
77 sTaskbarButtonCreatedMsg
= ::RegisterWindowMessageW(kTaskbarButtonEventId
);
78 NS_ASSERTION(sTaskbarButtonCreatedMsg
, "Could not register taskbar button creation message");
81 HINSTANCE module
= GetModuleHandle(nullptr);
83 const wchar_t *const kWindowClass
= L
"nsAppShell:EventWindowClass";
84 if (!GetClassInfoW(module
, kWindowClass
, &wc
)) {
86 wc
.lpfnWndProc
= EventWindowProc
;
89 wc
.hInstance
= module
;
92 wc
.hbrBackground
= (HBRUSH
) nullptr;
93 wc
.lpszMenuName
= (LPCWSTR
) nullptr;
94 wc
.lpszClassName
= kWindowClass
;
98 mEventWnd
= CreateWindowW(kWindowClass
, L
"nsAppShell:EventWindow",
99 0, 0, 0, 10, 10, nullptr, nullptr, module
, nullptr);
100 NS_ENSURE_STATE(mEventWnd
);
102 return nsBaseAppShell::Init();
107 nsAppShell::Run(void)
109 // Ignore failure; failing to start the application is not exactly an
110 // appropriate response to failing to start an audio session.
111 mozilla::widget::StartAudioSession();
113 nsresult rv
= nsBaseAppShell::Run();
115 mozilla::widget::StopAudioSession();
121 nsAppShell::Exit(void)
123 return nsBaseAppShell::Exit();
127 nsAppShell::DoProcessMoreGeckoEvents()
129 // Called by nsBaseAppShell's NativeEventCallback() after it has finished
130 // processing pending gecko events and there are still gecko events pending
131 // for the thread. (This can happen if NS_ProcessPendingEvents reached it's
132 // starvation timeout limit.) The default behavior in nsBaseAppShell is to
133 // call ScheduleNativeEventCallback to post a follow up native event callback
134 // message. This triggers an additional call to NativeEventCallback for more
135 // gecko event processing.
137 // There's a deadlock risk here with certain internal Windows modal loops. In
138 // our dispatch code, we prioritize messages so that input is handled first.
139 // However Windows modal dispatch loops often prioritize posted messages. If
140 // we find ourselves in a tight gecko timer loop where NS_ProcessPendingEvents
141 // takes longer than the timer duration, NS_HasPendingEvents(thread) will
142 // always be true. ScheduleNativeEventCallback will be called on every
143 // NativeEventCallback callback, and in a Windows modal dispatch loop, the
144 // callback message will be processed first -> input gets starved, dead lock.
146 // To avoid, don't post native callback messages from NativeEventCallback
147 // when we're in a modal loop. This gets us back into the Windows modal
148 // dispatch loop dispatching input messages. Once we drop out of the modal
149 // loop, we use mNativeCallbackPending to fire off a final NativeEventCallback
150 // if we need it, which insures NS_ProcessPendingEvents gets called and all
151 // gecko events get processed.
152 if (mEventloopNestingLevel
< 2) {
153 OnDispatchedEvent(nullptr);
154 mNativeCallbackPending
= false;
156 mNativeCallbackPending
= true;
161 nsAppShell::ScheduleNativeEventCallback()
163 // Post a message to the hidden message window
164 NS_ADDREF_THIS(); // will be released when the event is processed
166 MutexAutoLock
lock(mLastNativeEventScheduledMutex
);
167 // Time stamp this event so we can detect cases where the event gets
168 // dropping in sub classes / modal loops we do not control.
169 mLastNativeEventScheduled
= TimeStamp::NowLoRes();
171 ::PostMessage(mEventWnd
, sAppShellGeckoMsgId
, 0, reinterpret_cast<LPARAM
>(this));
175 nsAppShell::ProcessNextNativeEvent(bool mayWait
)
177 // Notify ipc we are spinning a (possibly nested) gecko event loop.
178 mozilla::ipc::MessageChannel::NotifyGeckoEventDispatch();
180 bool gotMessage
= false;
184 bool uiMessage
= false;
186 // For avoiding deadlock between our process and plugin process by
187 // mouse wheel messages, we're handling actually when we receive one of
188 // following internal messages which is posted by native mouse wheel
189 // message handler. Any other events, especially native modifier key
190 // events, should not be handled between native message and posted
191 // internal message because it may make different modifier key state or
192 // mouse cursor position between them.
193 if (mozilla::widget::MouseScrollHandler::IsWaitingInternalMessage()) {
194 gotMessage
= WinUtils::PeekMessage(&msg
, nullptr, MOZ_WM_MOUSEWHEEL_FIRST
,
195 MOZ_WM_MOUSEWHEEL_LAST
, PM_REMOVE
);
196 NS_ASSERTION(gotMessage
,
197 "waiting internal wheel message, but it has not come");
198 uiMessage
= gotMessage
;
202 gotMessage
= WinUtils::PeekMessage(&msg
, nullptr, 0, 0, PM_REMOVE
);
204 (msg
.message
>= WM_KEYFIRST
&& msg
.message
<= WM_IME_KEYLAST
) ||
205 (msg
.message
>= NS_WM_IMEFIRST
&& msg
.message
<= NS_WM_IMELAST
) ||
206 (msg
.message
>= WM_MOUSEFIRST
&& msg
.message
<= WM_MOUSELAST
);
210 if (msg
.message
== WM_QUIT
) {
211 ::PostQuitMessage(msg
.wParam
);
214 // If we had UI activity we would be processing it now so we know we
215 // have either kUIActivity or kActivityNoUIAVail.
216 mozilla::HangMonitor::NotifyActivity(
217 uiMessage
? mozilla::HangMonitor::kUIActivity
:
218 mozilla::HangMonitor::kActivityNoUIAVail
);
220 if (msg
.message
>= WM_KEYFIRST
&& msg
.message
<= WM_KEYLAST
&&
221 IMEHandler::ProcessRawKeyMessage(msg
)) {
222 continue; // the message is consumed.
225 ::TranslateMessage(&msg
);
226 ::DispatchMessageW(&msg
);
228 } else if (mayWait
) {
229 // Block and wait for any posted application message
230 mozilla::HangMonitor::Suspend();
231 WinUtils::WaitForMessage();
233 } while (!gotMessage
&& mayWait
);
235 // See DoProcessNextNativeEvent, mEventloopNestingLevel will be
236 // one when a modal loop unwinds.
237 if (mNativeCallbackPending
&& mEventloopNestingLevel
== 1)
238 DoProcessMoreGeckoEvents();
240 // Check for starved native callbacks. If we haven't processed one
241 // of these events in NATIVE_EVENT_STARVATION_LIMIT, fire one off.
242 static const mozilla::TimeDuration nativeEventStarvationLimit
=
243 mozilla::TimeDuration::FromSeconds(NATIVE_EVENT_STARVATION_LIMIT
);
245 TimeDuration timeSinceLastNativeEventScheduled
;
247 MutexAutoLock
lock(mLastNativeEventScheduledMutex
);
248 timeSinceLastNativeEventScheduled
=
249 TimeStamp::NowLoRes() - mLastNativeEventScheduled
;
251 if (timeSinceLastNativeEventScheduled
> nativeEventStarvationLimit
) {
252 ScheduleNativeEventCallback();