Backed out changeset 3fe07c50c854 (bug 946316) for bustage. a=backout
[gecko.git] / widget / windows / nsAppShell.cpp
blobd2a99e738546e35f23a393c5a6a0fc64c439193e
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"
8 #include "nsToolkit.h"
9 #include "nsThreadUtils.h"
10 #include "WinUtils.h"
11 #include "WinTaskbar.h"
12 #include "WinMouseScrollHandler.h"
13 #include "nsWindowDefs.h"
14 #include "nsString.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;
22 namespace mozilla {
23 namespace widget {
24 // Native event callback message.
25 UINT sAppShellGeckoMsgId = RegisterWindowMessageW(L"nsAppShell:EventID");
26 } }
28 const wchar_t* kTaskbarButtonEventId = L"TaskbarButtonCreated";
29 UINT sTaskbarButtonCreatedMsg;
31 /* static */
32 UINT nsAppShell::GetTaskbarButtonCreatedMessage() {
33 return sTaskbarButtonCreatedMsg;
36 namespace mozilla {
37 namespace crashreporter {
38 void LSPAnnotate();
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();
52 NS_RELEASE(as);
53 return TRUE;
55 return DefWindowProc(hwnd, uMsg, wParam, lParam);
58 nsAppShell::~nsAppShell()
60 if (mEventWnd) {
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
63 // the UI thread.
64 SendMessage(mEventWnd, WM_CLOSE, 0, 0);
68 nsresult
69 nsAppShell::Init()
71 #ifdef MOZ_CRASHREPORTER
72 LSPAnnotate();
73 #endif
75 mLastNativeEventScheduled = TimeStamp::NowLoRes();
77 sTaskbarButtonCreatedMsg = ::RegisterWindowMessageW(kTaskbarButtonEventId);
78 NS_ASSERTION(sTaskbarButtonCreatedMsg, "Could not register taskbar button creation message");
80 WNDCLASSW wc;
81 HINSTANCE module = GetModuleHandle(nullptr);
83 const wchar_t *const kWindowClass = L"nsAppShell:EventWindowClass";
84 if (!GetClassInfoW(module, kWindowClass, &wc)) {
85 wc.style = 0;
86 wc.lpfnWndProc = EventWindowProc;
87 wc.cbClsExtra = 0;
88 wc.cbWndExtra = 0;
89 wc.hInstance = module;
90 wc.hIcon = nullptr;
91 wc.hCursor = nullptr;
92 wc.hbrBackground = (HBRUSH) nullptr;
93 wc.lpszMenuName = (LPCWSTR) nullptr;
94 wc.lpszClassName = kWindowClass;
95 RegisterClassW(&wc);
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();
106 NS_IMETHODIMP
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();
117 return rv;
120 NS_IMETHODIMP
121 nsAppShell::Exit(void)
123 return nsBaseAppShell::Exit();
126 void
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;
155 } else {
156 mNativeCallbackPending = true;
160 void
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));
174 bool
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;
182 do {
183 MSG msg;
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;
201 if (!gotMessage) {
202 gotMessage = WinUtils::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE);
203 uiMessage =
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);
209 if (gotMessage) {
210 if (msg.message == WM_QUIT) {
211 ::PostQuitMessage(msg.wParam);
212 Exit();
213 } else {
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();
255 return gotMessage;