Bumping manifests a=b2g-bump
[gecko.git] / widget / windows / nsAppShell.cpp
blob3dc047f7e420efb6c9e6068e37f29a3e5c0b41e0
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 "mozilla/ipc/WindowsMessageLoop.h"
8 #include "nsAppShell.h"
9 #include "nsToolkit.h"
10 #include "nsThreadUtils.h"
11 #include "WinUtils.h"
12 #include "WinTaskbar.h"
13 #include "WinMouseScrollHandler.h"
14 #include "nsWindowDefs.h"
15 #include "nsString.h"
16 #include "WinIMEHandler.h"
17 #include "mozilla/widget/AudioSession.h"
18 #include "mozilla/HangMonitor.h"
19 #include "nsIDOMWakeLockListener.h"
20 #include "nsIPowerManagerService.h"
21 #include "mozilla/StaticPtr.h"
22 #include "nsTHashtable.h"
23 #include "nsHashKeys.h"
24 #include "GeckoProfiler.h"
26 using namespace mozilla;
27 using namespace mozilla::widget;
29 // A wake lock listener that disables screen saver when requested by
30 // Gecko. For example when we're playing video in a foreground tab we
31 // don't want the screen saver to turn on.
32 class WinWakeLockListener : public nsIDOMMozWakeLockListener {
33 public:
34 NS_DECL_ISUPPORTS;
36 private:
37 ~WinWakeLockListener() {}
39 NS_IMETHOD Callback(const nsAString& aTopic, const nsAString& aState) {
40 bool isLocked = mLockedTopics.Contains(aTopic);
41 bool shouldLock = aState.EqualsLiteral("locked-foreground");
42 if (isLocked == shouldLock) {
43 return NS_OK;
45 if (shouldLock) {
46 if (!mLockedTopics.Count()) {
47 // This is the first topic to request the screen saver be disabled.
48 // Prevent screen saver.
49 SetThreadExecutionState(ES_DISPLAY_REQUIRED|ES_CONTINUOUS);
51 mLockedTopics.PutEntry(aTopic);
52 } else {
53 mLockedTopics.RemoveEntry(aTopic);
54 if (!mLockedTopics.Count()) {
55 // No other outstanding topics have requested screen saver be disabled.
56 // Re-enable screen saver.
57 SetThreadExecutionState(ES_CONTINUOUS);
60 return NS_OK;
63 // Keep track of all the topics that have requested a wake lock. When the
64 // number of topics in the hashtable reaches zero, we can uninhibit the
65 // screensaver again.
66 nsTHashtable<nsStringHashKey> mLockedTopics;
69 NS_IMPL_ISUPPORTS(WinWakeLockListener, nsIDOMMozWakeLockListener)
70 StaticRefPtr<WinWakeLockListener> sWakeLockListener;
72 static void
73 AddScreenWakeLockListener()
75 nsCOMPtr<nsIPowerManagerService> sPowerManagerService = do_GetService(POWERMANAGERSERVICE_CONTRACTID);
76 if (sPowerManagerService) {
77 sWakeLockListener = new WinWakeLockListener();
78 sPowerManagerService->AddWakeLockListener(sWakeLockListener);
79 } else {
80 NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!");
84 static void
85 RemoveScreenWakeLockListener()
87 nsCOMPtr<nsIPowerManagerService> sPowerManagerService = do_GetService(POWERMANAGERSERVICE_CONTRACTID);
88 if (sPowerManagerService) {
89 sPowerManagerService->RemoveWakeLockListener(sWakeLockListener);
90 sPowerManagerService = nullptr;
91 sWakeLockListener = nullptr;
95 namespace mozilla {
96 namespace widget {
97 // Native event callback message.
98 UINT sAppShellGeckoMsgId = RegisterWindowMessageW(L"nsAppShell:EventID");
99 } }
101 const wchar_t* kTaskbarButtonEventId = L"TaskbarButtonCreated";
102 UINT sTaskbarButtonCreatedMsg;
104 /* static */
105 UINT nsAppShell::GetTaskbarButtonCreatedMessage() {
106 return sTaskbarButtonCreatedMsg;
109 namespace mozilla {
110 namespace crashreporter {
111 void LSPAnnotate();
112 } // namespace crashreporter
113 } // namespace mozilla
115 using mozilla::crashreporter::LSPAnnotate;
117 //-------------------------------------------------------------------------
119 /*static*/ LRESULT CALLBACK
120 nsAppShell::EventWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
122 if (uMsg == sAppShellGeckoMsgId) {
123 nsAppShell *as = reinterpret_cast<nsAppShell *>(lParam);
124 as->NativeEventCallback();
125 NS_RELEASE(as);
126 return TRUE;
128 return DefWindowProc(hwnd, uMsg, wParam, lParam);
131 nsAppShell::~nsAppShell()
133 if (mEventWnd) {
134 // DestroyWindow doesn't do anything when called from a non UI thread.
135 // Since mEventWnd was created on the UI thread, it must be destroyed on
136 // the UI thread.
137 SendMessage(mEventWnd, WM_CLOSE, 0, 0);
141 nsresult
142 nsAppShell::Init()
144 #ifdef MOZ_CRASHREPORTER
145 LSPAnnotate();
146 #endif
148 mLastNativeEventScheduled = TimeStamp::NowLoRes();
150 mozilla::ipc::windows::InitUIThread();
152 sTaskbarButtonCreatedMsg = ::RegisterWindowMessageW(kTaskbarButtonEventId);
153 NS_ASSERTION(sTaskbarButtonCreatedMsg, "Could not register taskbar button creation message");
155 WNDCLASSW wc;
156 HINSTANCE module = GetModuleHandle(nullptr);
158 const wchar_t *const kWindowClass = L"nsAppShell:EventWindowClass";
159 if (!GetClassInfoW(module, kWindowClass, &wc)) {
160 wc.style = 0;
161 wc.lpfnWndProc = EventWindowProc;
162 wc.cbClsExtra = 0;
163 wc.cbWndExtra = 0;
164 wc.hInstance = module;
165 wc.hIcon = nullptr;
166 wc.hCursor = nullptr;
167 wc.hbrBackground = (HBRUSH) nullptr;
168 wc.lpszMenuName = (LPCWSTR) nullptr;
169 wc.lpszClassName = kWindowClass;
170 RegisterClassW(&wc);
173 mEventWnd = CreateWindowW(kWindowClass, L"nsAppShell:EventWindow",
174 0, 0, 0, 10, 10, nullptr, nullptr, module, nullptr);
175 NS_ENSURE_STATE(mEventWnd);
177 return nsBaseAppShell::Init();
180 NS_IMETHODIMP
181 nsAppShell::Run(void)
183 // Ignore failure; failing to start the application is not exactly an
184 // appropriate response to failing to start an audio session.
185 mozilla::widget::StartAudioSession();
187 // Add an observer that disables the screen saver when requested by Gecko.
188 // For example when we're playing video in the foreground tab.
189 AddScreenWakeLockListener();
191 nsresult rv = nsBaseAppShell::Run();
193 RemoveScreenWakeLockListener();
195 mozilla::widget::StopAudioSession();
197 return rv;
200 NS_IMETHODIMP
201 nsAppShell::Exit(void)
203 return nsBaseAppShell::Exit();
206 void
207 nsAppShell::DoProcessMoreGeckoEvents()
209 // Called by nsBaseAppShell's NativeEventCallback() after it has finished
210 // processing pending gecko events and there are still gecko events pending
211 // for the thread. (This can happen if NS_ProcessPendingEvents reached it's
212 // starvation timeout limit.) The default behavior in nsBaseAppShell is to
213 // call ScheduleNativeEventCallback to post a follow up native event callback
214 // message. This triggers an additional call to NativeEventCallback for more
215 // gecko event processing.
217 // There's a deadlock risk here with certain internal Windows modal loops. In
218 // our dispatch code, we prioritize messages so that input is handled first.
219 // However Windows modal dispatch loops often prioritize posted messages. If
220 // we find ourselves in a tight gecko timer loop where NS_ProcessPendingEvents
221 // takes longer than the timer duration, NS_HasPendingEvents(thread) will
222 // always be true. ScheduleNativeEventCallback will be called on every
223 // NativeEventCallback callback, and in a Windows modal dispatch loop, the
224 // callback message will be processed first -> input gets starved, dead lock.
226 // To avoid, don't post native callback messages from NativeEventCallback
227 // when we're in a modal loop. This gets us back into the Windows modal
228 // dispatch loop dispatching input messages. Once we drop out of the modal
229 // loop, we use mNativeCallbackPending to fire off a final NativeEventCallback
230 // if we need it, which insures NS_ProcessPendingEvents gets called and all
231 // gecko events get processed.
232 if (mEventloopNestingLevel < 2) {
233 OnDispatchedEvent(nullptr);
234 mNativeCallbackPending = false;
235 } else {
236 mNativeCallbackPending = true;
240 void
241 nsAppShell::ScheduleNativeEventCallback()
243 // Post a message to the hidden message window
244 NS_ADDREF_THIS(); // will be released when the event is processed
246 MutexAutoLock lock(mLastNativeEventScheduledMutex);
247 // Time stamp this event so we can detect cases where the event gets
248 // dropping in sub classes / modal loops we do not control.
249 mLastNativeEventScheduled = TimeStamp::NowLoRes();
251 ::PostMessage(mEventWnd, sAppShellGeckoMsgId, 0, reinterpret_cast<LPARAM>(this));
254 bool
255 nsAppShell::ProcessNextNativeEvent(bool mayWait)
257 // Notify ipc we are spinning a (possibly nested) gecko event loop.
258 mozilla::ipc::MessageChannel::NotifyGeckoEventDispatch();
260 bool gotMessage = false;
262 do {
263 MSG msg;
264 bool uiMessage = false;
266 // For avoiding deadlock between our process and plugin process by
267 // mouse wheel messages, we're handling actually when we receive one of
268 // following internal messages which is posted by native mouse wheel
269 // message handler. Any other events, especially native modifier key
270 // events, should not be handled between native message and posted
271 // internal message because it may make different modifier key state or
272 // mouse cursor position between them.
273 if (mozilla::widget::MouseScrollHandler::IsWaitingInternalMessage()) {
274 gotMessage = WinUtils::PeekMessage(&msg, nullptr, MOZ_WM_MOUSEWHEEL_FIRST,
275 MOZ_WM_MOUSEWHEEL_LAST, PM_REMOVE);
276 NS_ASSERTION(gotMessage,
277 "waiting internal wheel message, but it has not come");
278 uiMessage = gotMessage;
281 if (!gotMessage) {
282 gotMessage = WinUtils::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE);
283 uiMessage =
284 (msg.message >= WM_KEYFIRST && msg.message <= WM_IME_KEYLAST) ||
285 (msg.message >= NS_WM_IMEFIRST && msg.message <= NS_WM_IMELAST) ||
286 (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST);
289 if (gotMessage) {
290 if (msg.message == WM_QUIT) {
291 ::PostQuitMessage(msg.wParam);
292 Exit();
293 } else {
294 // If we had UI activity we would be processing it now so we know we
295 // have either kUIActivity or kActivityNoUIAVail.
296 mozilla::HangMonitor::NotifyActivity(
297 uiMessage ? mozilla::HangMonitor::kUIActivity :
298 mozilla::HangMonitor::kActivityNoUIAVail);
300 if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST &&
301 IMEHandler::ProcessRawKeyMessage(msg)) {
302 continue; // the message is consumed.
305 ::TranslateMessage(&msg);
306 ::DispatchMessageW(&msg);
308 } else if (mayWait) {
309 // Block and wait for any posted application message
310 mozilla::HangMonitor::Suspend();
312 GeckoProfilerSleepRAII profiler_sleep;
313 WinUtils::WaitForMessage();
316 } while (!gotMessage && mayWait);
318 // See DoProcessNextNativeEvent, mEventloopNestingLevel will be
319 // one when a modal loop unwinds.
320 if (mNativeCallbackPending && mEventloopNestingLevel == 1)
321 DoProcessMoreGeckoEvents();
323 // Check for starved native callbacks. If we haven't processed one
324 // of these events in NATIVE_EVENT_STARVATION_LIMIT, fire one off.
325 static const mozilla::TimeDuration nativeEventStarvationLimit =
326 mozilla::TimeDuration::FromSeconds(NATIVE_EVENT_STARVATION_LIMIT);
328 TimeDuration timeSinceLastNativeEventScheduled;
330 MutexAutoLock lock(mLastNativeEventScheduledMutex);
331 timeSinceLastNativeEventScheduled =
332 TimeStamp::NowLoRes() - mLastNativeEventScheduled;
334 if (timeSinceLastNativeEventScheduled > nativeEventStarvationLimit) {
335 ScheduleNativeEventCallback();
338 return gotMessage;