1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sts=2 sw=2 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /*
8 * nsWindow - Native window management and event handling.
9 *
10 * nsWindow is organized into a set of major blocks and
11 * block subsections. The layout is as follows:
12 *
13 * Includes
14 * Variables
15 * nsIWidget impl.
16 * nsIWidget methods and utilities
17 * nsSwitchToUIThread impl.
18 * nsSwitchToUIThread methods and utilities
19 * Moz events
20 * Event initialization
21 * Event dispatching
22 * Native events
23 * Wndproc(s)
24 * Event processing
25 * OnEvent event handlers
26 * IME management and accessibility
27 * Transparency
28 * Popup hook handling
29 * Misc. utilities
30 * Child window impl.
31 *
32 * Search for "BLOCK:" to find major blocks.
33 * Search for "SECTION:" to find specific sections.
34 *
35 * Blocks should be split out into separate files if they
36 * become unmanageable.
37 *
38 * Related source:
39 *
40 * nsWindowDefs.h - Definitions, macros, structs, enums
41 * and general setup.
42 * nsWindowDbg.h/.cpp - Debug related code and directives.
43 * nsWindowGfx.h/.cpp - Graphics and painting.
44 *
45 */
46
47 /**************************************************************
48 **************************************************************
49 **
50 ** BLOCK: Includes
51 **
52 ** Include headers.
53 **
54 **************************************************************
55 **************************************************************/
56
57 #include "gfx2DGlue.h"
58 #include "gfxEnv.h"
59 #include "gfxPlatform.h"
60 #include "gfxPrefs.h"
61 #include "mozilla/Logging.h"
62 #include "mozilla/MathAlgorithms.h"
63 #include "mozilla/MiscEvents.h"
64 #include "mozilla/MouseEvents.h"
65 #include "mozilla/TouchEvents.h"
66
67 #include "mozilla/ipc/MessageChannel.h"
68 #include <algorithm>
69 #include <limits>
70
71 #include "nsWindow.h"
72
73 #include <shellapi.h>
74 #include <windows.h>
75 #include <wtsapi32.h>
76 #include <process.h>
77 #include <commctrl.h>
78 #include <unknwn.h>
79 #include <psapi.h>
80
81 #include "mozilla/Logging.h"
82 #include "prtime.h"
83 #include "prenv.h"
84
85 #include "mozilla/WidgetTraceEvent.h"
86 #include "nsIAppShell.h"
87 #include "nsISupportsPrimitives.h"
88 #include "nsIDOMMouseEvent.h"
89 #include "nsIKeyEventInPluginCallback.h"
90 #include "nsITheme.h"
91 #include "nsIObserverService.h"
92 #include "nsIScreenManager.h"
93 #include "imgIContainer.h"
94 #include "nsIFile.h"
95 #include "nsIRollupListener.h"
96 #include "nsIServiceManager.h"
97 #include "nsIClipboard.h"
98 #include "WinMouseScrollHandler.h"
99 #include "nsFontMetrics.h"
100 #include "nsIFontEnumerator.h"
101 #include "nsFont.h"
102 #include "nsRect.h"
103 #include "nsThreadUtils.h"
104 #include "nsNativeCharsetUtils.h"
105 #include "nsGkAtoms.h"
106 #include "nsCRT.h"
107 #include "nsAppDirectoryServiceDefs.h"
108 #include "nsWidgetsCID.h"
109 #include "nsTHashtable.h"
110 #include "nsHashKeys.h"
111 #include "nsString.h"
112 #include "mozilla/Services.h"
113 #include "nsNativeThemeWin.h"
114 #include "nsWindowsDllInterceptor.h"
115 #include "nsLayoutUtils.h"
116 #include "nsView.h"
117 #include "nsIWindowMediator.h"
118 #include "nsIServiceManager.h"
119 #include "nsWindowGfx.h"
120 #include "gfxWindowsPlatform.h"
121 #include "Layers.h"
122 #include "nsPrintfCString.h"
123 #include "mozilla/Preferences.h"
124 #include "nsISound.h"
125 #include "SystemTimeConverter.h"
126 #include "WinTaskbar.h"
127 #include "WidgetUtils.h"
128 #include "nsIWidgetListener.h"
129 #include "mozilla/dom/Touch.h"
130 #include "mozilla/gfx/2D.h"
131 #include "nsToolkitCompsCID.h"
132 #include "nsIAppStartup.h"
133 #include "mozilla/WindowsVersion.h"
134 #include "mozilla/TextEvents.h" // For WidgetKeyboardEvent
135 #include "mozilla/TextEventDispatcherListener.h"
136 #include "mozilla/widget/nsAutoRollup.h"
137 #include "mozilla/widget/WinNativeEventData.h"
138 #include "mozilla/widget/PlatformWidgetTypes.h"
139 #include "nsThemeConstants.h"
140 #include "nsBidiKeyboard.h"
141 #include "nsThemeConstants.h"
142 #include "gfxConfig.h"
143 #include "InProcessWinCompositorWidget.h"
144 #include "ScreenHelperWin.h"
145
146 #include "nsIGfxInfo.h"
147 #include "nsUXThemeConstants.h"
148 #include "KeyboardLayout.h"
149 #include "nsNativeDragTarget.h"
150 #include <mmsystem.h> // needed for WIN32_LEAN_AND_MEAN
151 #include <zmouse.h>
152 #include <richedit.h>
153
154 #if defined(ACCESSIBILITY)
155
156 #ifdef DEBUG
157 #include "mozilla/a11y/Logging.h"
158 #endif
159
160 #include "oleidl.h"
161 #include <winuser.h>
162 #include "nsAccessibilityService.h"
163 #include "mozilla/a11y/DocAccessible.h"
164 #include "mozilla/a11y/LazyInstantiator.h"
165 #include "mozilla/a11y/Platform.h"
166 #if !defined(WINABLEAPI)
167 #include <winable.h>
168 #endif // !defined(WINABLEAPI)
169 #endif // defined(ACCESSIBILITY)
170
171 #include "nsIWinTaskbar.h"
172 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
173
174 #include "nsIWindowsUIUtils.h"
175
176 #include "nsWindowDefs.h"
177
178 #include "nsCrashOnException.h"
179 #include "nsIXULRuntime.h"
180
181 #include "nsIContent.h"
182
183 #include "mozilla/HangMonitor.h"
184 #include "WinIMEHandler.h"
185
186 #include "npapi.h"
187
188 #include <d3d11.h>
189
190 #include "InkCollector.h"
191
192 // ERROR from wingdi.h (below) gets undefined by some code.
193 // #define ERROR 0
194 // #define RGN_ERROR ERROR
195 #define ERROR 0
196
197 #if !defined(SM_CONVERTIBLESLATEMODE)
198 #define SM_CONVERTIBLESLATEMODE 0x2003
199 #endif
200
201 #if !defined(WM_DPICHANGED)
202 #define WM_DPICHANGED 0x02E0
203 #endif
204
205 #include "mozilla/gfx/DeviceManagerDx.h"
206 #include "mozilla/layers/InputAPZContext.h"
207 #include "mozilla/layers/KnowsCompositor.h"
208 #include "InputData.h"
209
210 #include "mozilla/Telemetry.h"
211 #include "mozilla/plugins/PluginProcessParent.h"
212 #include "mozilla/webrender/WebRenderAPI.h"
213
214 using namespace mozilla;
215 using namespace mozilla::dom;
216 using namespace mozilla::gfx;
217 using namespace mozilla::layers;
218 using namespace mozilla::widget;
219 using namespace mozilla::plugins;
220
221 /**************************************************************
222 **************************************************************
223 **
224 ** BLOCK: Variables
225 **
226 ** nsWindow Class static initializations and global variables.
227 **
228 **************************************************************
229 **************************************************************/
230
231 /**************************************************************
232 *
233 * SECTION: nsWindow statics
234 *
235 **************************************************************/
236
237 bool nsWindow::sDropShadowEnabled = true;
238 uint32_t nsWindow::sInstanceCount = 0;
239 bool nsWindow::sSwitchKeyboardLayout = false;
240 BOOL nsWindow::sIsOleInitialized = FALSE;
241 HCURSOR nsWindow::sHCursor = nullptr;
242 imgIContainer* nsWindow::sCursorImgContainer = nullptr;
243 nsWindow* nsWindow::sCurrentWindow = nullptr;
244 bool nsWindow::sJustGotDeactivate = false;
245 bool nsWindow::sJustGotActivate = false;
246 bool nsWindow::sIsInMouseCapture = false;
247
248 // imported in nsWidgetFactory.cpp
249 TriStateBool nsWindow::sCanQuit = TRI_UNKNOWN;
250
251 // Hook Data Memebers for Dropdowns. sProcessHook Tells the
252 // hook methods whether they should be processing the hook
253 // messages.
254 HHOOK nsWindow::sMsgFilterHook = nullptr;
255 HHOOK nsWindow::sCallProcHook = nullptr;
256 HHOOK nsWindow::sCallMouseHook = nullptr;
257 bool nsWindow::sProcessHook = false;
258 UINT nsWindow::sRollupMsgId = 0;
259 HWND nsWindow::sRollupMsgWnd = nullptr;
260 UINT nsWindow::sHookTimerId = 0;
261
262 // Mouse Clicks - static variable definitions for figuring
263 // out 1 - 3 Clicks.
264 POINT nsWindow::sLastMousePoint = {0};
265 POINT nsWindow::sLastMouseMovePoint = {0};
266 LONG nsWindow::sLastMouseDownTime = 0L;
267 LONG nsWindow::sLastClickCount = 0L;
268 BYTE nsWindow::sLastMouseButton = 0;
269
270 bool nsWindow::sHaveInitializedPrefs = false;
271
272 TriStateBool nsWindow::sHasBogusPopupsDropShadowOnMultiMonitor = TRI_UNKNOWN;
273
TimeConverter()274 static SystemTimeConverter<DWORD>& TimeConverter() {
275 static SystemTimeConverter<DWORD> timeConverterSingleton;
276 return timeConverterSingleton;
277 }
278
279 namespace mozilla {
280
281 class CurrentWindowsTimeGetter {
282 public:
CurrentWindowsTimeGetter(HWND aWnd)283 explicit CurrentWindowsTimeGetter(HWND aWnd) : mWnd(aWnd) {}
284
GetCurrentTime() const285 DWORD GetCurrentTime() const { return ::GetTickCount(); }
286
GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp & aNow)287 void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp& aNow) {
288 DWORD currentTime = GetCurrentTime();
289 if (sBackwardsSkewStamp && currentTime == sLastPostTime) {
290 // There's already one inflight with this timestamp. Don't
291 // send a duplicate.
292 return;
293 }
294 sBackwardsSkewStamp = Some(aNow);
295 sLastPostTime = currentTime;
296 static_assert(sizeof(WPARAM) >= sizeof(DWORD),
297 "Can't fit a DWORD in a WPARAM");
298 ::PostMessage(mWnd, MOZ_WM_SKEWFIX, sLastPostTime, 0);
299 }
300
GetAndClearBackwardsSkewStamp(DWORD aPostTime,TimeStamp * aOutSkewStamp)301 static bool GetAndClearBackwardsSkewStamp(DWORD aPostTime,
302 TimeStamp* aOutSkewStamp) {
303 if (aPostTime != sLastPostTime) {
304 // The SKEWFIX message is stale; we've sent a new one since then.
305 // Ignore this one.
306 return false;
307 }
308 MOZ_ASSERT(sBackwardsSkewStamp);
309 *aOutSkewStamp = sBackwardsSkewStamp.value();
310 sBackwardsSkewStamp = Nothing();
311 return true;
312 }
313
314 private:
315 static Maybe<TimeStamp> sBackwardsSkewStamp;
316 static DWORD sLastPostTime;
317 HWND mWnd;
318 };
319
320 Maybe<TimeStamp> CurrentWindowsTimeGetter::sBackwardsSkewStamp;
321 DWORD CurrentWindowsTimeGetter::sLastPostTime = 0;
322
323 } // namespace mozilla
324
325 /**************************************************************
326 *
327 * SECTION: globals variables
328 *
329 **************************************************************/
330
331 static const char* sScreenManagerContractID =
332 "@mozilla.org/gfx/screenmanager;1";
333
334 extern mozilla::LazyLogModule gWindowsLog;
335
336 // Global used in Show window enumerations.
337 static bool gWindowsVisible = false;
338
339 // True if we have sent a notification that we are suspending/sleeping.
340 static bool gIsSleepMode = false;
341
342 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
343
344 // General purpose user32.dll hook object
345 static WindowsDllInterceptor sUser32Intercept;
346
347 // 2 pixel offset for eTransparencyBorderlessGlass which equals the size of
348 // the default window border Windows paints. Glass will be extended inward
349 // this distance to remove the border.
350 static const int32_t kGlassMarginAdjustment = 2;
351
352 // When the client area is extended out into the default window frame area,
353 // this is the minimum amount of space along the edge of resizable windows
354 // we will always display a resize cursor in, regardless of the underlying
355 // content.
356 static const int32_t kResizableBorderMinSize = 3;
357
358 // Cached pointer events enabler value, True if pointer events are enabled.
359 static bool gIsPointerEventsEnabled = false;
360
361 // We should never really try to accelerate windows bigger than this. In some
362 // cases this might lead to no D3D9 acceleration where we could have had it
363 // but D3D9 does not reliably report when it supports bigger windows. 8192
364 // is as safe as we can get, we know at least D3D10 hardware always supports
365 // this, other hardware we expect to report correctly in D3D9.
366 #define MAX_ACCELERATED_DIMENSION 8192
367
368 // On window open (as well as after), Windows has an unfortunate habit of
369 // sending rather a lot of WM_NCHITTEST messages. Because we have to do point
370 // to DOM target conversions for these, we cache responses for a given
371 // coordinate this many milliseconds:
372 #define HITTEST_CACHE_LIFETIME_MS 50
373
374 #if defined(ACCESSIBILITY)
375
376 namespace mozilla {
377
378 /**
379 * Windows touchscreen code works by setting a global WH_GETMESSAGE hook and
380 * injecting tiptsf.dll. The touchscreen process then posts registered messages
381 * to our main thread. The tiptsf hook picks up those registered messages and
382 * uses them as commands, some of which call into UIA, which then calls into
383 * MSAA, which then sends WM_GETOBJECT to us.
384 *
385 * We can get ahead of this by installing our own thread-local WH_GETMESSAGE
386 * hook. Since thread-local hooks are called ahead of global hooks, we will
387 * see these registered messages before tiptsf does. At this point we can then
388 * raise a flag that blocks a11y before invoking CallNextHookEx which will then
389 * invoke the global tiptsf hook. Then when we see WM_GETOBJECT, we check the
390 * flag by calling TIPMessageHandler::IsA11yBlocked().
391 *
392 * For Windows 8, we also hook tiptsf!ProcessCaretEvents, which is an a11y hook
393 * function that also calls into UIA.
394 */
395 class TIPMessageHandler {
396 public:
~TIPMessageHandler()397 ~TIPMessageHandler() {
398 if (mHook) {
399 ::UnhookWindowsHookEx(mHook);
400 }
401 }
402
Initialize()403 static void Initialize() {
404 if (!IsWin8OrLater()) {
405 return;
406 }
407
408 if (sInstance) {
409 return;
410 }
411
412 sInstance = new TIPMessageHandler();
413 ClearOnShutdown(&sInstance);
414 }
415
IsA11yBlocked()416 static bool IsA11yBlocked() {
417 if (!sInstance) {
418 return false;
419 }
420
421 return sInstance->mA11yBlockCount > 0;
422 }
423
424 private:
TIPMessageHandler()425 TIPMessageHandler() : mHook(nullptr), mA11yBlockCount(0) {
426 MOZ_ASSERT(NS_IsMainThread());
427
428 // Registered messages used by tiptsf
429 mMessages[0] = ::RegisterWindowMessage(L"ImmersiveFocusNotification");
430 mMessages[1] = ::RegisterWindowMessage(L"TipCloseMenus");
431 mMessages[2] = ::RegisterWindowMessage(L"TabletInputPanelOpening");
432 mMessages[3] = ::RegisterWindowMessage(L"IHM Pen or Touch Event noticed");
433 mMessages[4] = ::RegisterWindowMessage(L"ProgrammabilityCaretVisibility");
434 mMessages[5] = ::RegisterWindowMessage(L"CaretTrackingUpdateIPHidden");
435 mMessages[6] = ::RegisterWindowMessage(L"CaretTrackingUpdateIPInfo");
436
437 mHook = ::SetWindowsHookEx(WH_GETMESSAGE, &TIPHook, nullptr,
438 ::GetCurrentThreadId());
439 MOZ_ASSERT(mHook);
440
441 // On touchscreen devices, tiptsf.dll will have been loaded when STA COM was
442 // first initialized.
443 if (!IsWin10OrLater() && GetModuleHandle(L"tiptsf.dll") &&
444 !sProcessCaretEventsStub) {
445 sTipTsfInterceptor.Init("tiptsf.dll");
446 DebugOnly<bool> ok = sTipTsfInterceptor.AddHook(
447 "ProcessCaretEvents",
448 reinterpret_cast<intptr_t>(&ProcessCaretEventsHook),
449 (void**)&sProcessCaretEventsStub);
450 MOZ_ASSERT(ok);
451 }
452
453 if (!sSendMessageTimeoutWStub) {
454 sUser32Intercept.Init("user32.dll");
455 DebugOnly<bool> hooked = sUser32Intercept.AddHook(
456 "SendMessageTimeoutW",
457 reinterpret_cast<intptr_t>(&SendMessageTimeoutWHook),
458 (void**)&sSendMessageTimeoutWStub);
459 MOZ_ASSERT(hooked);
460 }
461 }
462
463 class MOZ_RAII A11yInstantiationBlocker {
464 public:
A11yInstantiationBlocker()465 A11yInstantiationBlocker() {
466 if (!TIPMessageHandler::sInstance) {
467 return;
468 }
469 ++TIPMessageHandler::sInstance->mA11yBlockCount;
470 }
471
~A11yInstantiationBlocker()472 ~A11yInstantiationBlocker() {
473 if (!TIPMessageHandler::sInstance) {
474 return;
475 }
476 MOZ_ASSERT(TIPMessageHandler::sInstance->mA11yBlockCount > 0);
477 --TIPMessageHandler::sInstance->mA11yBlockCount;
478 }
479 };
480
481 friend class A11yInstantiationBlocker;
482
TIPHook(int aCode,WPARAM aWParam,LPARAM aLParam)483 static LRESULT CALLBACK TIPHook(int aCode, WPARAM aWParam, LPARAM aLParam) {
484 if (aCode < 0 || !sInstance) {
485 return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
486 }
487
488 MSG* msg = reinterpret_cast<MSG*>(aLParam);
489 UINT& msgCode = msg->message;
490
491 for (uint32_t i = 0; i < ArrayLength(sInstance->mMessages); ++i) {
492 if (msgCode == sInstance->mMessages[i]) {
493 A11yInstantiationBlocker block;
494 return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
495 }
496 }
497
498 return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
499 }
500
ProcessCaretEventsHook(HWINEVENTHOOK aWinEventHook,DWORD aEvent,HWND aHwnd,LONG aObjectId,LONG aChildId,DWORD aGeneratingTid,DWORD aEventTime)501 static void CALLBACK ProcessCaretEventsHook(HWINEVENTHOOK aWinEventHook,
502 DWORD aEvent, HWND aHwnd,
503 LONG aObjectId, LONG aChildId,
504 DWORD aGeneratingTid,
505 DWORD aEventTime) {
506 A11yInstantiationBlocker block;
507 sProcessCaretEventsStub(aWinEventHook, aEvent, aHwnd, aObjectId, aChildId,
508 aGeneratingTid, aEventTime);
509 }
510
SendMessageTimeoutWHook(HWND aHwnd,UINT aMsgCode,WPARAM aWParam,LPARAM aLParam,UINT aFlags,UINT aTimeout,PDWORD_PTR aMsgResult)511 static LRESULT WINAPI SendMessageTimeoutWHook(HWND aHwnd, UINT aMsgCode,
512 WPARAM aWParam, LPARAM aLParam,
513 UINT aFlags, UINT aTimeout,
514 PDWORD_PTR aMsgResult) {
515 // We don't want to handle this unless the message is a WM_GETOBJECT that we
516 // want to block, and the aHwnd is a nsWindow that belongs to the current
517 // thread.
518 if (!aMsgResult || aMsgCode != WM_GETOBJECT ||
519 static_cast<DWORD>(aLParam) != OBJID_CLIENT ||
520 !WinUtils::GetNSWindowPtr(aHwnd) ||
521 ::GetWindowThreadProcessId(aHwnd, nullptr) != ::GetCurrentThreadId() ||
522 !IsA11yBlocked()) {
523 return sSendMessageTimeoutWStub(aHwnd, aMsgCode, aWParam, aLParam, aFlags,
524 aTimeout, aMsgResult);
525 }
526
527 // In this case we want to fake the result that would happen if we had
528 // decided not to handle WM_GETOBJECT in our WndProc. We hand the message
529 // off to DefWindowProc to accomplish this.
530 *aMsgResult = static_cast<DWORD_PTR>(
531 ::DefWindowProcW(aHwnd, aMsgCode, aWParam, aLParam));
532
533 return static_cast<LRESULT>(TRUE);
534 }
535
536 static WindowsDllInterceptor sTipTsfInterceptor;
537 static WINEVENTPROC sProcessCaretEventsStub;
538 static decltype(&SendMessageTimeoutW) sSendMessageTimeoutWStub;
539 static StaticAutoPtr<TIPMessageHandler> sInstance;
540
541 HHOOK mHook;
542 UINT mMessages[7];
543 uint32_t mA11yBlockCount;
544 };
545
546 WindowsDllInterceptor TIPMessageHandler::sTipTsfInterceptor;
547 WINEVENTPROC TIPMessageHandler::sProcessCaretEventsStub;
548 decltype(&SendMessageTimeoutW) TIPMessageHandler::sSendMessageTimeoutWStub;
549 StaticAutoPtr<TIPMessageHandler> TIPMessageHandler::sInstance;
550
551 } // namespace mozilla
552
553 #endif // defined(ACCESSIBILITY)
554
555 /**************************************************************
556 **************************************************************
557 **
558 ** BLOCK: nsIWidget impl.
559 **
560 ** nsIWidget interface implementation, broken down into
561 ** sections.
562 **
563 **************************************************************
564 **************************************************************/
565
566 /**************************************************************
567 *
568 * SECTION: nsWindow construction and destruction
569 *
570 **************************************************************/
571
nsWindow(bool aIsChildWindow)572 nsWindow::nsWindow(bool aIsChildWindow)
573 : nsWindowBase(),
574 mResizeState(NOT_RESIZING),
575 mIsChildWindow(aIsChildWindow) {
576 mIconSmall = nullptr;
577 mIconBig = nullptr;
578 mWnd = nullptr;
579 mTransitionWnd = nullptr;
580 mPaintDC = nullptr;
581 mPrevWndProc = nullptr;
582 mNativeDragTarget = nullptr;
583 mInDtor = false;
584 mIsVisible = false;
585 mIsTopWidgetWindow = false;
586 mUnicodeWidget = true;
587 mDisplayPanFeedback = false;
588 mTouchWindow = false;
589 mFutureMarginsToUse = false;
590 mCustomNonClient = false;
591 mHideChrome = false;
592 mFullscreenMode = false;
593 mMousePresent = false;
594 mDestroyCalled = false;
595 mHasTaskbarIconBeenCreated = false;
596 mMouseTransparent = false;
597 mPickerDisplayCount = 0;
598 mWindowType = eWindowType_child;
599 mBorderStyle = eBorderStyle_default;
600 mOldSizeMode = nsSizeMode_Normal;
601 mLastSizeMode = nsSizeMode_Normal;
602 mLastSize.width = 0;
603 mLastSize.height = 0;
604 mOldStyle = 0;
605 mOldExStyle = 0;
606 mPainting = 0;
607 mLastKeyboardLayout = 0;
608 mBlurSuppressLevel = 0;
609 mLastPaintEndTime = TimeStamp::Now();
610 mCachedHitTestPoint.x = 0;
611 mCachedHitTestPoint.y = 0;
612 mCachedHitTestTime = TimeStamp::Now();
613 mCachedHitTestResult = 0;
614 #ifdef MOZ_XUL
615 mTransparencyMode = eTransparencyOpaque;
616 memset(&mGlassMargins, 0, sizeof mGlassMargins);
617 #endif
618 DWORD background = ::GetSysColor(COLOR_BTNFACE);
619 mBrush = ::CreateSolidBrush(NSRGB_2_COLOREF(background));
620 mSendingSetText = false;
621 mDefaultScale = -1.0; // not yet set, will be calculated on first use
622
623 mTaskbarPreview = nullptr;
624
625 mCompositorWidgetDelegate = nullptr;
626
627 // Global initialization
628 if (!sInstanceCount) {
629 // Global app registration id for Win7 and up. See
630 // WinTaskbar.cpp for details.
631 mozilla::widget::WinTaskbar::RegisterAppUserModelID();
632 KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0));
633 #if defined(ACCESSIBILITY)
634 mozilla::TIPMessageHandler::Initialize();
635 #endif // defined(ACCESSIBILITY)
636 if (SUCCEEDED(::OleInitialize(nullptr))) {
637 sIsOleInitialized = TRUE;
638 }
639 NS_ASSERTION(sIsOleInitialized, "***** OLE is not initialized!\n");
640 MouseScrollHandler::Initialize();
641 // Init theme data
642 nsUXThemeData::UpdateNativeThemeInfo();
643 RedirectedKeyDownMessageManager::Forget();
644 if (mPointerEvents.ShouldEnableInkCollector()) {
645 InkCollector::sInkCollector = new InkCollector();
646 }
647
648 Preferences::AddBoolVarCache(&gIsPointerEventsEnabled,
649 "dom.w3c_pointer_events.enabled",
650 gIsPointerEventsEnabled);
651 } // !sInstanceCount
652
653 mIdleService = nullptr;
654
655 mSizeConstraintsScale = GetDefaultScale().scale;
656
657 sInstanceCount++;
658 }
659
~nsWindow()660 nsWindow::~nsWindow() {
661 mInDtor = true;
662
663 // If the widget was released without calling Destroy() then the native window
664 // still exists, and we need to destroy it. Destroy() will early-return if it
665 // was already called. In any case it is important to call it before
666 // destroying mPresentLock (cf. 1156182).
667 Destroy();
668
669 // Free app icon resources. This must happen after `OnDestroy` (see bug
670 // 708033).
671 if (mIconSmall) ::DestroyIcon(mIconSmall);
672
673 if (mIconBig) ::DestroyIcon(mIconBig);
674
675 sInstanceCount--;
676
677 // Global shutdown
678 if (sInstanceCount == 0) {
679 if (InkCollector::sInkCollector) {
680 InkCollector::sInkCollector->Shutdown();
681 InkCollector::sInkCollector = nullptr;
682 }
683 IMEHandler::Terminate();
684 NS_IF_RELEASE(sCursorImgContainer);
685 if (sIsOleInitialized) {
686 ::OleFlushClipboard();
687 ::OleUninitialize();
688 sIsOleInitialized = FALSE;
689 }
690 }
691
692 NS_IF_RELEASE(mNativeDragTarget);
693 }
694
695 /**************************************************************
696 *
697 * SECTION: nsIWidget::Create, nsIWidget::Destroy
698 *
699 * Creating and destroying windows for this widget.
700 *
701 **************************************************************/
702
703 // Allow Derived classes to modify the height that is passed
704 // when the window is created or resized.
GetHeight(int32_t aProposedHeight)705 int32_t nsWindow::GetHeight(int32_t aProposedHeight) { return aProposedHeight; }
706
ShouldCacheTitleBarInfo(nsWindowType aWindowType,nsBorderStyle aBorderStyle)707 static bool ShouldCacheTitleBarInfo(nsWindowType aWindowType,
708 nsBorderStyle aBorderStyle) {
709 return (aWindowType == eWindowType_toplevel) &&
710 (aBorderStyle == eBorderStyle_default ||
711 aBorderStyle == eBorderStyle_all) &&
712 (!nsUXThemeData::sTitlebarInfoPopulatedThemed ||
713 !nsUXThemeData::sTitlebarInfoPopulatedAero);
714 }
715
716 // Create the proper widget
Create(nsIWidget * aParent,nsNativeWidget aNativeParent,const LayoutDeviceIntRect & aRect,nsWidgetInitData * aInitData)717 nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
718 const LayoutDeviceIntRect& aRect,
719 nsWidgetInitData* aInitData) {
720 nsWidgetInitData defaultInitData;
721 if (!aInitData) aInitData = &defaultInitData;
722
723 mUnicodeWidget = aInitData->mUnicode;
724
725 nsIWidget* baseParent =
726 aInitData->mWindowType == eWindowType_dialog ||
727 aInitData->mWindowType == eWindowType_toplevel ||
728 aInitData->mWindowType == eWindowType_invisible
729 ? nullptr
730 : aParent;
731
732 mIsTopWidgetWindow = (nullptr == baseParent);
733 mBounds = aRect;
734
735 // Ensure that the toolkit is created.
736 nsToolkit::GetToolkit();
737
738 BaseCreate(baseParent, aInitData);
739
740 HWND parent;
741 if (aParent) { // has a nsIWidget parent
742 parent = aParent ? (HWND)aParent->GetNativeData(NS_NATIVE_WINDOW) : nullptr;
743 mParent = aParent;
744 } else { // has a nsNative parent
745 parent = (HWND)aNativeParent;
746 mParent =
747 aNativeParent ? WinUtils::GetNSWindowPtr((HWND)aNativeParent) : nullptr;
748 }
749
750 mIsRTL = aInitData->mRTL;
751 mOpeningAnimationSuppressed = aInitData->mIsAnimationSuppressed;
752
753 DWORD style = WindowStyle();
754 DWORD extendedStyle = WindowExStyle();
755
756 if (mWindowType == eWindowType_popup) {
757 if (!aParent) {
758 parent = nullptr;
759 }
760
761 if (!IsWin8OrLater() && HasBogusPopupsDropShadowOnMultiMonitor() &&
762 ShouldUseOffMainThreadCompositing()) {
763 extendedStyle |= WS_EX_COMPOSITED;
764 }
765
766 if (aInitData->mMouseTransparent) {
767 // This flag makes the window transparent to mouse events
768 mMouseTransparent = true;
769 extendedStyle |= WS_EX_TRANSPARENT;
770 }
771 } else if (mWindowType == eWindowType_invisible) {
772 // Make sure CreateWindowEx succeeds at creating a toplevel window
773 style &= ~0x40000000; // WS_CHILDWINDOW
774 } else {
775 // See if the caller wants to explictly set clip children and clip siblings
776 if (aInitData->clipChildren) {
777 style |= WS_CLIPCHILDREN;
778 } else {
779 style &= ~WS_CLIPCHILDREN;
780 }
781 if (aInitData->clipSiblings) {
782 style |= WS_CLIPSIBLINGS;
783 }
784 }
785
786 const wchar_t* className;
787 if (aInitData->mDropShadow) {
788 className = GetWindowPopupClass();
789 } else {
790 className = GetWindowClass();
791 }
792 // Plugins are created in the disabled state so that they can't
793 // steal focus away from our main window. This is especially
794 // important if the plugin has loaded in a background tab.
795 if (aInitData->mWindowType == eWindowType_plugin ||
796 aInitData->mWindowType == eWindowType_plugin_ipc_chrome ||
797 aInitData->mWindowType == eWindowType_plugin_ipc_content) {
798 style |= WS_DISABLED;
799 }
800 mWnd = ::CreateWindowExW(extendedStyle, className, L"", style, aRect.X(),
801 aRect.Y(), aRect.Width(), GetHeight(aRect.Height()),
802 parent, nullptr, nsToolkit::mDllInstance, nullptr);
803
804 if (!mWnd) {
805 NS_WARNING("nsWindow CreateWindowEx failed.");
806 return NS_ERROR_FAILURE;
807 }
808 // If mDefaultScale is set before mWnd has been set, it will have the scale of
809 // the primary monitor, rather than the monitor that the window is actually
810 // on. For non-popup windows this gets corrected by the WM_DPICHANGED message
811 // which resets mDefaultScale, but for popup windows we don't reset
812 // mDefaultScale on that message. In order to ensure that popup windows
813 // spawned on a non-primary monitor end up with the correct scale, we reset
814 // mDefaultScale here so that it gets recomputed using the correct monitor now
815 // that we have a mWnd.
816 mDefaultScale = -1.0;
817
818 if (mIsRTL) {
819 DWORD dwAttribute = TRUE;
820 DwmSetWindowAttribute(mWnd, DWMWA_NONCLIENT_RTL_LAYOUT, &dwAttribute,
821 sizeof dwAttribute);
822 }
823
824 if (mOpeningAnimationSuppressed) {
825 SuppressAnimation(true);
826 }
827
828 if (!IsPlugin() && mWindowType != eWindowType_invisible &&
829 MouseScrollHandler::Device::IsFakeScrollableWindowNeeded()) {
830 // Ugly Thinkpad Driver Hack (Bugs 507222 and 594977)
831 //
832 // We create two zero-sized windows as descendants of the top-level window,
833 // like so:
834 //
835 // Top-level window (MozillaWindowClass)
836 // FAKETRACKPOINTSCROLLCONTAINER (MozillaWindowClass)
837 // FAKETRACKPOINTSCROLLABLE (MozillaWindowClass)
838 //
839 // We need to have the middle window, otherwise the Trackpoint driver
840 // will fail to deliver scroll messages. WM_MOUSEWHEEL messages are
841 // sent to the FAKETRACKPOINTSCROLLABLE, which then propagate up the
842 // window hierarchy until they are handled by nsWindow::WindowProc.
843 // WM_HSCROLL messages are also sent to the FAKETRACKPOINTSCROLLABLE,
844 // but these do not propagate automatically, so we have the window
845 // procedure pretend that they were dispatched to the top-level window
846 // instead.
847 //
848 // The FAKETRACKPOINTSCROLLABLE needs to have the specific window styles it
849 // is given below so that it catches the Trackpoint driver's heuristics.
850 HWND scrollContainerWnd = ::CreateWindowW(
851 className, L"FAKETRACKPOINTSCROLLCONTAINER", WS_CHILD | WS_VISIBLE, 0,
852 0, 0, 0, mWnd, nullptr, nsToolkit::mDllInstance, nullptr);
853 HWND scrollableWnd = ::CreateWindowW(
854 className, L"FAKETRACKPOINTSCROLLABLE",
855 WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP | 0x30, 0, 0, 0, 0,
856 scrollContainerWnd, nullptr, nsToolkit::mDllInstance, nullptr);
857
858 // Give the FAKETRACKPOINTSCROLLABLE window a specific ID so that
859 // WindowProcInternal can distinguish it from the top-level window
860 // easily.
861 ::SetWindowLongPtrW(scrollableWnd, GWLP_ID, eFakeTrackPointScrollableID);
862
863 // Make FAKETRACKPOINTSCROLLABLE use nsWindow::WindowProc, and store the
864 // old window procedure in its "user data".
865 WNDPROC oldWndProc;
866 if (mUnicodeWidget)
867 oldWndProc = (WNDPROC)::SetWindowLongPtrW(scrollableWnd, GWLP_WNDPROC,
868 (LONG_PTR)nsWindow::WindowProc);
869 else
870 oldWndProc = (WNDPROC)::SetWindowLongPtrA(scrollableWnd, GWLP_WNDPROC,
871 (LONG_PTR)nsWindow::WindowProc);
872 ::SetWindowLongPtrW(scrollableWnd, GWLP_USERDATA, (LONG_PTR)oldWndProc);
873 }
874
875 SubclassWindow(TRUE);
876
877 // Starting with Windows XP, a process always runs within a terminal services
878 // session. In order to play nicely with RDP, fast user switching, and the
879 // lock screen, we should be handling WM_WTSSESSION_CHANGE. We must register
880 // our HWND in order to receive this message.
881 DebugOnly<BOOL> wtsRegistered =
882 ::WTSRegisterSessionNotification(mWnd, NOTIFY_FOR_THIS_SESSION);
883 NS_ASSERTION(wtsRegistered, "WTSRegisterSessionNotification failed!\n");
884
885 mDefaultIMC.Init(this);
886 IMEHandler::InitInputContext(this, mInputContext);
887
888 // Do some initialization work, but only if (a) it hasn't already been done,
889 // and (b) this is the hidden window (which is conveniently created before
890 // any visible windows but after the profile has been initialized).
891 if (!sHaveInitializedPrefs && mWindowType == eWindowType_invisible) {
892 sSwitchKeyboardLayout =
893 Preferences::GetBool("intl.keyboard.per_window_layout", false);
894 sHaveInitializedPrefs = true;
895 }
896
897 // Query for command button metric data for rendering the titlebar. We
898 // only do this once on the first window that has an actual titlebar
899 if (ShouldCacheTitleBarInfo(mWindowType, mBorderStyle)) {
900 nsUXThemeData::UpdateTitlebarInfo(mWnd);
901 }
902
903 static bool a11yPrimed = false;
904 if (!a11yPrimed && mWindowType == eWindowType_toplevel) {
905 a11yPrimed = true;
906 if (Preferences::GetInt("accessibility.force_disabled", 0) == -1) {
907 ::PostMessage(mWnd, MOZ_WM_STARTA11Y, 0, 0);
908 }
909 }
910 return NS_OK;
911 }
912
913 // Close this nsWindow
Destroy()914 void nsWindow::Destroy() {
915 // WM_DESTROY has already fired, avoid calling it twice
916 if (mOnDestroyCalled) return;
917
918 // Don't destroy windows that have file pickers open, we'll tear these down
919 // later once the picker is closed.
920 mDestroyCalled = true;
921 if (mPickerDisplayCount) return;
922
923 // During the destruction of all of our children, make sure we don't get
924 // deleted.
925 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
926
927 /**
928 * On windows the LayerManagerOGL destructor wants the widget to be around for
929 * cleanup. It also would like to have the HWND intact, so we nullptr it here.
930 */
931 DestroyLayerManager();
932
933 /* We should clear our cached resources now and not wait for the GC to
934 * delete the nsWindow. */
935 ClearCachedResources();
936
937 // The DestroyWindow function destroys the specified window. The function
938 // sends WM_DESTROY and WM_NCDESTROY messages to the window to deactivate it
939 // and remove the keyboard focus from it. The function also destroys the
940 // window's menu, flushes the thread message queue, destroys timers, removes
941 // clipboard ownership, and breaks the clipboard viewer chain (if the window
942 // is at the top of the viewer chain).
943 //
944 // If the specified window is a parent or owner window, DestroyWindow
945 // automatically destroys the associated child or owned windows when it
946 // destroys the parent or owner window. The function first destroys child or
947 // owned windows, and then it destroys the parent or owner window.
948 VERIFY(::DestroyWindow(mWnd));
949
950 // Our windows can be subclassed which may prevent us receiving WM_DESTROY. If
951 // OnDestroy() didn't get called, call it now.
952 if (false == mOnDestroyCalled) {
953 MSGResult msgResult;
954 mWindowHook.Notify(mWnd, WM_DESTROY, 0, 0, msgResult);
955 OnDestroy();
956 }
957 }
958
959 /**************************************************************
960 *
961 * SECTION: Window class utilities
962 *
963 * Utilities for calculating the proper window class name for
964 * Create window.
965 *
966 **************************************************************/
967
RegisterWindowClass(const wchar_t * aClassName,UINT aExtraStyle,LPWSTR aIconID) const968 const wchar_t* nsWindow::RegisterWindowClass(const wchar_t* aClassName,
969 UINT aExtraStyle,
970 LPWSTR aIconID) const {
971 WNDCLASSW wc;
972 if (::GetClassInfoW(nsToolkit::mDllInstance, aClassName, &wc)) {
973 // already registered
974 return aClassName;
975 }
976
977 wc.style = CS_DBLCLKS | aExtraStyle;
978 wc.lpfnWndProc = WinUtils::NonClientDpiScalingDefWindowProcW;
979 wc.cbClsExtra = 0;
980 wc.cbWndExtra = 0;
981 wc.hInstance = nsToolkit::mDllInstance;
982 wc.hIcon =
983 aIconID ? ::LoadIconW(::GetModuleHandleW(nullptr), aIconID) : nullptr;
984 wc.hCursor = nullptr;
985 wc.hbrBackground = mBrush;
986 wc.lpszMenuName = nullptr;
987 wc.lpszClassName = aClassName;
988
989 if (!::RegisterClassW(&wc)) {
990 // For older versions of Win32 (i.e., not XP), the registration may
991 // fail with aExtraStyle, so we have to re-register without it.
992 wc.style = CS_DBLCLKS;
993 ::RegisterClassW(&wc);
994 }
995 return aClassName;
996 }
997
998 static LPWSTR const gStockApplicationIcon = MAKEINTRESOURCEW(32512);
999
1000 // Return the proper window class for everything except popups.
GetWindowClass() const1001 const wchar_t* nsWindow::GetWindowClass() const {
1002 switch (mWindowType) {
1003 case eWindowType_invisible:
1004 return RegisterWindowClass(kClassNameHidden, 0, gStockApplicationIcon);
1005 case eWindowType_dialog:
1006 return RegisterWindowClass(kClassNameDialog, 0, 0);
1007 default:
1008 return RegisterWindowClass(GetMainWindowClass(), 0,
1009 gStockApplicationIcon);
1010 }
1011 }
1012
1013 // Return the proper popup window class
GetWindowPopupClass() const1014 const wchar_t* nsWindow::GetWindowPopupClass() const {
1015 return RegisterWindowClass(kClassNameDropShadow, CS_XP_DROPSHADOW,
1016 gStockApplicationIcon);
1017 }
1018
1019 /**************************************************************
1020 *
1021 * SECTION: Window styles utilities
1022 *
1023 * Return the proper windows styles and extended styles.
1024 *
1025 **************************************************************/
1026
1027 // Return nsWindow styles
WindowStyle()1028 DWORD nsWindow::WindowStyle() {
1029 DWORD style;
1030
1031 switch (mWindowType) {
1032 case eWindowType_plugin:
1033 case eWindowType_plugin_ipc_chrome:
1034 case eWindowType_plugin_ipc_content:
1035 case eWindowType_child:
1036 style = WS_OVERLAPPED;
1037 break;
1038
1039 case eWindowType_dialog:
1040 style = WS_OVERLAPPED | WS_BORDER | WS_DLGFRAME | WS_SYSMENU | DS_3DLOOK |
1041 DS_MODALFRAME | WS_CLIPCHILDREN;
1042 if (mBorderStyle != eBorderStyle_default)
1043 style |= WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
1044 break;
1045
1046 case eWindowType_popup:
1047 style = WS_POPUP;
1048 if (!HasGlass()) {
1049 style |= WS_OVERLAPPED;
1050 }
1051 break;
1052
1053 default:
1054 NS_ERROR("unknown border style");
1055 // fall through
1056
1057 case eWindowType_toplevel:
1058 case eWindowType_invisible:
1059 style = WS_OVERLAPPED | WS_BORDER | WS_DLGFRAME | WS_SYSMENU |
1060 WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN;
1061 break;
1062 }
1063
1064 if (mBorderStyle != eBorderStyle_default &&
1065 mBorderStyle != eBorderStyle_all) {
1066 if (mBorderStyle == eBorderStyle_none ||
1067 !(mBorderStyle & eBorderStyle_border))
1068 style &= ~WS_BORDER;
1069
1070 if (mBorderStyle == eBorderStyle_none ||
1071 !(mBorderStyle & eBorderStyle_title)) {
1072 style &= ~WS_DLGFRAME;
1073 style |= WS_POPUP;
1074 style &= ~WS_CHILD;
1075 }
1076
1077 if (mBorderStyle == eBorderStyle_none ||
1078 !(mBorderStyle & eBorderStyle_close))
1079 style &= ~0;
1080 // XXX The close box can only be removed by changing the window class,
1081 // as far as I know --- roc+moz@cs.cmu.edu
1082
1083 if (mBorderStyle == eBorderStyle_none ||
1084 !(mBorderStyle & (eBorderStyle_menu | eBorderStyle_close)))
1085 style &= ~WS_SYSMENU;
1086 // Looks like getting rid of the system menu also does away with the
1087 // close box. So, we only get rid of the system menu if you want neither it
1088 // nor the close box. How does the Windows "Dialog" window class get just
1089 // closebox and no sysmenu? Who knows.
1090
1091 if (mBorderStyle == eBorderStyle_none ||
1092 !(mBorderStyle & eBorderStyle_resizeh))
1093 style &= ~WS_THICKFRAME;
1094
1095 if (mBorderStyle == eBorderStyle_none ||
1096 !(mBorderStyle & eBorderStyle_minimize))
1097 style &= ~WS_MINIMIZEBOX;
1098
1099 if (mBorderStyle == eBorderStyle_none ||
1100 !(mBorderStyle & eBorderStyle_maximize))
1101 style &= ~WS_MAXIMIZEBOX;
1102
1103 if (IsPopupWithTitleBar()) {
1104 style |= WS_CAPTION;
1105 if (mBorderStyle & eBorderStyle_close) {
1106 style |= WS_SYSMENU;
1107 }
1108 }
1109 }
1110
1111 if (mIsChildWindow) {
1112 style |= WS_CLIPCHILDREN;
1113 if (!(style & WS_POPUP)) {
1114 style |= WS_CHILD; // WS_POPUP and WS_CHILD are mutually exclusive.
1115 }
1116 }
1117
1118 VERIFY_WINDOW_STYLE(style);
1119 return style;
1120 }
1121
1122 // Return nsWindow extended styles
WindowExStyle()1123 DWORD nsWindow::WindowExStyle() {
1124 switch (mWindowType) {
1125 case eWindowType_plugin:
1126 case eWindowType_plugin_ipc_chrome:
1127 case eWindowType_plugin_ipc_content:
1128 case eWindowType_child:
1129 return 0;
1130
1131 case eWindowType_dialog:
1132 return WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME;
1133
1134 case eWindowType_popup: {
1135 DWORD extendedStyle = WS_EX_TOOLWINDOW;
1136 if (mPopupLevel == ePopupLevelTop) extendedStyle |= WS_EX_TOPMOST;
1137 return extendedStyle;
1138 }
1139 default:
1140 NS_ERROR("unknown border style");
1141 // fall through
1142
1143 case eWindowType_toplevel:
1144 case eWindowType_invisible:
1145 return WS_EX_WINDOWEDGE;
1146 }
1147 }
1148
1149 /**************************************************************
1150 *
1151 * SECTION: Window subclassing utilities
1152 *
1153 * Set or clear window subclasses on native windows. Used in
1154 * Create and Destroy.
1155 *
1156 **************************************************************/
1157
1158 // Subclass (or remove the subclass from) this component's nsWindow
SubclassWindow(BOOL bState)1159 void nsWindow::SubclassWindow(BOOL bState) {
1160 if (bState) {
1161 if (!mWnd || !IsWindow(mWnd)) {
1162 NS_ERROR("Invalid window handle");
1163 }
1164
1165 if (mUnicodeWidget) {
1166 mPrevWndProc = reinterpret_cast<WNDPROC>(
1167 SetWindowLongPtrW(mWnd, GWLP_WNDPROC,
1168 reinterpret_cast<LONG_PTR>(nsWindow::WindowProc)));
1169 } else {
1170 mPrevWndProc = reinterpret_cast<WNDPROC>(
1171 SetWindowLongPtrA(mWnd, GWLP_WNDPROC,
1172 reinterpret_cast<LONG_PTR>(nsWindow::WindowProc)));
1173 }
1174 NS_ASSERTION(mPrevWndProc, "Null standard window procedure");
1175 // connect the this pointer to the nsWindow handle
1176 WinUtils::SetNSWindowBasePtr(mWnd, this);
1177 } else {
1178 if (IsWindow(mWnd)) {
1179 if (mUnicodeWidget) {
1180 SetWindowLongPtrW(mWnd, GWLP_WNDPROC,
1181 reinterpret_cast<LONG_PTR>(mPrevWndProc));
1182 } else {
1183 SetWindowLongPtrA(mWnd, GWLP_WNDPROC,
1184 reinterpret_cast<LONG_PTR>(mPrevWndProc));
1185 }
1186 }
1187 WinUtils::SetNSWindowBasePtr(mWnd, nullptr);
1188 mPrevWndProc = nullptr;
1189 }
1190 }
1191
1192 /**************************************************************
1193 *
1194 * SECTION: nsIWidget::SetParent, nsIWidget::GetParent
1195 *
1196 * Set or clear the parent widgets using window properties, and
1197 * handles calculating native parent handles.
1198 *
1199 **************************************************************/
1200
1201 // Get and set parent widgets
SetParent(nsIWidget * aNewParent)1202 void nsWindow::SetParent(nsIWidget* aNewParent) {
1203 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
1204 nsIWidget* parent = GetParent();
1205 if (parent) {
1206 parent->RemoveChild(this);
1207 }
1208
1209 mParent = aNewParent;
1210
1211 if (aNewParent) {
1212 ReparentNativeWidget(aNewParent);
1213 aNewParent->AddChild(this);
1214 return;
1215 }
1216 if (mWnd) {
1217 // If we have no parent, SetParent should return the desktop.
1218 VERIFY(::SetParent(mWnd, nullptr));
1219 }
1220 }
1221
ReparentNativeWidget(nsIWidget * aNewParent)1222 void nsWindow::ReparentNativeWidget(nsIWidget* aNewParent) {
1223 NS_PRECONDITION(aNewParent, "");
1224
1225 mParent = aNewParent;
1226 if (mWindowType == eWindowType_popup) {
1227 return;
1228 }
1229 HWND newParent = (HWND)aNewParent->GetNativeData(NS_NATIVE_WINDOW);
1230 NS_ASSERTION(newParent, "Parent widget has a null native window handle");
1231 if (newParent && mWnd) {
1232 ::SetParent(mWnd, newParent);
1233 }
1234 }
1235
GetParent(void)1236 nsIWidget* nsWindow::GetParent(void) {
1237 if (mIsTopWidgetWindow) {
1238 return nullptr;
1239 }
1240 if (mInDtor || mOnDestroyCalled) {
1241 return nullptr;
1242 }
1243 return mParent;
1244 }
1245
RoundDown(double aDouble)1246 static int32_t RoundDown(double aDouble) {
1247 return aDouble > 0 ? static_cast<int32_t>(floor(aDouble))
1248 : static_cast<int32_t>(ceil(aDouble));
1249 }
1250
GetDPI()1251 float nsWindow::GetDPI() {
1252 float dpi = 96.0f;
1253 nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
1254 if (screen) {
1255 screen->GetDpi(&dpi);
1256 }
1257 return dpi;
1258 }
1259
GetDefaultScaleInternal()1260 double nsWindow::GetDefaultScaleInternal() {
1261 if (mDefaultScale <= 0.0) {
1262 mDefaultScale = WinUtils::LogToPhysFactor(mWnd);
1263 }
1264 return mDefaultScale;
1265 }
1266
LogToPhys(double aValue)1267 int32_t nsWindow::LogToPhys(double aValue) {
1268 return WinUtils::LogToPhys(
1269 ::MonitorFromWindow(mWnd, MONITOR_DEFAULTTOPRIMARY), aValue);
1270 }
1271
GetParentWindow(bool aIncludeOwner)1272 nsWindow* nsWindow::GetParentWindow(bool aIncludeOwner) {
1273 return static_cast<nsWindow*>(GetParentWindowBase(aIncludeOwner));
1274 }
1275
GetParentWindowBase(bool aIncludeOwner)1276 nsWindowBase* nsWindow::GetParentWindowBase(bool aIncludeOwner) {
1277 if (mIsTopWidgetWindow) {
1278 // Must use a flag instead of mWindowType to tell if the window is the
1279 // owned by the topmost widget, because a child window can be embedded
1280 // inside a HWND which is not associated with a nsIWidget.
1281 return nullptr;
1282 }
1283
1284 // If this widget has already been destroyed, pretend we have no parent.
1285 // This corresponds to code in Destroy which removes the destroyed
1286 // widget from its parent's child list.
1287 if (mInDtor || mOnDestroyCalled) return nullptr;
1288
1289 // aIncludeOwner set to true implies walking the parent chain to retrieve the
1290 // root owner. aIncludeOwner set to false implies the search will stop at the
1291 // true parent (default).
1292 nsWindow* widget = nullptr;
1293 if (mWnd) {
1294 HWND parent = nullptr;
1295 if (aIncludeOwner)
1296 parent = ::GetParent(mWnd);
1297 else
1298 parent = ::GetAncestor(mWnd, GA_PARENT);
1299
1300 if (parent) {
1301 widget = WinUtils::GetNSWindowPtr(parent);
1302 if (widget) {
1303 // If the widget is in the process of being destroyed then
1304 // do NOT return it
1305 if (widget->mInDtor) {
1306 widget = nullptr;
1307 }
1308 }
1309 }
1310 }
1311
1312 return static_cast<nsWindowBase*>(widget);
1313 }
1314
EnumAllChildWindProc(HWND aWnd,LPARAM aParam)1315 BOOL CALLBACK nsWindow::EnumAllChildWindProc(HWND aWnd, LPARAM aParam) {
1316 nsWindow* wnd = WinUtils::GetNSWindowPtr(aWnd);
1317 if (wnd) {
1318 reinterpret_cast<nsTArray<nsWindow*>*>(aParam)->AppendElement(wnd);
1319 }
1320 return TRUE;
1321 }
1322
EnumAllThreadWindowProc(HWND aWnd,LPARAM aParam)1323 BOOL CALLBACK nsWindow::EnumAllThreadWindowProc(HWND aWnd, LPARAM aParam) {
1324 nsWindow* wnd = WinUtils::GetNSWindowPtr(aWnd);
1325 if (wnd) {
1326 reinterpret_cast<nsTArray<nsWindow*>*>(aParam)->AppendElement(wnd);
1327 }
1328 EnumChildWindows(aWnd, EnumAllChildWindProc, aParam);
1329 return TRUE;
1330 }
1331
EnumAllWindows()1332 /* static*/ nsTArray<nsWindow*> nsWindow::EnumAllWindows() {
1333 nsTArray<nsWindow*> windows;
1334 EnumThreadWindows(GetCurrentThreadId(), EnumAllThreadWindowProc,
1335 reinterpret_cast<LPARAM>(&windows));
1336 return windows;
1337 }
1338
CreateSourceSurfaceForGfxSurface(gfxASurface * aSurface)1339 static already_AddRefed<SourceSurface> CreateSourceSurfaceForGfxSurface(
1340 gfxASurface* aSurface) {
1341 MOZ_ASSERT(aSurface);
1342 return Factory::CreateSourceSurfaceForCairoSurface(
1343 aSurface->CairoSurface(), aSurface->GetSize(),
1344 aSurface->GetSurfaceFormat());
1345 }
1346
EnsureSnapshotSurface(ScrollSnapshot & aSnapshotData,const mozilla::gfx::IntSize & aSize)1347 nsWindow::ScrollSnapshot* nsWindow::EnsureSnapshotSurface(
1348 ScrollSnapshot& aSnapshotData, const mozilla::gfx::IntSize& aSize) {
1349 // If the surface doesn't exist or is the wrong size then create new one.
1350 if (!aSnapshotData.surface || aSnapshotData.surface->GetSize() != aSize) {
1351 aSnapshotData.surface = new gfxWindowsSurface(aSize, kScrollCaptureFormat);
1352 aSnapshotData.surfaceHasSnapshot = false;
1353 }
1354
1355 return &aSnapshotData;
1356 }
1357
CreateScrollSnapshot()1358 already_AddRefed<SourceSurface> nsWindow::CreateScrollSnapshot() {
1359 RECT clip = {0};
1360 int rgnType = ::GetWindowRgnBox(mWnd, &clip);
1361 if (rgnType == RGN_ERROR) {
1362 // We failed to get the clip assume that we need a full fallback.
1363 clip.left = 0;
1364 clip.top = 0;
1365 clip.right = mBounds.Width();
1366 clip.bottom = mBounds.Height();
1367 return GetFallbackScrollSnapshot(clip);
1368 }
1369
1370 // Check that the window is in a position to snapshot. We don't check for
1371 // clipped width as that doesn't currently matter for APZ scrolling.
1372 if (clip.top || clip.bottom != mBounds.Height()) {
1373 return GetFallbackScrollSnapshot(clip);
1374 }
1375
1376 HDC windowDC = ::GetDC(mWnd);
1377 if (!windowDC) {
1378 return GetFallbackScrollSnapshot(clip);
1379 }
1380 auto releaseDC = MakeScopeExit([&] { ::ReleaseDC(mWnd, windowDC); });
1381
1382 gfx::IntSize snapshotSize(mBounds.Width(), mBounds.Height());
1383 ScrollSnapshot* snapshot;
1384 if (clip.left || clip.right != mBounds.Width()) {
1385 // Can't do a full snapshot, so use the partial snapshot.
1386 snapshot = EnsureSnapshotSurface(mPartialSnapshot, snapshotSize);
1387 } else {
1388 snapshot = EnsureSnapshotSurface(mFullSnapshot, snapshotSize);
1389 }
1390
1391 // Note that we know that the clip is full height.
1392 if (!::BitBlt(snapshot->surface->GetDC(), clip.left, 0,
1393 clip.right - clip.left, clip.bottom, windowDC, clip.left, 0,
1394 SRCCOPY)) {
1395 return GetFallbackScrollSnapshot(clip);
1396 }
1397 ::GdiFlush();
1398 snapshot->surface->Flush();
1399 snapshot->surfaceHasSnapshot = true;
1400 snapshot->clip = clip;
1401 mCurrentSnapshot = snapshot;
1402
1403 return CreateSourceSurfaceForGfxSurface(mCurrentSnapshot->surface);
1404 }
1405
GetFallbackScrollSnapshot(const RECT & aRequiredClip)1406 already_AddRefed<SourceSurface> nsWindow::GetFallbackScrollSnapshot(
1407 const RECT& aRequiredClip) {
1408 gfx::IntSize snapshotSize(mBounds.Width(), mBounds.Height());
1409
1410 // If the current snapshot is the correct size and covers the required clip,
1411 // just keep that by returning null.
1412 // Note: we know the clip is always full height.
1413 if (mCurrentSnapshot &&
1414 mCurrentSnapshot->surface->GetSize() == snapshotSize &&
1415 mCurrentSnapshot->clip.left <= aRequiredClip.left &&
1416 mCurrentSnapshot->clip.right >= aRequiredClip.right) {
1417 return nullptr;
1418 }
1419
1420 // Otherwise we'll use the full snapshot, making sure it is big enough first.
1421 mCurrentSnapshot = EnsureSnapshotSurface(mFullSnapshot, snapshotSize);
1422
1423 // If there is no snapshot, create a default.
1424 if (!mCurrentSnapshot->surfaceHasSnapshot) {
1425 gfx::SurfaceFormat format = mCurrentSnapshot->surface->GetSurfaceFormat();
1426 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForCairoSurface(
1427 mCurrentSnapshot->surface->CairoSurface(),
1428 mCurrentSnapshot->surface->GetSize(), &format);
1429
1430 DefaultFillScrollCapture(dt);
1431 }
1432
1433 return CreateSourceSurfaceForGfxSurface(mCurrentSnapshot->surface);
1434 }
1435
1436 /**************************************************************
1437 *
1438 * SECTION: nsIWidget::Show
1439 *
1440 * Hide or show this component.
1441 *
1442 **************************************************************/
1443
Show(bool bState)1444 void nsWindow::Show(bool bState) {
1445 if (mWindowType == eWindowType_popup) {
1446 // See bug 603793. When we try to draw D3D9/10 windows with a drop shadow
1447 // without the DWM on a secondary monitor, windows fails to composite
1448 // our windows correctly. We therefor switch off the drop shadow for
1449 // pop-up windows when the DWM is disabled and two monitors are
1450 // connected.
1451 if (HasBogusPopupsDropShadowOnMultiMonitor() &&
1452 WinUtils::GetMonitorCount() > 1 &&
1453 !nsUXThemeData::CheckForCompositor()) {
1454 if (sDropShadowEnabled) {
1455 ::SetClassLongA(mWnd, GCL_STYLE, 0);
1456 sDropShadowEnabled = false;
1457 }
1458 } else {
1459 if (!sDropShadowEnabled) {
1460 ::SetClassLongA(mWnd, GCL_STYLE, CS_DROPSHADOW);
1461 sDropShadowEnabled = true;
1462 }
1463 }
1464
1465 // WS_EX_COMPOSITED conflicts with the WS_EX_LAYERED style and causes
1466 // some popup menus to become invisible.
1467 LONG_PTR exStyle = ::GetWindowLongPtrW(mWnd, GWL_EXSTYLE);
1468 if (exStyle & WS_EX_LAYERED) {
1469 ::SetWindowLongPtrW(mWnd, GWL_EXSTYLE, exStyle & ~WS_EX_COMPOSITED);
1470 }
1471 }
1472
1473 bool syncInvalidate = false;
1474
1475 bool wasVisible = mIsVisible;
1476 // Set the status now so that anyone asking during ShowWindow or
1477 // SetWindowPos would get the correct answer.
1478 mIsVisible = bState;
1479
1480 // We may have cached an out of date visible state. This can happen
1481 // when session restore sets the full screen mode.
1482 if (mIsVisible)
1483 mOldStyle |= WS_VISIBLE;
1484 else
1485 mOldStyle &= ~WS_VISIBLE;
1486
1487 if (!mIsVisible && wasVisible) {
1488 ClearCachedResources();
1489 }
1490
1491 if (mWnd) {
1492 if (bState) {
1493 if (!wasVisible && mWindowType == eWindowType_toplevel) {
1494 // speed up the initial paint after show for
1495 // top level windows:
1496 syncInvalidate = true;
1497 switch (mSizeMode) {
1498 case nsSizeMode_Fullscreen:
1499 ::ShowWindow(mWnd, SW_SHOW);
1500 break;
1501 case nsSizeMode_Maximized:
1502 ::ShowWindow(mWnd, SW_SHOWMAXIMIZED);
1503 break;
1504 case nsSizeMode_Minimized:
1505 ::ShowWindow(mWnd, SW_SHOWMINIMIZED);
1506 break;
1507 default:
1508 if (CanTakeFocus()) {
1509 ::ShowWindow(mWnd, SW_SHOWNORMAL);
1510 } else {
1511 ::ShowWindow(mWnd, SW_SHOWNOACTIVATE);
1512 Unused << GetAttention(2);
1513 }
1514 break;
1515 }
1516 } else {
1517 DWORD flags = SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW;
1518 if (wasVisible) flags |= SWP_NOZORDER;
1519
1520 if (mWindowType == eWindowType_popup) {
1521 // ensure popups are the topmost of the TOPMOST
1522 // layer. Remember not to set the SWP_NOZORDER
1523 // flag as that might allow the taskbar to overlap
1524 // the popup.
1525 flags |= SWP_NOACTIVATE;
1526 HWND owner = ::GetWindow(mWnd, GW_OWNER);
1527 ::SetWindowPos(mWnd, owner ? 0 : HWND_TOPMOST, 0, 0, 0, 0, flags);
1528 } else {
1529 if (mWindowType == eWindowType_dialog && !CanTakeFocus())
1530 flags |= SWP_NOACTIVATE;
1531
1532 ::SetWindowPos(mWnd, HWND_TOP, 0, 0, 0, 0, flags);
1533 }
1534 }
1535
1536 if (!wasVisible && (mWindowType == eWindowType_toplevel ||
1537 mWindowType == eWindowType_dialog)) {
1538 // when a toplevel window or dialog is shown, initialize the UI state
1539 ::SendMessageW(
1540 mWnd, WM_CHANGEUISTATE,
1541 MAKEWPARAM(UIS_INITIALIZE, UISF_HIDEFOCUS | UISF_HIDEACCEL), 0);
1542 }
1543 } else {
1544 // Clear contents to avoid ghosting of old content if we display
1545 // this window again.
1546 if (wasVisible && mTransparencyMode == eTransparencyTransparent) {
1547 if (mCompositorWidgetDelegate) {
1548 mCompositorWidgetDelegate->ClearTransparentWindow();
1549 }
1550 }
1551 if (mWindowType != eWindowType_dialog) {
1552 ::ShowWindow(mWnd, SW_HIDE);
1553 } else {
1554 ::SetWindowPos(mWnd, 0, 0, 0, 0, 0,
1555 SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER |
1556 SWP_NOACTIVATE);
1557 }
1558 }
1559 }
1560
1561 #ifdef MOZ_XUL
1562 if (!wasVisible && bState) {
1563 Invalidate();
1564 if (syncInvalidate && !mInDtor && !mOnDestroyCalled) {
1565 ::UpdateWindow(mWnd);
1566 }
1567 }
1568 #endif
1569
1570 if (mOpeningAnimationSuppressed) {
1571 SuppressAnimation(false);
1572 }
1573 }
1574
1575 /**************************************************************
1576 *
1577 * SECTION: nsIWidget::IsVisible
1578 *
1579 * Returns the visibility state.
1580 *
1581 **************************************************************/
1582
1583 // Return true if the whether the component is visible, false otherwise
IsVisible() const1584 bool nsWindow::IsVisible() const { return mIsVisible; }
1585
1586 /**************************************************************
1587 *
1588 * SECTION: Window clipping utilities
1589 *
1590 * Used in Size and Move operations for setting the proper
1591 * window clipping regions for window transparency.
1592 *
1593 **************************************************************/
1594
1595 // XP and Vista visual styles sometimes require window clipping regions to be
1596 // applied for proper transparency. These routines are called on size and move
1597 // operations.
1598 // XXX this is apparently still needed in Windows 7 and later
ClearThemeRegion()1599 void nsWindow::ClearThemeRegion() {
1600 if (!HasGlass() &&
1601 (mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
1602 (mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) {
1603 SetWindowRgn(mWnd, nullptr, false);
1604 }
1605 }
1606
SetThemeRegion()1607 void nsWindow::SetThemeRegion() {
1608 // Popup types that have a visual styles region applied (bug 376408). This can
1609 // be expanded for other window types as needed. The regions are applied
1610 // generically to the base window so default constants are used for part and
1611 // state. At some point we might need part and state values from
1612 // nsNativeThemeWin's GetThemePartAndState, but currently windows that change
1613 // shape based on state haven't come up.
1614 if (!HasGlass() &&
1615 (mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
1616 (mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) {
1617 HRGN hRgn = nullptr;
1618 RECT rect = {0, 0, mBounds.Width(), mBounds.Height()};
1619
1620 HDC dc = ::GetDC(mWnd);
1621 GetThemeBackgroundRegion(nsUXThemeData::GetTheme(eUXTooltip), dc,
1622 TTP_STANDARD, TS_NORMAL, &rect, &hRgn);
1623 if (hRgn) {
1624 if (!SetWindowRgn(mWnd, hRgn,
1625 false)) // do not delete or alter hRgn if accepted.
1626 DeleteObject(hRgn);
1627 }
1628 ::ReleaseDC(mWnd, dc);
1629 }
1630 }
1631
1632 /**************************************************************
1633 *
1634 * SECTION: Touch and APZ-related functions
1635 *
1636 **************************************************************/
1637
RegisterTouchWindow()1638 void nsWindow::RegisterTouchWindow() {
1639 mTouchWindow = true;
1640 ::RegisterTouchWindow(mWnd, TWF_WANTPALM);
1641 ::EnumChildWindows(mWnd, nsWindow::RegisterTouchForDescendants, 0);
1642 }
1643
RegisterTouchForDescendants(HWND aWnd,LPARAM aMsg)1644 BOOL CALLBACK nsWindow::RegisterTouchForDescendants(HWND aWnd, LPARAM aMsg) {
1645 nsWindow* win = WinUtils::GetNSWindowPtr(aWnd);
1646 if (win) {
1647 ::RegisterTouchWindow(aWnd, TWF_WANTPALM);
1648 }
1649 return TRUE;
1650 }
1651
1652 /**************************************************************
1653 *
1654 * SECTION: nsIWidget::Move, nsIWidget::Resize,
1655 * nsIWidget::Size, nsIWidget::BeginResizeDrag
1656 *
1657 * Repositioning and sizing a window.
1658 *
1659 **************************************************************/
1660
SetSizeConstraints(const SizeConstraints & aConstraints)1661 void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints) {
1662 SizeConstraints c = aConstraints;
1663 if (mWindowType != eWindowType_popup) {
1664 c.mMinSize.width =
1665 std::max(int32_t(::GetSystemMetrics(SM_CXMINTRACK)), c.mMinSize.width);
1666 c.mMinSize.height =
1667 std::max(int32_t(::GetSystemMetrics(SM_CYMINTRACK)), c.mMinSize.height);
1668 }
1669 KnowsCompositor* knowsCompositor = GetLayerManager()->AsKnowsCompositor();
1670 if (knowsCompositor) {
1671 int32_t maxSize = knowsCompositor->GetMaxTextureSize();
1672 // We can't make ThebesLayers bigger than this anyway.. no point it letting
1673 // a window grow bigger as we won't be able to draw content there in
1674 // general.
1675 c.mMaxSize.width = std::min(c.mMaxSize.width, maxSize);
1676 c.mMaxSize.height = std::min(c.mMaxSize.height, maxSize);
1677 }
1678
1679 mSizeConstraintsScale = GetDefaultScale().scale;
1680
1681 nsBaseWidget::SetSizeConstraints(c);
1682 }
1683
GetSizeConstraints()1684 const SizeConstraints nsWindow::GetSizeConstraints() {
1685 double scale = GetDefaultScale().scale;
1686 if (mSizeConstraintsScale == scale || mSizeConstraintsScale == 0.0) {
1687 return mSizeConstraints;
1688 }
1689 scale /= mSizeConstraintsScale;
1690 SizeConstraints c = mSizeConstraints;
1691 if (c.mMinSize.width != NS_MAXSIZE) {
1692 c.mMinSize.width = NSToIntRound(c.mMinSize.width * scale);
1693 }
1694 if (c.mMinSize.height != NS_MAXSIZE) {
1695 c.mMinSize.height = NSToIntRound(c.mMinSize.height * scale);
1696 }
1697 if (c.mMaxSize.width != NS_MAXSIZE) {
1698 c.mMaxSize.width = NSToIntRound(c.mMaxSize.width * scale);
1699 }
1700 if (c.mMaxSize.height != NS_MAXSIZE) {
1701 c.mMaxSize.height = NSToIntRound(c.mMaxSize.height * scale);
1702 }
1703 return c;
1704 }
1705
1706 // Move this component
Move(double aX,double aY)1707 void nsWindow::Move(double aX, double aY) {
1708 if (mWindowType == eWindowType_toplevel ||
1709 mWindowType == eWindowType_dialog) {
1710 SetSizeMode(nsSizeMode_Normal);
1711 }
1712
1713 // for top-level windows only, convert coordinates from desktop pixels
1714 // (the "parent" coordinate space) to the window's device pixel space
1715 double scale =
1716 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
1717 int32_t x = NSToIntRound(aX * scale);
1718 int32_t y = NSToIntRound(aY * scale);
1719
1720 // Check to see if window needs to be moved first
1721 // to avoid a costly call to SetWindowPos. This check
1722 // can not be moved to the calling code in nsView, because
1723 // some platforms do not position child windows correctly
1724
1725 // Only perform this check for non-popup windows, since the positioning can
1726 // in fact change even when the x/y do not. We always need to perform the
1727 // check. See bug #97805 for details.
1728 if (mWindowType != eWindowType_popup && mBounds.IsEqualXY(x, y)) {
1729 // Nothing to do, since it is already positioned correctly.
1730 return;
1731 }
1732
1733 mBounds.MoveTo(x, y);
1734
1735 if (mWnd) {
1736 #ifdef DEBUG
1737 // complain if a window is moved offscreen (legal, but potentially
1738 // worrisome)
1739 if (mIsTopWidgetWindow) { // only a problem for top-level windows
1740 // Make sure this window is actually on the screen before we move it
1741 // XXX: Needs multiple monitor support
1742 HDC dc = ::GetDC(mWnd);
1743 if (dc) {
1744 if (::GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) {
1745 RECT workArea;
1746 ::SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0);
1747 // no annoying assertions. just mention the issue.
1748 if (x < 0 || x >= workArea.right || y < 0 || y >= workArea.bottom) {
1749 MOZ_LOG(gWindowsLog, LogLevel::Info,
1750 ("window moved to offscreen position\n"));
1751 }
1752 }
1753 ::ReleaseDC(mWnd, dc);
1754 }
1755 }
1756 #endif
1757 ClearThemeRegion();
1758
1759 UINT flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE;
1760 // Workaround SetWindowPos bug with D3D9. If our window has a clip
1761 // region, some drivers or OSes may incorrectly copy into the clipped-out
1762 // area.
1763 if (IsPlugin() && !mLayerManager && mClipRects &&
1764 (mClipRectCount != 1 ||
1765 !mClipRects[0].IsEqualInterior(
1766 LayoutDeviceIntRect(0, 0, mBounds.Width(), mBounds.Height())))) {
1767 flags |= SWP_NOCOPYBITS;
1768 }
1769 double oldScale = mDefaultScale;
1770 mResizeState = IN_SIZEMOVE;
1771 VERIFY(::SetWindowPos(mWnd, nullptr, x, y, 0, 0, flags));
1772 mResizeState = NOT_RESIZING;
1773 if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
1774 ChangedDPI();
1775 }
1776
1777 SetThemeRegion();
1778 }
1779 NotifyRollupGeometryChange();
1780 }
1781
1782 // Resize this component
Resize(double aWidth,double aHeight,bool aRepaint)1783 void nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) {
1784 // for top-level windows only, convert coordinates from desktop pixels
1785 // (the "parent" coordinate space) to the window's device pixel space
1786 double scale =
1787 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
1788 int32_t width = NSToIntRound(aWidth * scale);
1789 int32_t height = NSToIntRound(aHeight * scale);
1790
1791 NS_ASSERTION((width >= 0), "Negative width passed to nsWindow::Resize");
1792 NS_ASSERTION((height >= 0), "Negative height passed to nsWindow::Resize");
1793
1794 ConstrainSize(&width, &height);
1795
1796 // Avoid unnecessary resizing calls
1797 if (mBounds.IsEqualSize(width, height)) {
1798 if (aRepaint) {
1799 Invalidate();
1800 }
1801 return;
1802 }
1803
1804 // Set cached value for lightweight and printing
1805 mBounds.SizeTo(width, height);
1806
1807 if (mWnd) {
1808 UINT flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE;
1809
1810 if (!aRepaint) {
1811 flags |= SWP_NOREDRAW;
1812 }
1813
1814 ClearThemeRegion();
1815 double oldScale = mDefaultScale;
1816 VERIFY(
1817 ::SetWindowPos(mWnd, nullptr, 0, 0, width, GetHeight(height), flags));
1818 if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
1819 ChangedDPI();
1820 }
1821 SetThemeRegion();
1822 }
1823
1824 if (aRepaint) Invalidate();
1825
1826 NotifyRollupGeometryChange();
1827 }
1828
1829 // Resize this component
Resize(double aX,double aY,double aWidth,double aHeight,bool aRepaint)1830 void nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
1831 bool aRepaint) {
1832 // for top-level windows only, convert coordinates from desktop pixels
1833 // (the "parent" coordinate space) to the window's device pixel space
1834 double scale =
1835 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
1836 int32_t x = NSToIntRound(aX * scale);
1837 int32_t y = NSToIntRound(aY * scale);
1838 int32_t width = NSToIntRound(aWidth * scale);
1839 int32_t height = NSToIntRound(aHeight * scale);
1840
1841 NS_ASSERTION((width >= 0), "Negative width passed to nsWindow::Resize");
1842 NS_ASSERTION((height >= 0), "Negative height passed to nsWindow::Resize");
1843
1844 ConstrainSize(&width, &height);
1845
1846 // Avoid unnecessary resizing calls
1847 if (mBounds.IsEqualRect(x, y, width, height)) {
1848 if (aRepaint) {
1849 Invalidate();
1850 }
1851 return;
1852 }
1853
1854 // Set cached value for lightweight and printing
1855 mBounds.SetRect(x, y, width, height);
1856
1857 if (mWnd) {
1858 UINT flags = SWP_NOZORDER | SWP_NOACTIVATE;
1859 if (!aRepaint) {
1860 flags |= SWP_NOREDRAW;
1861 }
1862
1863 ClearThemeRegion();
1864 double oldScale = mDefaultScale;
1865 VERIFY(
1866 ::SetWindowPos(mWnd, nullptr, x, y, width, GetHeight(height), flags));
1867 if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
1868 ChangedDPI();
1869 }
1870 if (mTransitionWnd) {
1871 // If we have a fullscreen transition window, we need to make
1872 // it topmost again, otherwise the taskbar may be raised by
1873 // the system unexpectedly when we leave fullscreen state.
1874 ::SetWindowPos(mTransitionWnd, HWND_TOPMOST, 0, 0, 0, 0,
1875 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
1876 }
1877 SetThemeRegion();
1878 }
1879
1880 if (aRepaint) Invalidate();
1881
1882 NotifyRollupGeometryChange();
1883 }
1884
BeginResizeDrag(WidgetGUIEvent * aEvent,int32_t aHorizontal,int32_t aVertical)1885 nsresult nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent, int32_t aHorizontal,
1886 int32_t aVertical) {
1887 NS_ENSURE_ARG_POINTER(aEvent);
1888
1889 if (aEvent->mClass != eMouseEventClass) {
1890 // you can only begin a resize drag with a mouse event
1891 return NS_ERROR_INVALID_ARG;
1892 }
1893
1894 if (aEvent->AsMouseEvent()->button != WidgetMouseEvent::eLeftButton) {
1895 // you can only begin a resize drag with the left mouse button
1896 return NS_ERROR_INVALID_ARG;
1897 }
1898
1899 // work out what sizemode we're talking about
1900 WPARAM syscommand;
1901 if (aVertical < 0) {
1902 if (aHorizontal < 0) {
1903 syscommand = SC_SIZE | WMSZ_TOPLEFT;
1904 } else if (aHorizontal == 0) {
1905 syscommand = SC_SIZE | WMSZ_TOP;
1906 } else {
1907 syscommand = SC_SIZE | WMSZ_TOPRIGHT;
1908 }
1909 } else if (aVertical == 0) {
1910 if (aHorizontal < 0) {
1911 syscommand = SC_SIZE | WMSZ_LEFT;
1912 } else if (aHorizontal == 0) {
1913 return NS_ERROR_INVALID_ARG;
1914 } else {
1915 syscommand = SC_SIZE | WMSZ_RIGHT;
1916 }
1917 } else {
1918 if (aHorizontal < 0) {
1919 syscommand = SC_SIZE | WMSZ_BOTTOMLEFT;
1920 } else if (aHorizontal == 0) {
1921 syscommand = SC_SIZE | WMSZ_BOTTOM;
1922 } else {
1923 syscommand = SC_SIZE | WMSZ_BOTTOMRIGHT;
1924 }
1925 }
1926
1927 // resizing doesn't work if the mouse is already captured
1928 CaptureMouse(false);
1929
1930 // find the top-level window
1931 HWND toplevelWnd = WinUtils::GetTopLevelHWND(mWnd, true);
1932
1933 // tell Windows to start the resize
1934 ::PostMessage(toplevelWnd, WM_SYSCOMMAND, syscommand,
1935 POINTTOPOINTS(aEvent->mRefPoint));
1936
1937 return NS_OK;
1938 }
1939
1940 /**************************************************************
1941 *
1942 * SECTION: Window Z-order and state.
1943 *
1944 * nsIWidget::PlaceBehind, nsIWidget::SetSizeMode,
1945 * nsIWidget::ConstrainPosition
1946 *
1947 * Z-order, positioning, restore, minimize, and maximize.
1948 *
1949 **************************************************************/
1950
1951 // Position the window behind the given window
PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,nsIWidget * aWidget,bool aActivate)1952 void nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
1953 nsIWidget* aWidget, bool aActivate) {
1954 HWND behind = HWND_TOP;
1955 if (aPlacement == eZPlacementBottom)
1956 behind = HWND_BOTTOM;
1957 else if (aPlacement == eZPlacementBelow && aWidget)
1958 behind = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW);
1959 UINT flags = SWP_NOMOVE | SWP_NOREPOSITION | SWP_NOSIZE;
1960 if (!aActivate) flags |= SWP_NOACTIVATE;
1961
1962 if (!CanTakeFocus() && behind == HWND_TOP) {
1963 // Can't place the window to top so place it behind the foreground window
1964 // (as long as it is not topmost)
1965 HWND wndAfter = ::GetForegroundWindow();
1966 if (!wndAfter)
1967 behind = HWND_BOTTOM;
1968 else if (!(GetWindowLongPtrW(wndAfter, GWL_EXSTYLE) & WS_EX_TOPMOST))
1969 behind = wndAfter;
1970 flags |= SWP_NOACTIVATE;
1971 }
1972
1973 ::SetWindowPos(mWnd, behind, 0, 0, 0, 0, flags);
1974 }
1975
GetCurrentShowCmd(HWND aWnd)1976 static UINT GetCurrentShowCmd(HWND aWnd) {
1977 WINDOWPLACEMENT pl;
1978 pl.length = sizeof(pl);
1979 ::GetWindowPlacement(aWnd, &pl);
1980 return pl.showCmd;
1981 }
1982
1983 // Maximize, minimize or restore the window.
SetSizeMode(nsSizeMode aMode)1984 void nsWindow::SetSizeMode(nsSizeMode aMode) {
1985 // Let's not try and do anything if we're already in that state.
1986 // (This is needed to prevent problems when calling window.minimize(), which
1987 // calls us directly, and then the OS triggers another call to us.)
1988 if (aMode == mSizeMode) return;
1989
1990 // save the requested state
1991 mLastSizeMode = mSizeMode;
1992 nsBaseWidget::SetSizeMode(aMode);
1993 if (mIsVisible) {
1994 int mode;
1995
1996 switch (aMode) {
1997 case nsSizeMode_Fullscreen:
1998 mode = SW_SHOW;
1999 break;
2000
2001 case nsSizeMode_Maximized:
2002 mode = SW_MAXIMIZE;
2003 break;
2004
2005 case nsSizeMode_Minimized:
2006 mode = SW_MINIMIZE;
2007 break;
2008
2009 default:
2010 mode = SW_RESTORE;
2011 }
2012
2013 // Don't call ::ShowWindow if we're trying to "restore" a window that is
2014 // already in a normal state. Prevents a bug where snapping to one side
2015 // of the screen and then minimizing would cause Windows to forget our
2016 // window's correct restored position/size.
2017 if (!(GetCurrentShowCmd(mWnd) == SW_SHOWNORMAL && mode == SW_RESTORE)) {
2018 ::ShowWindow(mWnd, mode);
2019 }
2020 // we activate here to ensure that the right child window is focused
2021 if (mode == SW_MAXIMIZE || mode == SW_SHOW)
2022 DispatchFocusToTopLevelWindow(true);
2023 }
2024 }
2025
SuppressAnimation(bool aSuppress)2026 void nsWindow::SuppressAnimation(bool aSuppress) {
2027 DWORD dwAttribute = aSuppress ? TRUE : FALSE;
2028 DwmSetWindowAttribute(mWnd, DWMWA_TRANSITIONS_FORCEDISABLED, &dwAttribute,
2029 sizeof dwAttribute);
2030 }
2031
2032 // Constrain a potential move to fit onscreen
2033 // Position (aX, aY) is specified in Windows screen (logical) pixels,
2034 // except when using per-monitor DPI, in which case it's device pixels.
ConstrainPosition(bool aAllowSlop,int32_t * aX,int32_t * aY)2035 void nsWindow::ConstrainPosition(bool aAllowSlop, int32_t* aX, int32_t* aY) {
2036 if (!mIsTopWidgetWindow) // only a problem for top-level windows
2037 return;
2038
2039 double dpiScale = GetDesktopToDeviceScale().scale;
2040
2041 // We need to use the window size in the kind of pixels used for window-
2042 // manipulation APIs.
2043 int32_t logWidth =
2044 std::max<int32_t>(NSToIntRound(mBounds.Width() / dpiScale), 1);
2045 int32_t logHeight =
2046 std::max<int32_t>(NSToIntRound(mBounds.Height() / dpiScale), 1);
2047
2048 /* get our playing field. use the current screen, or failing that
2049 for any reason, use device caps for the default screen. */
2050 RECT screenRect;
2051
2052 nsCOMPtr<nsIScreenManager> screenmgr =
2053 do_GetService(sScreenManagerContractID);
2054 if (!screenmgr) {
2055 return;
2056 }
2057 nsCOMPtr<nsIScreen> screen;
2058 int32_t left, top, width, height;
2059
2060 screenmgr->ScreenForRect(*aX, *aY, logWidth, logHeight,
2061 getter_AddRefs(screen));
2062 if (mSizeMode != nsSizeMode_Fullscreen) {
2063 // For normalized windows, use the desktop work area.
2064 nsresult rv = screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
2065 if (NS_FAILED(rv)) {
2066 return;
2067 }
2068 } else {
2069 // For full screen windows, use the desktop.
2070 nsresult rv = screen->GetRectDisplayPix(&left, &top, &width, &height);
2071 if (NS_FAILED(rv)) {
2072 return;
2073 }
2074 }
2075 screenRect.left = left;
2076 screenRect.right = left + width;
2077 screenRect.top = top;
2078 screenRect.bottom = top + height;
2079
2080 if (aAllowSlop) {
2081 if (*aX < screenRect.left - logWidth + kWindowPositionSlop)
2082 *aX = screenRect.left - logWidth + kWindowPositionSlop;
2083 else if (*aX >= screenRect.right - kWindowPositionSlop)
2084 *aX = screenRect.right - kWindowPositionSlop;
2085
2086 if (*aY < screenRect.top - logHeight + kWindowPositionSlop)
2087 *aY = screenRect.top - logHeight + kWindowPositionSlop;
2088 else if (*aY >= screenRect.bottom - kWindowPositionSlop)
2089 *aY = screenRect.bottom - kWindowPositionSlop;
2090
2091 } else {
2092 if (*aX < screenRect.left)
2093 *aX = screenRect.left;
2094 else if (*aX >= screenRect.right - logWidth)
2095 *aX = screenRect.right - logWidth;
2096
2097 if (*aY < screenRect.top)
2098 *aY = screenRect.top;
2099 else if (*aY >= screenRect.bottom - logHeight)
2100 *aY = screenRect.bottom - logHeight;
2101 }
2102 }
2103
2104 /**************************************************************
2105 *
2106 * SECTION: nsIWidget::Enable, nsIWidget::IsEnabled
2107 *
2108 * Enabling and disabling the widget.
2109 *
2110 **************************************************************/
2111
2112 // Enable/disable this component
Enable(bool bState)2113 void nsWindow::Enable(bool bState) {
2114 if (mWnd) {
2115 ::EnableWindow(mWnd, bState);
2116 }
2117 }
2118
2119 // Return the current enable state
IsEnabled() const2120 bool nsWindow::IsEnabled() const {
2121 return !mWnd || (::IsWindowEnabled(mWnd) &&
2122 ::IsWindowEnabled(::GetAncestor(mWnd, GA_ROOT)));
2123 }
2124
2125 /**************************************************************
2126 *
2127 * SECTION: nsIWidget::SetFocus
2128 *
2129 * Give the focus to this widget.
2130 *
2131 **************************************************************/
2132
SetFocus(bool aRaise)2133 nsresult nsWindow::SetFocus(bool aRaise) {
2134 if (mWnd) {
2135 #ifdef WINSTATE_DEBUG_OUTPUT
2136 if (mWnd == WinUtils::GetTopLevelHWND(mWnd)) {
2137 MOZ_LOG(gWindowsLog, LogLevel::Info,
2138 ("*** SetFocus: [ top] raise=%d\n", aRaise));
2139 } else {
2140 MOZ_LOG(gWindowsLog, LogLevel::Info,
2141 ("*** SetFocus: [child] raise=%d\n", aRaise));
2142 }
2143 #endif
2144 // Uniconify, if necessary
2145 HWND toplevelWnd = WinUtils::GetTopLevelHWND(mWnd);
2146 if (aRaise && ::IsIconic(toplevelWnd)) {
2147 ::ShowWindow(toplevelWnd, SW_RESTORE);
2148 }
2149 ::SetFocus(mWnd);
2150 }
2151 return NS_OK;
2152 }
2153
2154 /**************************************************************
2155 *
2156 * SECTION: Bounds
2157 *
2158 * GetBounds, GetClientBounds, GetScreenBounds,
2159 * GetRestoredBounds, GetClientOffset
2160 * SetDrawsInTitlebar, SetNonClientMargins
2161 *
2162 * Bound calculations.
2163 *
2164 **************************************************************/
2165
2166 // Return the window's full dimensions in screen coordinates.
2167 // If the window has a parent, converts the origin to an offset
2168 // of the parent's screen origin.
GetBounds()2169 LayoutDeviceIntRect nsWindow::GetBounds() {
2170 if (!mWnd) {
2171 return mBounds;
2172 }
2173
2174 RECT r;
2175 VERIFY(::GetWindowRect(mWnd, &r));
2176
2177 LayoutDeviceIntRect rect;
2178
2179 // assign size
2180 rect.SizeTo(r.right - r.left, r.bottom - r.top);
2181
2182 // popup window bounds' are in screen coordinates, not relative to parent
2183 // window
2184 if (mWindowType == eWindowType_popup) {
2185 rect.MoveTo(r.left, r.top);
2186 return rect;
2187 }
2188
2189 // chrome on parent:
2190 // ___ 5,5 (chrome start)
2191 // | ____ 10,10 (client start)
2192 // | | ____ 20,20 (child start)
2193 // | | |
2194 // 20,20 - 5,5 = 15,15 (??)
2195 // minus GetClientOffset:
2196 // 15,15 - 5,5 = 10,10
2197 //
2198 // no chrome on parent:
2199 // ______ 10,10 (win start)
2200 // | ____ 20,20 (child start)
2201 // | |
2202 // 20,20 - 10,10 = 10,10
2203 //
2204 // walking the chain:
2205 // ___ 5,5 (chrome start)
2206 // | ___ 10,10 (client start)
2207 // | | ___ 20,20 (child start)
2208 // | | | __ 30,30 (child start)
2209 // | | | |
2210 // 30,30 - 20,20 = 10,10 (offset from second child to first)
2211 // 20,20 - 5,5 = 15,15 + 10,10 = 25,25 (??)
2212 // minus GetClientOffset:
2213 // 25,25 - 5,5 = 20,20 (offset from second child to parent client)
2214
2215 // convert coordinates if parent exists
2216 HWND parent = ::GetParent(mWnd);
2217 if (parent) {
2218 RECT pr;
2219 VERIFY(::GetWindowRect(parent, &pr));
2220 r.left -= pr.left;
2221 r.top -= pr.top;
2222 // adjust for chrome
2223 nsWindow* pWidget = static_cast<nsWindow*>(GetParent());
2224 if (pWidget && pWidget->IsTopLevelWidget()) {
2225 LayoutDeviceIntPoint clientOffset = pWidget->GetClientOffset();
2226 r.left -= clientOffset.x;
2227 r.top -= clientOffset.y;
2228 }
2229 }
2230 rect.MoveTo(r.left, r.top);
2231 return rect;
2232 }
2233
2234 // Get this component dimension
GetClientBounds()2235 LayoutDeviceIntRect nsWindow::GetClientBounds() {
2236 if (!mWnd) {
2237 return LayoutDeviceIntRect(0, 0, 0, 0);
2238 }
2239
2240 RECT r;
2241 VERIFY(::GetClientRect(mWnd, &r));
2242
2243 LayoutDeviceIntRect bounds = GetBounds();
2244 LayoutDeviceIntRect rect;
2245 rect.MoveTo(bounds.TopLeft() + GetClientOffset());
2246 rect.SizeTo(r.right - r.left, r.bottom - r.top);
2247 return rect;
2248 }
2249
2250 // Like GetBounds, but don't offset by the parent
GetScreenBounds()2251 LayoutDeviceIntRect nsWindow::GetScreenBounds() {
2252 if (!mWnd) {
2253 return mBounds;
2254 }
2255
2256 RECT r;
2257 VERIFY(::GetWindowRect(mWnd, &r));
2258
2259 return LayoutDeviceIntRect(r.left, r.top, r.right - r.left, r.bottom - r.top);
2260 }
2261
GetRestoredBounds(LayoutDeviceIntRect & aRect)2262 nsresult nsWindow::GetRestoredBounds(LayoutDeviceIntRect& aRect) {
2263 if (SizeMode() == nsSizeMode_Normal) {
2264 aRect = GetScreenBounds();
2265 return NS_OK;
2266 }
2267 if (!mWnd) {
2268 return NS_ERROR_FAILURE;
2269 }
2270
2271 WINDOWPLACEMENT pl = {sizeof(WINDOWPLACEMENT)};
2272 VERIFY(::GetWindowPlacement(mWnd, &pl));
2273 const RECT& r = pl.rcNormalPosition;
2274
2275 HMONITOR monitor = ::MonitorFromWindow(mWnd, MONITOR_DEFAULTTONULL);
2276 if (!monitor) {
2277 return NS_ERROR_FAILURE;
2278 }
2279 MONITORINFO mi = {sizeof(MONITORINFO)};
2280 VERIFY(::GetMonitorInfo(monitor, &mi));
2281
2282 aRect.SetRect(r.left, r.top, r.right - r.left, r.bottom - r.top);
2283 aRect.MoveBy(mi.rcWork.left - mi.rcMonitor.left,
2284 mi.rcWork.top - mi.rcMonitor.top);
2285 return NS_OK;
2286 }
2287
2288 // Return the x,y offset of the client area from the origin of the window. If
2289 // the window is borderless returns (0,0).
GetClientOffset()2290 LayoutDeviceIntPoint nsWindow::GetClientOffset() {
2291 if (!mWnd) {
2292 return LayoutDeviceIntPoint(0, 0);
2293 }
2294
2295 RECT r1;
2296 GetWindowRect(mWnd, &r1);
2297 LayoutDeviceIntPoint pt = WidgetToScreenOffset();
2298 return LayoutDeviceIntPoint(pt.x - r1.left, pt.y - r1.top);
2299 }
2300
SetDrawsInTitlebar(bool aState)2301 void nsWindow::SetDrawsInTitlebar(bool aState) {
2302 nsWindow* window = GetTopLevelWindow(true);
2303 if (window && window != this) {
2304 return window->SetDrawsInTitlebar(aState);
2305 }
2306
2307 if (aState) {
2308 // top, right, bottom, left for nsIntMargin
2309 LayoutDeviceIntMargin margins(0, -1, -1, -1);
2310 SetNonClientMargins(margins);
2311 } else {
2312 LayoutDeviceIntMargin margins(-1, -1, -1, -1);
2313 SetNonClientMargins(margins);
2314 }
2315 }
2316
ResetLayout()2317 void nsWindow::ResetLayout() {
2318 // This will trigger a frame changed event, triggering
2319 // nc calc size and a sizemode gecko event.
2320 SetWindowPos(mWnd, 0, 0, 0, 0, 0,
2321 SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE |
2322 SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER);
2323
2324 // If hidden, just send the frame changed event for now.
2325 if (!mIsVisible) return;
2326
2327 // Send a gecko size event to trigger reflow.
2328 RECT clientRc = {0};
2329 GetClientRect(mWnd, &clientRc);
2330 OnResize(WinUtils::ToIntRect(clientRc).Size());
2331
2332 // Invalidate and update
2333 Invalidate();
2334 }
2335
2336 // Internally track the caption status via a window property. Required
2337 // due to our internal handling of WM_NCACTIVATE when custom client
2338 // margins are set.
2339 static const wchar_t kManageWindowInfoProperty[] = L"ManageWindowInfoProperty";
2340 typedef BOOL(WINAPI* GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
2341 static GetWindowInfoPtr sGetWindowInfoPtrStub = nullptr;
2342
GetWindowInfoHook(HWND hWnd,PWINDOWINFO pwi)2343 BOOL WINAPI GetWindowInfoHook(HWND hWnd, PWINDOWINFO pwi) {
2344 if (!sGetWindowInfoPtrStub) {
2345 NS_ASSERTION(FALSE, "Something is horribly wrong in GetWindowInfoHook!");
2346 return FALSE;
2347 }
2348 int windowStatus =
2349 reinterpret_cast<LONG_PTR>(GetPropW(hWnd, kManageWindowInfoProperty));
2350 // No property set, return the default data.
2351 if (!windowStatus) return sGetWindowInfoPtrStub(hWnd, pwi);
2352 // Call GetWindowInfo and update dwWindowStatus with our
2353 // internally tracked value.
2354 BOOL result = sGetWindowInfoPtrStub(hWnd, pwi);
2355 if (result && pwi)
2356 pwi->dwWindowStatus = (windowStatus == 1 ? 0 : WS_ACTIVECAPTION);
2357 return result;
2358 }
2359
UpdateGetWindowInfoCaptionStatus(bool aActiveCaption)2360 void nsWindow::UpdateGetWindowInfoCaptionStatus(bool aActiveCaption) {
2361 if (!mWnd) return;
2362
2363 if (!sGetWindowInfoPtrStub) {
2364 sUser32Intercept.Init("user32.dll");
2365 if (!sUser32Intercept.AddHook("GetWindowInfo",
2366 reinterpret_cast<intptr_t>(GetWindowInfoHook),
2367 (void**)&sGetWindowInfoPtrStub))
2368 return;
2369 }
2370 // Update our internally tracked caption status
2371 SetPropW(mWnd, kManageWindowInfoProperty,
2372 reinterpret_cast<HANDLE>(static_cast<INT_PTR>(aActiveCaption) + 1));
2373 }
2374
2375 /**
2376 * Called when the window layout changes: full screen mode transitions,
2377 * theme changes, and composition changes. Calculates the new non-client
2378 * margins and fires off a frame changed event, which triggers an nc calc
2379 * size windows event, kicking the changes in.
2380 *
2381 * The offsets calculated here are based on the value of `mNonClientMargins`
2382 * which is specified in the "chromemargins" attribute of the window. For
2383 * each margin, the value specified has the following meaning:
2384 * -1 - leave the default frame in place
2385 * 0 - remove the frame
2386 * >0 - frame size equals min(0, (default frame size - margin value))
2387 *
2388 * This function calculates and populates `mNonClientOffset`.
2389 * In our processing of `WM_NCCALCSIZE`, the frame size will be calculated
2390 * as (default frame size - offset). For example, if the left frame should
2391 * be 1 pixel narrower than the default frame size, `mNonClientOffset.left`
2392 * will equal 1.
2393 *
2394 * For maximized, fullscreen, and minimized windows, the values stored in
2395 * `mNonClientMargins` are ignored, and special processing takes place.
2396 *
2397 * For non-glass windows, we only allow frames to be their default size
2398 * or removed entirely.
2399 */
UpdateNonClientMargins(int32_t aSizeMode,bool aReflowWindow)2400 bool nsWindow::UpdateNonClientMargins(int32_t aSizeMode, bool aReflowWindow) {
2401 if (!mCustomNonClient) return false;
2402
2403 if (aSizeMode == -1) {
2404 aSizeMode = mSizeMode;
2405 }
2406
2407 bool hasCaption = (mBorderStyle & (eBorderStyle_all | eBorderStyle_title |
2408 eBorderStyle_menu | eBorderStyle_default));
2409
2410 // mCaptionHeight is the default size of the NC area at
2411 // the top of the window. If the window has a caption,
2412 // the size is calculated as the sum of:
2413 // SM_CYFRAME - The thickness of the sizing border
2414 // around a resizable window
2415 // SM_CXPADDEDBORDER - The amount of border padding
2416 // for captioned windows
2417 // SM_CYCAPTION - The height of the caption area
2418 //
2419 // If the window does not have a caption, mCaptionHeight will be equal to
2420 // `GetSystemMetrics(SM_CYFRAME)`
2421 mCaptionHeight = GetSystemMetrics(SM_CYFRAME) +
2422 (hasCaption ? GetSystemMetrics(SM_CYCAPTION) +
2423 GetSystemMetrics(SM_CXPADDEDBORDER)
2424 : 0);
2425
2426 // mHorResizeMargin is the size of the default NC areas on the
2427 // left and right sides of our window. It is calculated as
2428 // the sum of:
2429 // SM_CXFRAME - The thickness of the sizing border
2430 // SM_CXPADDEDBORDER - The amount of border padding
2431 // for captioned windows
2432 //
2433 // If the window does not have a caption, mHorResizeMargin will be equal to
2434 // `GetSystemMetrics(SM_CXFRAME)`
2435 mHorResizeMargin = GetSystemMetrics(SM_CXFRAME) +
2436 (hasCaption ? GetSystemMetrics(SM_CXPADDEDBORDER) : 0);
2437
2438 // mVertResizeMargin is the size of the default NC area at the
2439 // bottom of the window. It is calculated as the sum of:
2440 // SM_CYFRAME - The thickness of the sizing border
2441 // SM_CXPADDEDBORDER - The amount of border padding
2442 // for captioned windows.
2443 //
2444 // If the window does not have a caption, mVertResizeMargin will be equal to
2445 // `GetSystemMetrics(SM_CYFRAME)`
2446 mVertResizeMargin = GetSystemMetrics(SM_CYFRAME) +
2447 (hasCaption ? GetSystemMetrics(SM_CXPADDEDBORDER) : 0);
2448
2449 if (aSizeMode == nsSizeMode_Minimized) {
2450 // Use default frame size for minimized windows
2451 mNonClientOffset.top = 0;
2452 mNonClientOffset.left = 0;
2453 mNonClientOffset.right = 0;
2454 mNonClientOffset.bottom = 0;
2455 } else if (aSizeMode == nsSizeMode_Fullscreen) {
2456 // Remove the default frame from the top of our fullscreen window. This
2457 // makes the whole caption part of our client area, allowing us to draw
2458 // in the whole caption area. Additionally remove the default frame from
2459 // the left, right, and bottom.
2460 mNonClientOffset.top = mCaptionHeight;
2461 mNonClientOffset.bottom = mVertResizeMargin;
2462 mNonClientOffset.left = mHorResizeMargin;
2463 mNonClientOffset.right = mHorResizeMargin;
2464 } else if (aSizeMode == nsSizeMode_Maximized) {
2465 // Remove the default frame from the top of our maximized window. This
2466 // makes the whole caption part of our client area, allowing us to draw
2467 // in the whole caption area. Use default frame size on left, right, and
2468 // bottom. The reason this works is that, for maximized windows,
2469 // Windows positions them so that their frames fall off the screen.
2470 // This gives the illusion of windows having no frames when they are
2471 // maximized. If we try to mess with the frame sizes by setting these
2472 // offsets to positive values, our client area will fall off the screen.
2473 mNonClientOffset.top = mCaptionHeight;
2474 mNonClientOffset.bottom = 0;
2475 mNonClientOffset.left = 0;
2476 mNonClientOffset.right = 0;
2477
2478 APPBARDATA appBarData;
2479 appBarData.cbSize = sizeof(appBarData);
2480 UINT taskbarState = SHAppBarMessage(ABM_GETSTATE, &appBarData);
2481 if (ABS_AUTOHIDE & taskbarState) {
2482 UINT edge = -1;
2483 appBarData.hWnd = FindWindow(L"Shell_TrayWnd", nullptr);
2484 if (appBarData.hWnd) {
2485 HMONITOR taskbarMonitor =
2486 ::MonitorFromWindow(appBarData.hWnd, MONITOR_DEFAULTTOPRIMARY);
2487 HMONITOR windowMonitor =
2488 ::MonitorFromWindow(mWnd, MONITOR_DEFAULTTONEAREST);
2489 if (taskbarMonitor == windowMonitor) {
2490 SHAppBarMessage(ABM_GETTASKBARPOS, &appBarData);
2491 edge = appBarData.uEdge;
2492 }
2493 }
2494
2495 if (ABE_LEFT == edge) {
2496 mNonClientOffset.left -= 1;
2497 } else if (ABE_RIGHT == edge) {
2498 mNonClientOffset.right -= 1;
2499 } else if (ABE_BOTTOM == edge || ABE_TOP == edge) {
2500 mNonClientOffset.bottom -= 1;
2501 }
2502 }
2503 } else {
2504 bool glass = nsUXThemeData::CheckForCompositor();
2505
2506 // We're dealing with a "normal" window (not maximized, minimized, or
2507 // fullscreen), so process `mNonClientMargins` and set `mNonClientOffset`
2508 // accordingly.
2509 //
2510 // Setting `mNonClientOffset` to 0 has the effect of leaving the default
2511 // frame intact. Setting it to a value greater than 0 reduces the frame
2512 // size by that amount.
2513
2514 if (mNonClientMargins.top > 0 && glass) {
2515 mNonClientOffset.top = std::min(mCaptionHeight, mNonClientMargins.top);
2516 } else if (mNonClientMargins.top == 0) {
2517 mNonClientOffset.top = mCaptionHeight;
2518 } else {
2519 mNonClientOffset.top = 0;
2520 }
2521
2522 if (mNonClientMargins.bottom > 0 && glass) {
2523 mNonClientOffset.bottom =
2524 std::min(mVertResizeMargin, mNonClientMargins.bottom);
2525 } else if (mNonClientMargins.bottom == 0) {
2526 mNonClientOffset.bottom = mVertResizeMargin;
2527 } else {
2528 mNonClientOffset.bottom = 0;
2529 }
2530
2531 if (mNonClientMargins.left > 0 && glass) {
2532 mNonClientOffset.left =
2533 std::min(mHorResizeMargin, mNonClientMargins.left);
2534 } else if (mNonClientMargins.left == 0) {
2535 mNonClientOffset.left = mHorResizeMargin;
2536 } else {
2537 mNonClientOffset.left = 0;
2538 }
2539
2540 if (mNonClientMargins.right > 0 && glass) {
2541 mNonClientOffset.right =
2542 std::min(mHorResizeMargin, mNonClientMargins.right);
2543 } else if (mNonClientMargins.right == 0) {
2544 mNonClientOffset.right = mHorResizeMargin;
2545 } else {
2546 mNonClientOffset.right = 0;
2547 }
2548 }
2549
2550 if (aReflowWindow) {
2551 // Force a reflow of content based on the new client
2552 // dimensions.
2553 ResetLayout();
2554 }
2555
2556 return true;
2557 }
2558
SetNonClientMargins(LayoutDeviceIntMargin & margins)2559 nsresult nsWindow::SetNonClientMargins(LayoutDeviceIntMargin& margins) {
2560 if (!mIsTopWidgetWindow || mBorderStyle == eBorderStyle_none)
2561 return NS_ERROR_INVALID_ARG;
2562
2563 if (mHideChrome) {
2564 mFutureMarginsOnceChromeShows = margins;
2565 mFutureMarginsToUse = true;
2566 return NS_OK;
2567 }
2568 mFutureMarginsToUse = false;
2569
2570 // Request for a reset
2571 if (margins.top == -1 && margins.left == -1 && margins.right == -1 &&
2572 margins.bottom == -1) {
2573 mCustomNonClient = false;
2574 mNonClientMargins = margins;
2575 // Force a reflow of content based on the new client
2576 // dimensions.
2577 ResetLayout();
2578
2579 int windowStatus =
2580 reinterpret_cast<LONG_PTR>(GetPropW(mWnd, kManageWindowInfoProperty));
2581 if (windowStatus) {
2582 ::SendMessageW(mWnd, WM_NCACTIVATE, 1 != windowStatus, 0);
2583 }
2584
2585 return NS_OK;
2586 }
2587
2588 if (margins.top < -1 || margins.bottom < -1 || margins.left < -1 ||
2589 margins.right < -1)
2590 return NS_ERROR_INVALID_ARG;
2591
2592 mNonClientMargins = margins;
2593 mCustomNonClient = true;
2594 if (!UpdateNonClientMargins()) {
2595 NS_WARNING("UpdateNonClientMargins failed!");
2596 return NS_OK;
2597 }
2598
2599 return NS_OK;
2600 }
2601
InvalidateNonClientRegion()2602 void nsWindow::InvalidateNonClientRegion() {
2603 // +-+-----------------------+-+
2604 // | | app non-client chrome | |
2605 // | +-----------------------+ |
2606 // | | app client chrome | | }
2607 // | +-----------------------+ | }
2608 // | | app content | | } area we don't want to invalidate
2609 // | +-----------------------+ | }
2610 // | | app client chrome | | }
2611 // | +-----------------------+ |
2612 // +---------------------------+ <
2613 // ^ ^ windows non-client chrome
2614 // client area = app *
2615 RECT rect;
2616 GetWindowRect(mWnd, &rect);
2617 MapWindowPoints(nullptr, mWnd, (LPPOINT)&rect, 2);
2618 HRGN winRgn = CreateRectRgnIndirect(&rect);
2619
2620 // Subtract app client chrome and app content leaving
2621 // windows non-client chrome and app non-client chrome
2622 // in winRgn.
2623 GetWindowRect(mWnd, &rect);
2624 rect.top += mCaptionHeight;
2625 rect.right -= mHorResizeMargin;
2626 rect.bottom -= mHorResizeMargin;
2627 rect.left += mVertResizeMargin;
2628 MapWindowPoints(nullptr, mWnd, (LPPOINT)&rect, 2);
2629 HRGN clientRgn = CreateRectRgnIndirect(&rect);
2630 CombineRgn(winRgn, winRgn, clientRgn, RGN_DIFF);
2631 DeleteObject(clientRgn);
2632
2633 // triggers ncpaint and paint events for the two areas
2634 RedrawWindow(mWnd, nullptr, winRgn, RDW_FRAME | RDW_INVALIDATE);
2635 DeleteObject(winRgn);
2636 }
2637
ExcludeNonClientFromPaintRegion(HRGN aRegion)2638 HRGN nsWindow::ExcludeNonClientFromPaintRegion(HRGN aRegion) {
2639 RECT rect;
2640 HRGN rgn = nullptr;
2641 if (aRegion == (HRGN)1) { // undocumented value indicating a full refresh
2642 GetWindowRect(mWnd, &rect);
2643 rgn = CreateRectRgnIndirect(&rect);
2644 } else {
2645 rgn = aRegion;
2646 }
2647 GetClientRect(mWnd, &rect);
2648 MapWindowPoints(mWnd, nullptr, (LPPOINT)&rect, 2);
2649 HRGN nonClientRgn = CreateRectRgnIndirect(&rect);
2650 CombineRgn(rgn, rgn, nonClientRgn, RGN_DIFF);
2651 DeleteObject(nonClientRgn);
2652 return rgn;
2653 }
2654
2655 /**************************************************************
2656 *
2657 * SECTION: nsIWidget::SetBackgroundColor
2658 *
2659 * Sets the window background paint color.
2660 *
2661 **************************************************************/
2662
SetBackgroundColor(const nscolor & aColor)2663 void nsWindow::SetBackgroundColor(const nscolor& aColor) {
2664 if (mBrush) ::DeleteObject(mBrush);
2665
2666 mBrush = ::CreateSolidBrush(NSRGB_2_COLOREF(aColor));
2667 if (mWnd != nullptr) {
2668 ::SetClassLongPtrW(mWnd, GCLP_HBRBACKGROUND, (LONG_PTR)mBrush);
2669 }
2670 }
2671
2672 /**************************************************************
2673 *
2674 * SECTION: nsIWidget::SetCursor
2675 *
2676 * SetCursor and related utilities for manging cursor state.
2677 *
2678 **************************************************************/
2679
2680 // Set this component cursor
SetCursor(nsCursor aCursor)2681 void nsWindow::SetCursor(nsCursor aCursor) {
2682 // Only change cursor if it's changing
2683
2684 // XXX mCursor isn't always right. Scrollbars and others change it, too.
2685 // XXX If we want this optimization we need a better way to do it.
2686 // if (aCursor != mCursor) {
2687 HCURSOR newCursor = nullptr;
2688
2689 switch (aCursor) {
2690 case eCursor_select:
2691 newCursor = ::LoadCursor(nullptr, IDC_IBEAM);
2692 break;
2693
2694 case eCursor_wait:
2695 newCursor = ::LoadCursor(nullptr, IDC_WAIT);
2696 break;
2697
2698 case eCursor_hyperlink: {
2699 newCursor = ::LoadCursor(nullptr, IDC_HAND);
2700 break;
2701 }
2702
2703 case eCursor_standard:
2704 case eCursor_context_menu: // XXX See bug 258960.
2705 newCursor = ::LoadCursor(nullptr, IDC_ARROW);
2706 break;
2707
2708 case eCursor_n_resize:
2709 case eCursor_s_resize:
2710 newCursor = ::LoadCursor(nullptr, IDC_SIZENS);
2711 break;
2712
2713 case eCursor_w_resize:
2714 case eCursor_e_resize:
2715 newCursor = ::LoadCursor(nullptr, IDC_SIZEWE);
2716 break;
2717
2718 case eCursor_nw_resize:
2719 case eCursor_se_resize:
2720 newCursor = ::LoadCursor(nullptr, IDC_SIZENWSE);
2721 break;
2722
2723 case eCursor_ne_resize:
2724 case eCursor_sw_resize:
2725 newCursor = ::LoadCursor(nullptr, IDC_SIZENESW);
2726 break;
2727
2728 case eCursor_crosshair:
2729 newCursor = ::LoadCursor(nullptr, IDC_CROSS);
2730 break;
2731
2732 case eCursor_move:
2733 newCursor = ::LoadCursor(nullptr, IDC_SIZEALL);
2734 break;
2735
2736 case eCursor_help:
2737 newCursor = ::LoadCursor(nullptr, IDC_HELP);
2738 break;
2739
2740 case eCursor_copy: // CSS3
2741 newCursor =
2742 ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_COPY));
2743 break;
2744
2745 case eCursor_alias:
2746 newCursor =
2747 ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ALIAS));
2748 break;
2749
2750 case eCursor_cell:
2751 newCursor =
2752 ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_CELL));
2753 break;
2754
2755 case eCursor_grab:
2756 newCursor =
2757 ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_GRAB));
2758 break;
2759
2760 case eCursor_grabbing:
2761 newCursor =
2762 ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_GRABBING));
2763 break;
2764
2765 case eCursor_spinning:
2766 newCursor = ::LoadCursor(nullptr, IDC_APPSTARTING);
2767 break;
2768
2769 case eCursor_zoom_in:
2770 newCursor =
2771 ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ZOOMIN));
2772 break;
2773
2774 case eCursor_zoom_out:
2775 newCursor =
2776 ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ZOOMOUT));
2777 break;
2778
2779 case eCursor_not_allowed:
2780 case eCursor_no_drop:
2781 newCursor = ::LoadCursor(nullptr, IDC_NO);
2782 break;
2783
2784 case eCursor_col_resize:
2785 newCursor =
2786 ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_COLRESIZE));
2787 break;
2788
2789 case eCursor_row_resize:
2790 newCursor =
2791 ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ROWRESIZE));
2792 break;
2793
2794 case eCursor_vertical_text:
2795 newCursor = ::LoadCursor(nsToolkit::mDllInstance,
2796 MAKEINTRESOURCE(IDC_VERTICALTEXT));
2797 break;
2798
2799 case eCursor_all_scroll:
2800 // XXX not 100% appropriate perhaps
2801 newCursor = ::LoadCursor(nullptr, IDC_SIZEALL);
2802 break;
2803
2804 case eCursor_nesw_resize:
2805 newCursor = ::LoadCursor(nullptr, IDC_SIZENESW);
2806 break;
2807
2808 case eCursor_nwse_resize:
2809 newCursor = ::LoadCursor(nullptr, IDC_SIZENWSE);
2810 break;
2811
2812 case eCursor_ns_resize:
2813 newCursor = ::LoadCursor(nullptr, IDC_SIZENS);
2814 break;
2815
2816 case eCursor_ew_resize:
2817 newCursor = ::LoadCursor(nullptr, IDC_SIZEWE);
2818 break;
2819
2820 case eCursor_none:
2821 newCursor =
2822 ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_NONE));
2823 break;
2824
2825 default:
2826 NS_ERROR("Invalid cursor type");
2827 break;
2828 }
2829
2830 if (nullptr != newCursor) {
2831 mCursor = aCursor;
2832 HCURSOR oldCursor = ::SetCursor(newCursor);
2833
2834 if (sHCursor == oldCursor) {
2835 NS_IF_RELEASE(sCursorImgContainer);
2836 if (sHCursor != nullptr) ::DestroyIcon(sHCursor);
2837 sHCursor = nullptr;
2838 }
2839 }
2840 }
2841
2842 // Setting the actual cursor
SetCursor(imgIContainer * aCursor,uint32_t aHotspotX,uint32_t aHotspotY)2843 nsresult nsWindow::SetCursor(imgIContainer* aCursor, uint32_t aHotspotX,
2844 uint32_t aHotspotY) {
2845 if (sCursorImgContainer == aCursor && sHCursor) {
2846 ::SetCursor(sHCursor);
2847 return NS_OK;
2848 }
2849
2850 int32_t width;
2851 int32_t height;
2852
2853 nsresult rv;
2854 rv = aCursor->GetWidth(&width);
2855 NS_ENSURE_SUCCESS(rv, rv);
2856 rv = aCursor->GetHeight(&height);
2857 NS_ENSURE_SUCCESS(rv, rv);
2858
2859 // Reject cursors greater than 128 pixels in either direction, to prevent
2860 // spoofing.
2861 // XXX ideally we should rescale. Also, we could modify the API to
2862 // allow trusted content to set larger cursors.
2863 if (width > 128 || height > 128) return NS_ERROR_NOT_AVAILABLE;
2864
2865 HCURSOR cursor;
2866 double scale = GetDefaultScale().scale;
2867 IntSize size = RoundedToInt(Size(width * scale, height * scale));
2868 rv = nsWindowGfx::CreateIcon(aCursor, true, aHotspotX, aHotspotY, size,
2869 &cursor);
2870 NS_ENSURE_SUCCESS(rv, rv);
2871
2872 mCursor = eCursorInvalid;
2873 ::SetCursor(cursor);
2874
2875 NS_IF_RELEASE(sCursorImgContainer);
2876 sCursorImgContainer = aCursor;
2877 NS_ADDREF(sCursorImgContainer);
2878
2879 if (sHCursor != nullptr) ::DestroyIcon(sHCursor);
2880 sHCursor = cursor;
2881
2882 return NS_OK;
2883 }
2884
2885 /**************************************************************
2886 *
2887 * SECTION: nsIWidget::Get/SetTransparencyMode
2888 *
2889 * Manage the transparency mode of the window containing this
2890 * widget. Only works for popup and dialog windows when the
2891 * Desktop Window Manager compositor is not enabled.
2892 *
2893 **************************************************************/
2894
2895 #ifdef MOZ_XUL
GetTransparencyMode()2896 nsTransparencyMode nsWindow::GetTransparencyMode() {
2897 return GetTopLevelWindow(true)->GetWindowTranslucencyInner();
2898 }
2899
SetTransparencyMode(nsTransparencyMode aMode)2900 void nsWindow::SetTransparencyMode(nsTransparencyMode aMode) {
2901 nsWindow* window = GetTopLevelWindow(true);
2902 MOZ_ASSERT(window);
2903
2904 if (!window || window->DestroyCalled()) {
2905 return;
2906 }
2907
2908 if (nsWindowType::eWindowType_toplevel == window->mWindowType &&
2909 mTransparencyMode != aMode && !nsUXThemeData::CheckForCompositor()) {
2910 NS_WARNING("Cannot set transparency mode on top-level windows.");
2911 return;
2912 }
2913
2914 window->SetWindowTranslucencyInner(aMode);
2915 }
2916
UpdateOpaqueRegion(const LayoutDeviceIntRegion & aOpaqueRegion)2917 void nsWindow::UpdateOpaqueRegion(const LayoutDeviceIntRegion& aOpaqueRegion) {
2918 if (!HasGlass() || GetParent()) return;
2919
2920 // If there is no opaque region or hidechrome=true, set margins
2921 // to support a full sheet of glass. Comments in MSDN indicate
2922 // all values must be set to -1 to get a full sheet of glass.
2923 MARGINS margins = {-1, -1, -1, -1};
2924 if (!aOpaqueRegion.IsEmpty()) {
2925 LayoutDeviceIntRect pluginBounds;
2926 for (nsIWidget* child = GetFirstChild(); child;
2927 child = child->GetNextSibling()) {
2928 if (child->IsPlugin()) {
2929 // Collect the bounds of all plugins for GetLargestRectangle.
2930 LayoutDeviceIntRect childBounds = child->GetBounds();
2931 pluginBounds.UnionRect(pluginBounds, childBounds);
2932 }
2933 }
2934
2935 LayoutDeviceIntRect clientBounds = GetClientBounds();
2936
2937 // Find the largest rectangle and use that to calculate the inset. Our top
2938 // priority is to include the bounds of all plugins.
2939 LayoutDeviceIntRect largest =
2940 aOpaqueRegion.GetLargestRectangle(pluginBounds);
2941 margins.cxLeftWidth = largest.X();
2942 margins.cxRightWidth = clientBounds.Width() - largest.XMost();
2943 margins.cyBottomHeight = clientBounds.Height() - largest.YMost();
2944 if (mCustomNonClient) {
2945 // The minimum glass height must be the caption buttons height,
2946 // otherwise the buttons are drawn incorrectly.
2947 largest.MoveToY(std::max<uint32_t>(
2948 largest.Y(), nsUXThemeData::GetCommandButtonBoxMetrics().cy));
2949 }
2950 margins.cyTopHeight = largest.Y();
2951 }
2952
2953 // Only update glass area if there are changes
2954 if (memcmp(&mGlassMargins, &margins, sizeof mGlassMargins)) {
2955 mGlassMargins = margins;
2956 UpdateGlass();
2957 }
2958 }
2959
2960 /**************************************************************
2961 *
2962 * SECTION: nsIWidget::UpdateWindowDraggingRegion
2963 *
2964 * For setting the draggable titlebar region from CSS
2965 * with -moz-window-dragging: drag.
2966 *
2967 **************************************************************/
2968
UpdateWindowDraggingRegion(const LayoutDeviceIntRegion & aRegion)2969 void nsWindow::UpdateWindowDraggingRegion(
2970 const LayoutDeviceIntRegion& aRegion) {
2971 if (mDraggableRegion != aRegion) {
2972 mDraggableRegion = aRegion;
2973 }
2974 }
2975
UpdateGlass()2976 void nsWindow::UpdateGlass() {
2977 MARGINS margins = mGlassMargins;
2978
2979 // DWMNCRP_USEWINDOWSTYLE - The non-client rendering area is
2980 // rendered based on the window style.
2981 // DWMNCRP_ENABLED - The non-client area rendering is
2982 // enabled; the window style is ignored.
2983 DWMNCRENDERINGPOLICY policy = DWMNCRP_USEWINDOWSTYLE;
2984 switch (mTransparencyMode) {
2985 case eTransparencyBorderlessGlass:
2986 // Only adjust if there is some opaque rectangle
2987 if (margins.cxLeftWidth >= 0) {
2988 margins.cxLeftWidth += kGlassMarginAdjustment;
2989 margins.cyTopHeight += kGlassMarginAdjustment;
2990 margins.cxRightWidth += kGlassMarginAdjustment;
2991 margins.cyBottomHeight += kGlassMarginAdjustment;
2992 }
2993 // Fall through
2994 case eTransparencyGlass:
2995 policy = DWMNCRP_ENABLED;
2996 break;
2997 default:
2998 break;
2999 }
3000
3001 MOZ_LOG(gWindowsLog, LogLevel::Info,
3002 ("glass margins: left:%d top:%d right:%d bottom:%d\n",
3003 margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth,
3004 margins.cyBottomHeight));
3005
3006 // Extends the window frame behind the client area
3007 if (nsUXThemeData::CheckForCompositor()) {
3008 DwmExtendFrameIntoClientArea(mWnd, &margins);
3009 DwmSetWindowAttribute(mWnd, DWMWA_NCRENDERING_POLICY, &policy,
3010 sizeof policy);
3011 }
3012 }
3013 #endif
3014
3015 /**************************************************************
3016 *
3017 * SECTION: nsIWidget::HideWindowChrome
3018 *
3019 * Show or hide window chrome.
3020 *
3021 **************************************************************/
3022
HideWindowChrome(bool aShouldHide)3023 void nsWindow::HideWindowChrome(bool aShouldHide) {
3024 HWND hwnd = WinUtils::GetTopLevelHWND(mWnd, true);
3025 if (!WinUtils::GetNSWindowPtr(hwnd)) {
3026 NS_WARNING("Trying to hide window decorations in an embedded context");
3027 return;
3028 }
3029
3030 if (mHideChrome == aShouldHide) return;
3031
3032 DWORD_PTR style, exStyle;
3033 mHideChrome = aShouldHide;
3034 if (aShouldHide) {
3035 DWORD_PTR tempStyle = ::GetWindowLongPtrW(hwnd, GWL_STYLE);
3036 DWORD_PTR tempExStyle = ::GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
3037
3038 style = tempStyle & ~(WS_CAPTION | WS_THICKFRAME);
3039 exStyle = tempExStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE |
3040 WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
3041
3042 mOldStyle = tempStyle;
3043 mOldExStyle = tempExStyle;
3044 } else {
3045 if (!mOldStyle || !mOldExStyle) {
3046 mOldStyle = ::GetWindowLongPtrW(hwnd, GWL_STYLE);
3047 mOldExStyle = ::GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
3048 }
3049
3050 style = mOldStyle;
3051 exStyle = mOldExStyle;
3052 if (mFutureMarginsToUse) {
3053 SetNonClientMargins(mFutureMarginsOnceChromeShows);
3054 }
3055 }
3056
3057 VERIFY_WINDOW_STYLE(style);
3058 ::SetWindowLongPtrW(hwnd, GWL_STYLE, style);
3059 ::SetWindowLongPtrW(hwnd, GWL_EXSTYLE, exStyle);
3060 }
3061
3062 /**************************************************************
3063 *
3064 * SECTION: nsWindow::Invalidate
3065 *
3066 * Invalidate an area of the client for painting.
3067 *
3068 **************************************************************/
3069
3070 // Invalidate this component visible area
Invalidate(bool aEraseBackground,bool aUpdateNCArea,bool aIncludeChildren)3071 void nsWindow::Invalidate(bool aEraseBackground, bool aUpdateNCArea,
3072 bool aIncludeChildren) {
3073 if (!mWnd) {
3074 return;
3075 }
3076
3077 #ifdef WIDGET_DEBUG_OUTPUT
3078 debug_DumpInvalidate(stdout, this, nullptr, "noname", (int32_t)mWnd);
3079 #endif // WIDGET_DEBUG_OUTPUT
3080
3081 DWORD flags = RDW_INVALIDATE;
3082 if (aEraseBackground) {
3083 flags |= RDW_ERASE;
3084 }
3085 if (aUpdateNCArea) {
3086 flags |= RDW_FRAME;
3087 }
3088 if (aIncludeChildren) {
3089 flags |= RDW_ALLCHILDREN;
3090 }
3091
3092 VERIFY(::RedrawWindow(mWnd, nullptr, nullptr, flags));
3093 }
3094
3095 // Invalidate this component visible area
Invalidate(const LayoutDeviceIntRect & aRect)3096 void nsWindow::Invalidate(const LayoutDeviceIntRect& aRect) {
3097 if (mWnd) {
3098 #ifdef WIDGET_DEBUG_OUTPUT
3099 debug_DumpInvalidate(stdout, this, &aRect, "noname", (int32_t)mWnd);
3100 #endif // WIDGET_DEBUG_OUTPUT
3101
3102 RECT rect;
3103
3104 rect.left = aRect.X();
3105 rect.top = aRect.Y();
3106 rect.right = aRect.XMost();
3107 rect.bottom = aRect.YMost();
3108
3109 VERIFY(::InvalidateRect(mWnd, &rect, FALSE));
3110 }
3111 }
3112
FullscreenTransitionWindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)3113 static LRESULT CALLBACK FullscreenTransitionWindowProc(HWND hWnd, UINT uMsg,
3114 WPARAM wParam,
3115 LPARAM lParam) {
3116 switch (uMsg) {
3117 case WM_FULLSCREEN_TRANSITION_BEFORE:
3118 case WM_FULLSCREEN_TRANSITION_AFTER: {
3119 DWORD duration = (DWORD)lParam;
3120 DWORD flags = AW_BLEND;
3121 if (uMsg == WM_FULLSCREEN_TRANSITION_AFTER) {
3122 flags |= AW_HIDE;
3123 }
3124 ::AnimateWindow(hWnd, duration, flags);
3125 // The message sender should have added ref for us.
3126 NS_DispatchToMainThread(
3127 already_AddRefed<nsIRunnable>((nsIRunnable*)wParam));
3128 break;
3129 }
3130 case WM_DESTROY:
3131 ::PostQuitMessage(0);
3132 break;
3133 default:
3134 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
3135 }
3136 return 0;
3137 }
3138
3139 struct FullscreenTransitionInitData {
3140 nsIntRect mBounds;
3141 HANDLE mSemaphore;
3142 HANDLE mThread;
3143 HWND mWnd;
3144
FullscreenTransitionInitDataFullscreenTransitionInitData3145 FullscreenTransitionInitData()
3146 : mSemaphore(nullptr), mThread(nullptr), mWnd(nullptr) {}
3147
~FullscreenTransitionInitDataFullscreenTransitionInitData3148 ~FullscreenTransitionInitData() {
3149 if (mSemaphore) {
3150 ::CloseHandle(mSemaphore);
3151 }
3152 if (mThread) {
3153 ::CloseHandle(mThread);
3154 }
3155 }
3156 };
3157
FullscreenTransitionThreadProc(LPVOID lpParam)3158 static DWORD WINAPI FullscreenTransitionThreadProc(LPVOID lpParam) {
3159 // Initialize window class
3160 static bool sInitialized = false;
3161 if (!sInitialized) {
3162 WNDCLASSW wc = {};
3163 wc.lpfnWndProc = ::FullscreenTransitionWindowProc;
3164 wc.hInstance = nsToolkit::mDllInstance;
3165 wc.hbrBackground = ::CreateSolidBrush(RGB(0, 0, 0));
3166 wc.lpszClassName = kClassNameTransition;
3167 ::RegisterClassW(&wc);
3168 sInitialized = true;
3169 }
3170
3171 auto data = static_cast<FullscreenTransitionInitData*>(lpParam);
3172 HWND wnd = ::CreateWindowW(kClassNameTransition, L"", 0, 0, 0, 0, 0, nullptr,
3173 nullptr, nsToolkit::mDllInstance, nullptr);
3174 if (!wnd) {
3175 ::ReleaseSemaphore(data->mSemaphore, 1, nullptr);
3176 return 0;
3177 }
3178
3179 // Since AnimateWindow blocks the thread of the transition window,
3180 // we need to hide the cursor for that window, otherwise the system
3181 // would show the busy pointer to the user.
3182 ::ShowCursor(false);
3183 ::SetWindowLongW(wnd, GWL_STYLE, 0);
3184 ::SetWindowLongW(
3185 wnd, GWL_EXSTYLE,
3186 WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE);
3187 ::SetWindowPos(wnd, HWND_TOPMOST, data->mBounds.X(), data->mBounds.Y(),
3188 data->mBounds.Width(), data->mBounds.Height(), 0);
3189 data->mWnd = wnd;
3190 ::ReleaseSemaphore(data->mSemaphore, 1, nullptr);
3191 // The initialization data may no longer be valid
3192 // after we release the semaphore.
3193 data = nullptr;
3194
3195 MSG msg;
3196 while (::GetMessageW(&msg, nullptr, 0, 0)) {
3197 ::TranslateMessage(&msg);
3198 ::DispatchMessage(&msg);
3199 }
3200 ::ShowCursor(true);
3201 ::DestroyWindow(wnd);
3202 return 0;
3203 }
3204
3205 class FullscreenTransitionData final : public nsISupports {
3206 public:
3207 NS_DECL_ISUPPORTS
3208
FullscreenTransitionData(HWND aWnd)3209 explicit FullscreenTransitionData(HWND aWnd) : mWnd(aWnd) {
3210 MOZ_ASSERT(NS_IsMainThread(),
3211 "FullscreenTransitionData "
3212 "should be constructed in the main thread");
3213 }
3214
3215 const HWND mWnd;
3216
3217 private:
~FullscreenTransitionData()3218 ~FullscreenTransitionData() {
3219 MOZ_ASSERT(NS_IsMainThread(),
3220 "FullscreenTransitionData "
3221 "should be deconstructed in the main thread");
3222 ::PostMessageW(mWnd, WM_DESTROY, 0, 0);
3223 }
3224 };
3225
NS_IMPL_ISUPPORTS0(FullscreenTransitionData)3226 NS_IMPL_ISUPPORTS0(FullscreenTransitionData)
3227
3228 /* virtual */ bool nsWindow::PrepareForFullscreenTransition(
3229 nsISupports** aData) {
3230 // We don't support fullscreen transition when composition is not
3231 // enabled, which could make the transition broken and annoying.
3232 // See bug 1184201.
3233 if (!nsUXThemeData::CheckForCompositor()) {
3234 return false;
3235 }
3236
3237 FullscreenTransitionInitData initData;
3238 nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
3239 int32_t x, y, width, height;
3240 screen->GetRectDisplayPix(&x, &y, &width, &height);
3241 MOZ_ASSERT(BoundsUseDesktopPixels(),
3242 "Should only be called on top-level window");
3243 double scale = GetDesktopToDeviceScale().scale; // XXX or GetDefaultScale() ?
3244 initData.mBounds.SetRect(NSToIntRound(x * scale), NSToIntRound(y * scale),
3245 NSToIntRound(width * scale),
3246 NSToIntRound(height * scale));
3247
3248 // Create a semaphore for synchronizing the window handle which will
3249 // be created by the transition thread and used by the main thread for
3250 // posting the transition messages.
3251 initData.mSemaphore = ::CreateSemaphore(nullptr, 0, 1, nullptr);
3252 if (initData.mSemaphore) {
3253 initData.mThread = ::CreateThread(
3254 nullptr, 0, FullscreenTransitionThreadProc, &initData, 0, nullptr);
3255 if (initData.mThread) {
3256 ::WaitForSingleObject(initData.mSemaphore, INFINITE);
3257 }
3258 }
3259 if (!initData.mWnd) {
3260 return false;
3261 }
3262
3263 mTransitionWnd = initData.mWnd;
3264
3265 auto data = new FullscreenTransitionData(initData.mWnd);
3266 *aData = data;
3267 NS_ADDREF(data);
3268 return true;
3269 }
3270
PerformFullscreenTransition(FullscreenTransitionStage aStage,uint16_t aDuration,nsISupports * aData,nsIRunnable * aCallback)3271 /* virtual */ void nsWindow::PerformFullscreenTransition(
3272 FullscreenTransitionStage aStage, uint16_t aDuration, nsISupports* aData,
3273 nsIRunnable* aCallback) {
3274 auto data = static_cast<FullscreenTransitionData*>(aData);
3275 nsCOMPtr<nsIRunnable> callback = aCallback;
3276 UINT msg = aStage == eBeforeFullscreenToggle ? WM_FULLSCREEN_TRANSITION_BEFORE
3277 : WM_FULLSCREEN_TRANSITION_AFTER;
3278 WPARAM wparam = (WPARAM)callback.forget().take();
3279 ::PostMessage(data->mWnd, msg, wparam, (LPARAM)aDuration);
3280 }
3281
CleanupFullscreenTransition()3282 /* virtual */ void nsWindow::CleanupFullscreenTransition() {
3283 MOZ_ASSERT(NS_IsMainThread(),
3284 "CleanupFullscreenTransition "
3285 "should only run on the main thread");
3286
3287 mTransitionWnd = nullptr;
3288 }
3289
MakeFullScreen(bool aFullScreen,nsIScreen * aTargetScreen)3290 nsresult nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen) {
3291 // taskbarInfo will be nullptr pre Windows 7 until Bug 680227 is resolved.
3292 nsCOMPtr<nsIWinTaskbar> taskbarInfo = do_GetService(NS_TASKBAR_CONTRACTID);
3293
3294 mFullscreenMode = aFullScreen;
3295 if (aFullScreen) {
3296 if (mSizeMode == nsSizeMode_Fullscreen) return NS_OK;
3297 mOldSizeMode = mSizeMode;
3298 SetSizeMode(nsSizeMode_Fullscreen);
3299
3300 // Notify the taskbar that we will be entering full screen mode.
3301 if (taskbarInfo) {
3302 taskbarInfo->PrepareFullScreenHWND(mWnd, TRUE);
3303 }
3304 } else {
3305 if (mSizeMode != nsSizeMode_Fullscreen) return NS_OK;
3306 SetSizeMode(mOldSizeMode);
3307 }
3308
3309 // If we are going fullscreen, the window size continues to change
3310 // and the window will be reflow again then.
3311 UpdateNonClientMargins(mSizeMode, /* Reflow */ !aFullScreen);
3312
3313 // Will call hide chrome, reposition window. Note this will
3314 // also cache dimensions for restoration, so it should only
3315 // be called once per fullscreen request.
3316 nsBaseWidget::InfallibleMakeFullScreen(aFullScreen, aTargetScreen);
3317
3318 if (mIsVisible && !aFullScreen && mOldSizeMode == nsSizeMode_Normal) {
3319 // Ensure the window exiting fullscreen get activated. Window
3320 // activation might be bypassed in SetSizeMode.
3321 DispatchFocusToTopLevelWindow(true);
3322 }
3323
3324 // Notify the taskbar that we have exited full screen mode.
3325 if (!aFullScreen && taskbarInfo) {
3326 taskbarInfo->PrepareFullScreenHWND(mWnd, FALSE);
3327 }
3328
3329 if (mWidgetListener) {
3330 mWidgetListener->SizeModeChanged(mSizeMode);
3331 mWidgetListener->FullscreenChanged(aFullScreen);
3332 }
3333
3334 return NS_OK;
3335 }
3336
3337 /**************************************************************
3338 *
3339 * SECTION: Native data storage
3340 *
3341 * nsIWidget::GetNativeData
3342 * nsIWidget::FreeNativeData
3343 *
3344 * Set or clear native data based on a constant.
3345 *
3346 **************************************************************/
3347
3348 // Return some native data according to aDataType
GetNativeData(uint32_t aDataType)3349 void* nsWindow::GetNativeData(uint32_t aDataType) {
3350 switch (aDataType) {
3351 case NS_NATIVE_TMP_WINDOW:
3352 return (void*)::CreateWindowExW(
3353 mIsRTL ? WS_EX_LAYOUTRTL : 0, GetWindowClass(), L"", WS_CHILD,
3354 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, mWnd,
3355 nullptr, nsToolkit::mDllInstance, nullptr);
3356 case NS_NATIVE_PLUGIN_ID:
3357 case NS_NATIVE_PLUGIN_PORT:
3358 case NS_NATIVE_WIDGET:
3359 case NS_NATIVE_WINDOW:
3360 return (void*)mWnd;
3361 case NS_NATIVE_SHAREABLE_WINDOW:
3362 return (void*)WinUtils::GetTopLevelHWND(mWnd);
3363 case NS_NATIVE_GRAPHIC:
3364 MOZ_ASSERT_UNREACHABLE("Not supported on Windows:");
3365 return nullptr;
3366 case NS_RAW_NATIVE_IME_CONTEXT: {
3367 void* pseudoIMEContext = GetPseudoIMEContext();
3368 if (pseudoIMEContext) {
3369 return pseudoIMEContext;
3370 }
3371 MOZ_FALLTHROUGH;
3372 }
3373 case NS_NATIVE_TSF_THREAD_MGR:
3374 case NS_NATIVE_TSF_CATEGORY_MGR:
3375 case NS_NATIVE_TSF_DISPLAY_ATTR_MGR:
3376 return IMEHandler::GetNativeData(this, aDataType);
3377
3378 default:
3379 break;
3380 }
3381
3382 return nullptr;
3383 }
3384
SetChildStyleAndParent(HWND aChildWindow,HWND aParentWindow)3385 static void SetChildStyleAndParent(HWND aChildWindow, HWND aParentWindow) {
3386 // Make sure the window is styled to be a child window.
3387 LONG_PTR style = GetWindowLongPtr(aChildWindow, GWL_STYLE);
3388 style |= WS_CHILD;
3389 style &= ~WS_POPUP;
3390 SetWindowLongPtr(aChildWindow, GWL_STYLE, style);
3391
3392 // Do the reparenting. Note that this call will probably cause a sync native
3393 // message to the process that owns the child window.
3394 ::SetParent(aChildWindow, aParentWindow);
3395 }
3396
SetNativeData(uint32_t aDataType,uintptr_t aVal)3397 void nsWindow::SetNativeData(uint32_t aDataType, uintptr_t aVal) {
3398 switch (aDataType) {
3399 case NS_NATIVE_CHILD_WINDOW:
3400 case NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW: {
3401 HWND childHwnd = reinterpret_cast<HWND>(aVal);
3402 DWORD childProc = 0;
3403 GetWindowThreadProcessId(childHwnd, &childProc);
3404 if (!PluginProcessParent::IsPluginProcessId(
3405 static_cast<base::ProcessId>(childProc))) {
3406 MOZ_ASSERT_UNREACHABLE(
3407 "SetNativeData window origin was not a plugin process.");
3408 break;
3409 }
3410 HWND parentHwnd = aDataType == NS_NATIVE_CHILD_WINDOW
3411 ? mWnd
3412 : WinUtils::GetTopLevelHWND(mWnd);
3413 SetChildStyleAndParent(childHwnd, parentHwnd);
3414 break;
3415 }
3416 default:
3417 NS_ERROR("SetNativeData called with unsupported data type.");
3418 }
3419 }
3420
3421 // Free some native data according to aDataType
FreeNativeData(void * data,uint32_t aDataType)3422 void nsWindow::FreeNativeData(void* data, uint32_t aDataType) {
3423 switch (aDataType) {
3424 case NS_NATIVE_GRAPHIC:
3425 case NS_NATIVE_WIDGET:
3426 case NS_NATIVE_WINDOW:
3427 case NS_NATIVE_PLUGIN_PORT:
3428 break;
3429 default:
3430 break;
3431 }
3432 }
3433
3434 /**************************************************************
3435 *
3436 * SECTION: nsIWidget::SetTitle
3437 *
3438 * Set the main windows title text.
3439 *
3440 **************************************************************/
3441
SetTitle(const nsAString & aTitle)3442 nsresult nsWindow::SetTitle(const nsAString& aTitle) {
3443 const nsString& strTitle = PromiseFlatString(aTitle);
3444 AutoRestore<bool> sendingText(mSendingSetText);
3445 mSendingSetText = true;
3446 ::SendMessageW(mWnd, WM_SETTEXT, (WPARAM)0, (LPARAM)(LPCWSTR)strTitle.get());
3447 return NS_OK;
3448 }
3449
3450 /**************************************************************
3451 *
3452 * SECTION: nsIWidget::SetIcon
3453 *
3454 * Set the main windows icon.
3455 *
3456 **************************************************************/
3457
SetIcon(const nsAString & aIconSpec)3458 void nsWindow::SetIcon(const nsAString& aIconSpec) {
3459 // Assume the given string is a local identifier for an icon file.
3460
3461 nsCOMPtr<nsIFile> iconFile;
3462 ResolveIconName(aIconSpec, NS_LITERAL_STRING(".ico"),
3463 getter_AddRefs(iconFile));
3464 if (!iconFile) return;
3465
3466 nsAutoString iconPath;
3467 iconFile->GetPath(iconPath);
3468
3469 // XXX this should use MZLU (see bug 239279)
3470
3471 ::SetLastError(0);
3472
3473 HICON bigIcon =
3474 (HICON)::LoadImageW(nullptr, (LPCWSTR)iconPath.get(), IMAGE_ICON,
3475 ::GetSystemMetrics(SM_CXICON),
3476 ::GetSystemMetrics(SM_CYICON), LR_LOADFROMFILE);
3477 HICON smallIcon =
3478 (HICON)::LoadImageW(nullptr, (LPCWSTR)iconPath.get(), IMAGE_ICON,
3479 ::GetSystemMetrics(SM_CXSMICON),
3480 ::GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE);
3481
3482 if (bigIcon) {
3483 HICON icon = (HICON)::SendMessageW(mWnd, WM_SETICON, (WPARAM)ICON_BIG,
3484 (LPARAM)bigIcon);
3485 if (icon) ::DestroyIcon(icon);
3486 mIconBig = bigIcon;
3487 }
3488 #ifdef DEBUG_SetIcon
3489 else {
3490 NS_LossyConvertUTF16toASCII cPath(iconPath);
3491 MOZ_LOG(gWindowsLog, LogLevel::Info,
3492 ("\nIcon load error; icon=%s, rc=0x%08X\n\n", cPath.get(),
3493 ::GetLastError()));
3494 }
3495 #endif
3496 if (smallIcon) {
3497 HICON icon = (HICON)::SendMessageW(mWnd, WM_SETICON, (WPARAM)ICON_SMALL,
3498 (LPARAM)smallIcon);
3499 if (icon) ::DestroyIcon(icon);
3500 mIconSmall = smallIcon;
3501 }
3502 #ifdef DEBUG_SetIcon
3503 else {
3504 NS_LossyConvertUTF16toASCII cPath(iconPath);
3505 MOZ_LOG(gWindowsLog, LogLevel::Info,
3506 ("\nSmall icon load error; icon=%s, rc=0x%08X\n\n", cPath.get(),
3507 ::GetLastError()));
3508 }
3509 #endif
3510 }
3511
3512 /**************************************************************
3513 *
3514 * SECTION: nsIWidget::WidgetToScreenOffset
3515 *
3516 * Return this widget's origin in screen coordinates.
3517 *
3518 **************************************************************/
3519
WidgetToScreenOffset()3520 LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() {
3521 POINT point;
3522 point.x = 0;
3523 point.y = 0;
3524 ::ClientToScreen(mWnd, &point);
3525 return LayoutDeviceIntPoint(point.x, point.y);
3526 }
3527
ClientToWindowSize(const LayoutDeviceIntSize & aClientSize)3528 LayoutDeviceIntSize nsWindow::ClientToWindowSize(
3529 const LayoutDeviceIntSize& aClientSize) {
3530 if (mWindowType == eWindowType_popup && !IsPopupWithTitleBar())
3531 return aClientSize;
3532
3533 // just use (200, 200) as the position
3534 RECT r;
3535 r.left = 200;
3536 r.top = 200;
3537 r.right = 200 + aClientSize.width;
3538 r.bottom = 200 + aClientSize.height;
3539 ::AdjustWindowRectEx(&r, WindowStyle(), false, WindowExStyle());
3540
3541 return LayoutDeviceIntSize(r.right - r.left, r.bottom - r.top);
3542 }
3543
3544 /**************************************************************
3545 *
3546 * SECTION: nsIWidget::EnableDragDrop
3547 *
3548 * Enables/Disables drag and drop of files on this widget.
3549 *
3550 **************************************************************/
3551
EnableDragDrop(bool aEnable)3552 void nsWindow::EnableDragDrop(bool aEnable) {
3553 if (!mWnd) {
3554 // Return early if the window already closed
3555 return;
3556 }
3557
3558 if (aEnable) {
3559 if (!mNativeDragTarget) {
3560 mNativeDragTarget = new nsNativeDragTarget(this);
3561 mNativeDragTarget->AddRef();
3562 if (SUCCEEDED(::CoLockObjectExternal((LPUNKNOWN)mNativeDragTarget, TRUE,
3563 FALSE))) {
3564 ::RegisterDragDrop(mWnd, (LPDROPTARGET)mNativeDragTarget);
3565 }
3566 }
3567 } else {
3568 if (mWnd && mNativeDragTarget) {
3569 ::RevokeDragDrop(mWnd);
3570 ::CoLockObjectExternal((LPUNKNOWN)mNativeDragTarget, FALSE, TRUE);
3571 mNativeDragTarget->DragCancel();
3572 NS_RELEASE(mNativeDragTarget);
3573 }
3574 }
3575 }
3576
3577 /**************************************************************
3578 *
3579 * SECTION: nsIWidget::CaptureMouse
3580 *
3581 * Enables/Disables system mouse capture.
3582 *
3583 **************************************************************/
3584
CaptureMouse(bool aCapture)3585 void nsWindow::CaptureMouse(bool aCapture) {
3586 TRACKMOUSEEVENT mTrack;
3587 mTrack.cbSize = sizeof(TRACKMOUSEEVENT);
3588 mTrack.dwHoverTime = 0;
3589 mTrack.hwndTrack = mWnd;
3590 if (aCapture) {
3591 mTrack.dwFlags = TME_CANCEL | TME_LEAVE;
3592 ::SetCapture(mWnd);
3593 } else {
3594 mTrack.dwFlags = TME_LEAVE;
3595 ::ReleaseCapture();
3596 }
3597 sIsInMouseCapture = aCapture;
3598 TrackMouseEvent(&mTrack);
3599 }
3600
3601 /**************************************************************
3602 *
3603 * SECTION: nsIWidget::CaptureRollupEvents
3604 *
3605 * Dealing with event rollup on destroy for popups. Enables &
3606 * Disables system capture of any and all events that would
3607 * cause a dropdown to be rolled up.
3608 *
3609 **************************************************************/
3610
CaptureRollupEvents(nsIRollupListener * aListener,bool aDoCapture)3611 void nsWindow::CaptureRollupEvents(nsIRollupListener* aListener,
3612 bool aDoCapture) {
3613 if (aDoCapture) {
3614 gRollupListener = aListener;
3615 if (!sMsgFilterHook && !sCallProcHook && !sCallMouseHook) {
3616 RegisterSpecialDropdownHooks();
3617 }
3618 sProcessHook = true;
3619 } else {
3620 gRollupListener = nullptr;
3621 sProcessHook = false;
3622 UnregisterSpecialDropdownHooks();
3623 }
3624 }
3625
3626 /**************************************************************
3627 *
3628 * SECTION: nsIWidget::GetAttention
3629 *
3630 * Bring this window to the user's attention.
3631 *
3632 **************************************************************/
3633
3634 // Draw user's attention to this window until it comes to foreground.
GetAttention(int32_t aCycleCount)3635 nsresult nsWindow::GetAttention(int32_t aCycleCount) {
3636 // Got window?
3637 if (!mWnd) return NS_ERROR_NOT_INITIALIZED;
3638
3639 HWND flashWnd = WinUtils::GetTopLevelHWND(mWnd, false, false);
3640 HWND fgWnd = ::GetForegroundWindow();
3641 // Don't flash if the flash count is 0 or if the foreground window is our
3642 // window handle or that of our owned-most window.
3643 if (aCycleCount == 0 || flashWnd == fgWnd ||
3644 flashWnd == WinUtils::GetTopLevelHWND(fgWnd, false, false)) {
3645 return NS_OK;
3646 }
3647
3648 DWORD defaultCycleCount = 0;
3649 ::SystemParametersInfo(SPI_GETFOREGROUNDFLASHCOUNT, 0, &defaultCycleCount, 0);
3650
3651 FLASHWINFO flashInfo = {sizeof(FLASHWINFO), flashWnd, FLASHW_ALL,
3652 aCycleCount > 0 ? aCycleCount : defaultCycleCount, 0};
3653 ::FlashWindowEx(&flashInfo);
3654
3655 return NS_OK;
3656 }
3657
StopFlashing()3658 void nsWindow::StopFlashing() {
3659 HWND flashWnd = mWnd;
3660 while (HWND ownerWnd = ::GetWindow(flashWnd, GW_OWNER)) {
3661 flashWnd = ownerWnd;
3662 }
3663
3664 FLASHWINFO flashInfo = {sizeof(FLASHWINFO), flashWnd, FLASHW_STOP, 0, 0};
3665 ::FlashWindowEx(&flashInfo);
3666 }
3667
3668 /**************************************************************
3669 *
3670 * SECTION: nsIWidget::HasPendingInputEvent
3671 *
3672 * Ask whether there user input events pending. All input events are
3673 * included, including those not targeted at this nsIwidget instance.
3674 *
3675 **************************************************************/
3676
HasPendingInputEvent()3677 bool nsWindow::HasPendingInputEvent() {
3678 // If there is pending input or the user is currently
3679 // moving the window then return true.
3680 // Note: When the user is moving the window WIN32 spins
3681 // a separate event loop and input events are not
3682 // reported to the application.
3683 if (HIWORD(GetQueueStatus(QS_INPUT))) return true;
3684 GUITHREADINFO guiInfo;
3685 guiInfo.cbSize = sizeof(GUITHREADINFO);
3686 if (!GetGUIThreadInfo(GetCurrentThreadId(), &guiInfo)) return false;
3687 return GUI_INMOVESIZE == (guiInfo.flags & GUI_INMOVESIZE);
3688 }
3689
3690 /**************************************************************
3691 *
3692 * SECTION: nsIWidget::GetLayerManager
3693 *
3694 * Get the layer manager associated with this widget.
3695 *
3696 **************************************************************/
3697
GetLayerManager(PLayerTransactionChild * aShadowManager,LayersBackend aBackendHint,LayerManagerPersistence aPersistence)3698 LayerManager* nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
3699 LayersBackend aBackendHint,
3700 LayerManagerPersistence aPersistence) {
3701 RECT windowRect;
3702 ::GetClientRect(mWnd, &windowRect);
3703
3704 // Try OMTC first.
3705 if (!mLayerManager && ShouldUseOffMainThreadCompositing()) {
3706 gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
3707
3708 // e10s uses the parameter to pass in the shadow manager from the TabChild
3709 // so we don't expect to see it there since this doesn't support e10s.
3710 NS_ASSERTION(aShadowManager == nullptr,
3711 "Async Compositor not supported with e10s");
3712 CreateCompositor();
3713 }
3714
3715 if (!mLayerManager) {
3716 MOZ_ASSERT(!mCompositorSession && !mCompositorBridgeChild);
3717 MOZ_ASSERT(!mCompositorWidgetDelegate);
3718
3719 // Ensure we have a widget proxy even if we're not using the compositor,
3720 // since all our transparent window handling lives there.
3721 WinCompositorWidgetInitData initData(
3722 reinterpret_cast<uintptr_t>(mWnd),
3723 reinterpret_cast<uintptr_t>(static_cast<nsIWidget*>(this)),
3724 mTransparencyMode);
3725 // If we're not using the compositor, the options don't actually matter.
3726 CompositorOptions options(false, false);
3727 mBasicLayersSurface =
3728 new InProcessWinCompositorWidget(initData, options, this);
3729 mCompositorWidgetDelegate = mBasicLayersSurface;
3730 mLayerManager = CreateBasicLayerManager();
3731 }
3732
3733 NS_ASSERTION(mLayerManager, "Couldn't provide a valid layer manager.");
3734
3735 return mLayerManager;
3736 }
3737
3738 /**************************************************************
3739 *
3740 * SECTION: nsBaseWidget::SetCompositorWidgetDelegate
3741 *
3742 * Called to connect the nsWindow to the delegate providing
3743 * platform compositing API access.
3744 *
3745 **************************************************************/
3746
SetCompositorWidgetDelegate(CompositorWidgetDelegate * delegate)3747 void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) {
3748 if (delegate) {
3749 mCompositorWidgetDelegate = delegate->AsPlatformSpecificDelegate();
3750 MOZ_ASSERT(mCompositorWidgetDelegate,
3751 "nsWindow::SetCompositorWidgetDelegate called with a "
3752 "non-PlatformCompositorWidgetDelegate");
3753 } else {
3754 mCompositorWidgetDelegate = nullptr;
3755 }
3756 }
3757
3758 /**************************************************************
3759 *
3760 * SECTION: nsIWidget::OnDefaultButtonLoaded
3761 *
3762 * Called after the dialog is loaded and it has a default button.
3763 *
3764 **************************************************************/
3765
OnDefaultButtonLoaded(const LayoutDeviceIntRect & aButtonRect)3766 nsresult nsWindow::OnDefaultButtonLoaded(
3767 const LayoutDeviceIntRect& aButtonRect) {
3768 if (aButtonRect.IsEmpty()) return NS_OK;
3769
3770 // Don't snap when we are not active.
3771 HWND activeWnd = ::GetActiveWindow();
3772 if (activeWnd != ::GetForegroundWindow() ||
3773 WinUtils::GetTopLevelHWND(mWnd, true) !=
3774 WinUtils::GetTopLevelHWND(activeWnd, true)) {
3775 return NS_OK;
3776 }
3777
3778 bool isAlwaysSnapCursor =
3779 Preferences::GetBool("ui.cursor_snapping.always_enabled", false);
3780
3781 if (!isAlwaysSnapCursor) {
3782 BOOL snapDefaultButton;
3783 if (!::SystemParametersInfo(SPI_GETSNAPTODEFBUTTON, 0, &snapDefaultButton,
3784 0) ||
3785 !snapDefaultButton)
3786 return NS_OK;
3787 }
3788
3789 LayoutDeviceIntRect widgetRect = GetScreenBounds();
3790 LayoutDeviceIntRect buttonRect(aButtonRect + widgetRect.TopLeft());
3791
3792 LayoutDeviceIntPoint centerOfButton(buttonRect.X() + buttonRect.Width() / 2,
3793 buttonRect.Y() + buttonRect.Height() / 2);
3794 // The center of the button can be outside of the widget.
3795 // E.g., it could be hidden by scrolling.
3796 if (!widgetRect.Contains(centerOfButton)) {
3797 return NS_OK;
3798 }
3799
3800 if (!::SetCursorPos(centerOfButton.x, centerOfButton.y)) {
3801 NS_ERROR("SetCursorPos failed");
3802 return NS_ERROR_FAILURE;
3803 }
3804 return NS_OK;
3805 }
3806
UpdateThemeGeometries(const nsTArray<ThemeGeometry> & aThemeGeometries)3807 void nsWindow::UpdateThemeGeometries(
3808 const nsTArray<ThemeGeometry>& aThemeGeometries) {
3809 RefPtr<LayerManager> layerManager = GetLayerManager();
3810 if (!layerManager) {
3811 return;
3812 }
3813
3814 nsIntRegion clearRegion;
3815 if (!HasGlass() || !nsUXThemeData::CheckForCompositor()) {
3816 // Make sure and clear old regions we've set previously. Note HasGlass can
3817 // be false for glass desktops if the window we are rendering to doesn't
3818 // make use of glass (e.g. fullscreen browsing).
3819 layerManager->SetRegionToClear(clearRegion);
3820 return;
3821 }
3822
3823 // On Win10, force show the top border:
3824 if (IsWin10OrLater() && mCustomNonClient && mSizeMode == nsSizeMode_Normal) {
3825 RECT rect;
3826 ::GetWindowRect(mWnd, &rect);
3827 // We want 1 pixel of border for every whole 100% of scaling
3828 double borderSize = std::min(1, RoundDown(GetDesktopToDeviceScale().scale));
3829 clearRegion.Or(clearRegion, gfx::IntRect::Truncate(
3830 0, 0, rect.right - rect.left, borderSize));
3831 }
3832
3833 mWindowButtonsRect = Nothing();
3834
3835 if (!IsWin10OrLater()) {
3836 for (size_t i = 0; i < aThemeGeometries.Length(); i++) {
3837 if (aThemeGeometries[i].mType ==
3838 nsNativeThemeWin::eThemeGeometryTypeWindowButtons) {
3839 LayoutDeviceIntRect bounds = aThemeGeometries[i].mRect;
3840 // Extend the bounds by one pixel to the right, because that's how much
3841 // the actual window button shape extends past the client area of the
3842 // window (and overlaps the right window frame).
3843 bounds.SetWidth(bounds.Width() + 1);
3844 if (!mWindowButtonsRect) {
3845 mWindowButtonsRect = Some(bounds);
3846 }
3847 clearRegion.Or(clearRegion, gfx::IntRect::Truncate(
3848 bounds.X(), bounds.Y(), bounds.Width(),
3849 bounds.Height() - 2.0));
3850 clearRegion.Or(clearRegion, gfx::IntRect::Truncate(
3851 bounds.X() + 1.0, bounds.YMost() - 2.0,
3852 bounds.Width() - 2.0, 1.0));
3853 clearRegion.Or(clearRegion, gfx::IntRect::Truncate(
3854 bounds.X() + 2.0, bounds.YMost() - 1.0,
3855 bounds.Width() - 4.0, 1.0));
3856 }
3857 }
3858 }
3859
3860 layerManager->SetRegionToClear(clearRegion);
3861 }
3862
AddWindowOverlayWebRenderCommands(layers::WebRenderBridgeChild * aWrBridge,wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources)3863 void nsWindow::AddWindowOverlayWebRenderCommands(
3864 layers::WebRenderBridgeChild* aWrBridge, wr::DisplayListBuilder& aBuilder,
3865 wr::IpcResourceUpdateQueue& aResources) {
3866 if (mWindowButtonsRect) {
3867 wr::LayoutRect rect = wr::ToLayoutRect(*mWindowButtonsRect);
3868 nsTArray<wr::ComplexClipRegion> roundedClip;
3869 roundedClip.AppendElement(wr::ToComplexClipRegion(
3870 RoundedRect(ThebesRect(mWindowButtonsRect->ToUnknownRect()),
3871 RectCornerRadii(0, 0, 3, 3))));
3872 wr::WrClipId clipId =
3873 aBuilder.DefineClip(Nothing(), Nothing(), rect, &roundedClip);
3874 aBuilder.PushClip(clipId);
3875 aBuilder.PushClearRect(rect);
3876 aBuilder.PopClip();
3877 }
3878 }
3879
GetMaxTouchPoints() const3880 uint32_t nsWindow::GetMaxTouchPoints() const {
3881 return WinUtils::GetMaxTouchPoints();
3882 }
3883
3884 /**************************************************************
3885 **************************************************************
3886 **
3887 ** BLOCK: Moz Events
3888 **
3889 ** Moz GUI event management.
3890 **
3891 **************************************************************
3892 **************************************************************/
3893
3894 /**************************************************************
3895 *
3896 * SECTION: Mozilla event initialization
3897 *
3898 * Helpers for initializing moz events.
3899 *
3900 **************************************************************/
3901
3902 // Event initialization
InitEvent(WidgetGUIEvent & event,LayoutDeviceIntPoint * aPoint)3903 void nsWindow::InitEvent(WidgetGUIEvent& event, LayoutDeviceIntPoint* aPoint) {
3904 if (nullptr == aPoint) { // use the point from the event
3905 // get the message position in client coordinates
3906 if (mWnd != nullptr) {
3907 DWORD pos = ::GetMessagePos();
3908 POINT cpos;
3909
3910 cpos.x = GET_X_LPARAM(pos);
3911 cpos.y = GET_Y_LPARAM(pos);
3912
3913 ::ScreenToClient(mWnd, &cpos);
3914 event.mRefPoint = LayoutDeviceIntPoint(cpos.x, cpos.y);
3915 } else {
3916 event.mRefPoint = LayoutDeviceIntPoint(0, 0);
3917 }
3918 } else {
3919 // use the point override if provided
3920 event.mRefPoint = *aPoint;
3921 }
3922
3923 event.AssignEventTime(CurrentMessageWidgetEventTime());
3924 }
3925
CurrentMessageWidgetEventTime() const3926 WidgetEventTime nsWindow::CurrentMessageWidgetEventTime() const {
3927 LONG messageTime = ::GetMessageTime();
3928 return WidgetEventTime(messageTime, GetMessageTimeStamp(messageTime));
3929 }
3930
3931 /**************************************************************
3932 *
3933 * SECTION: Moz event dispatch helpers
3934 *
3935 * Helpers for dispatching different types of moz events.
3936 *
3937 **************************************************************/
3938
3939 // Main event dispatch. Invokes callback and ProcessEvent method on
3940 // Event Listener object. Part of nsIWidget.
DispatchEvent(WidgetGUIEvent * event,nsEventStatus & aStatus)3941 nsresult nsWindow::DispatchEvent(WidgetGUIEvent* event,
3942 nsEventStatus& aStatus) {
3943 #ifdef WIDGET_DEBUG_OUTPUT
3944 debug_DumpEvent(stdout, event->mWidget, event, "something", (int32_t)mWnd);
3945 #endif // WIDGET_DEBUG_OUTPUT
3946
3947 aStatus = nsEventStatus_eIgnore;
3948
3949 // Top level windows can have a view attached which requires events be sent
3950 // to the underlying base window and the view. Added when we combined the
3951 // base chrome window with the main content child for nc client area (title
3952 // bar) rendering.
3953 if (mAttachedWidgetListener) {
3954 aStatus = mAttachedWidgetListener->HandleEvent(event, mUseAttachedEvents);
3955 } else if (mWidgetListener) {
3956 aStatus = mWidgetListener->HandleEvent(event, mUseAttachedEvents);
3957 }
3958
3959 // the window can be destroyed during processing of seemingly innocuous events
3960 // like, say, mousedowns due to the magic of scripting. mousedowns will return
3961 // nsEventStatus_eIgnore, which causes problems with the deleted window.
3962 // therefore:
3963 if (mOnDestroyCalled) aStatus = nsEventStatus_eConsumeNoDefault;
3964 return NS_OK;
3965 }
3966
DispatchStandardEvent(EventMessage aMsg)3967 bool nsWindow::DispatchStandardEvent(EventMessage aMsg) {
3968 WidgetGUIEvent event(true, aMsg, this);
3969 InitEvent(event);
3970
3971 bool result = DispatchWindowEvent(&event);
3972 return result;
3973 }
3974
DispatchKeyboardEvent(WidgetKeyboardEvent * event)3975 bool nsWindow::DispatchKeyboardEvent(WidgetKeyboardEvent* event) {
3976 nsEventStatus status = DispatchInputEvent(event);
3977 return ConvertStatus(status);
3978 }
3979
DispatchContentCommandEvent(WidgetContentCommandEvent * aEvent)3980 bool nsWindow::DispatchContentCommandEvent(WidgetContentCommandEvent* aEvent) {
3981 nsEventStatus status;
3982 DispatchEvent(aEvent, status);
3983 return ConvertStatus(status);
3984 }
3985
DispatchWheelEvent(WidgetWheelEvent * aEvent)3986 bool nsWindow::DispatchWheelEvent(WidgetWheelEvent* aEvent) {
3987 nsEventStatus status = DispatchInputEvent(aEvent->AsInputEvent());
3988 return ConvertStatus(status);
3989 }
3990
DispatchWindowEvent(WidgetGUIEvent * event)3991 bool nsWindow::DispatchWindowEvent(WidgetGUIEvent* event) {
3992 nsEventStatus status;
3993 DispatchEvent(event, status);
3994 return ConvertStatus(status);
3995 }
3996
DispatchWindowEvent(WidgetGUIEvent * event,nsEventStatus & aStatus)3997 bool nsWindow::DispatchWindowEvent(WidgetGUIEvent* event,
3998 nsEventStatus& aStatus) {
3999 DispatchEvent(event, aStatus);
4000 return ConvertStatus(aStatus);
4001 }
4002
4003 // Recursively dispatch synchronous paints for nsIWidget
4004 // descendants with invalidated rectangles.
DispatchStarvedPaints(HWND aWnd,LPARAM aMsg)4005 BOOL CALLBACK nsWindow::DispatchStarvedPaints(HWND aWnd, LPARAM aMsg) {
4006 LONG_PTR proc = ::GetWindowLongPtrW(aWnd, GWLP_WNDPROC);
4007 if (proc == (LONG_PTR)&nsWindow::WindowProc) {
4008 // its one of our windows so check to see if it has a
4009 // invalidated rect. If it does. Dispatch a synchronous
4010 // paint.
4011 if (GetUpdateRect(aWnd, nullptr, FALSE)) VERIFY(::UpdateWindow(aWnd));
4012 }
4013 return TRUE;
4014 }
4015
4016 // Check for pending paints and dispatch any pending paint
4017 // messages for any nsIWidget which is a descendant of the
4018 // top-level window that *this* window is embedded within.
4019 //
4020 // Note: We do not dispatch pending paint messages for non
4021 // nsIWidget managed windows.
DispatchPendingEvents()4022 void nsWindow::DispatchPendingEvents() {
4023 if (mPainting) {
4024 NS_WARNING(
4025 "We were asked to dispatch pending events during painting, "
4026 "denying since that's unsafe.");
4027 return;
4028 }
4029
4030 // We need to ensure that reflow events do not get starved.
4031 // At the same time, we don't want to recurse through here
4032 // as that would prevent us from dispatching starved paints.
4033 static int recursionBlocker = 0;
4034 if (recursionBlocker++ == 0) {
4035 NS_ProcessPendingEvents(nullptr, PR_MillisecondsToInterval(100));
4036 --recursionBlocker;
4037 }
4038
4039 // Quickly check to see if there are any paint events pending,
4040 // but only dispatch them if it has been long enough since the
4041 // last paint completed.
4042 if (::GetQueueStatus(QS_PAINT) &&
4043 ((TimeStamp::Now() - mLastPaintEndTime).ToMilliseconds() >= 50)) {
4044 // Find the top level window.
4045 HWND topWnd = WinUtils::GetTopLevelHWND(mWnd);
4046
4047 // Dispatch pending paints for topWnd and all its descendant windows.
4048 // Note: EnumChildWindows enumerates all descendant windows not just
4049 // the children (but not the window itself).
4050 nsWindow::DispatchStarvedPaints(topWnd, 0);
4051 ::EnumChildWindows(topWnd, nsWindow::DispatchStarvedPaints, 0);
4052 }
4053 }
4054
DispatchPluginEvent(UINT aMessage,WPARAM aWParam,LPARAM aLParam,bool aDispatchPendingEvents)4055 bool nsWindow::DispatchPluginEvent(UINT aMessage, WPARAM aWParam,
4056 LPARAM aLParam,
4057 bool aDispatchPendingEvents) {
4058 bool ret = nsWindowBase::DispatchPluginEvent(
4059 WinUtils::InitMSG(aMessage, aWParam, aLParam, mWnd));
4060 if (aDispatchPendingEvents && !Destroyed()) {
4061 DispatchPendingEvents();
4062 }
4063 return ret;
4064 }
4065
TouchEventShouldStartDrag(EventMessage aEventMessage,LayoutDeviceIntPoint aEventPoint)4066 bool nsWindow::TouchEventShouldStartDrag(EventMessage aEventMessage,
4067 LayoutDeviceIntPoint aEventPoint) {
4068 // Allow users to start dragging by double-tapping.
4069 if (aEventMessage == eMouseDoubleClick) {
4070 return true;
4071 }
4072
4073 // In chrome UI, allow touchdownstartsdrag attributes
4074 // to cause any touchdown event to trigger a drag.
4075 if (aEventMessage == eMouseDown) {
4076 WidgetMouseEvent hittest(true, eMouseHitTest, this,
4077 WidgetMouseEvent::eReal);
4078 hittest.mRefPoint = aEventPoint;
4079 hittest.mIgnoreRootScrollFrame = true;
4080 hittest.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
4081 DispatchInputEvent(&hittest);
4082
4083 EventTarget* target = hittest.GetDOMEventTarget();
4084 if (target) {
4085 nsCOMPtr<nsIContent> node = do_QueryInterface(target);
4086
4087 // Check if the element or any parent element has the
4088 // attribute we're looking for.
4089 while (node) {
4090 if (node->IsElement()) {
4091 nsAutoString startDrag;
4092 node->AsElement()->GetAttribute(
4093 NS_LITERAL_STRING("touchdownstartsdrag"), startDrag);
4094 if (!startDrag.IsEmpty()) {
4095 return true;
4096 }
4097 }
4098 node = node->GetParent();
4099 }
4100 }
4101 }
4102
4103 return false;
4104 }
4105
4106 // Deal with all sort of mouse event
DispatchMouseEvent(EventMessage aEventMessage,WPARAM wParam,LPARAM lParam,bool aIsContextMenuKey,int16_t aButton,uint16_t aInputSource,WinPointerInfo * aPointerInfo)4107 bool nsWindow::DispatchMouseEvent(EventMessage aEventMessage, WPARAM wParam,
4108 LPARAM lParam, bool aIsContextMenuKey,
4109 int16_t aButton, uint16_t aInputSource,
4110 WinPointerInfo* aPointerInfo) {
4111 enum { eUnset, ePrecise, eTouch };
4112 static int sTouchInputActiveState = eUnset;
4113 bool result = false;
4114
4115 UserActivity();
4116
4117 if (!mWidgetListener) {
4118 return result;
4119 }
4120
4121 LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
4122 LayoutDeviceIntPoint mpScreen = eventPoint + WidgetToScreenOffset();
4123
4124 // Suppress mouse moves caused by widget creation. Make sure to do this early
4125 // so that we update sLastMouseMovePoint even for touch-induced mousemove
4126 // events.
4127 if (aEventMessage == eMouseMove) {
4128 if ((sLastMouseMovePoint.x == mpScreen.x) &&
4129 (sLastMouseMovePoint.y == mpScreen.y)) {
4130 return result;
4131 }
4132 sLastMouseMovePoint.x = mpScreen.x;
4133 sLastMouseMovePoint.y = mpScreen.y;
4134 }
4135
4136 if (WinUtils::GetIsMouseFromTouch(aEventMessage)) {
4137 if (aEventMessage == eMouseDown) {
4138 Telemetry::Accumulate(Telemetry::FX_TOUCH_USED, 1);
4139 }
4140
4141 // Fire an observer when the user initially touches a touch screen. Front
4142 // end uses this to modify UX.
4143 if (sTouchInputActiveState != eTouch) {
4144 sTouchInputActiveState = eTouch;
4145 nsCOMPtr<nsIObserverService> obsServ =
4146 mozilla::services::GetObserverService();
4147 obsServ->NotifyObservers(nullptr, "touch-input-detected", nullptr);
4148 }
4149
4150 if (mTouchWindow) {
4151 // If mTouchWindow is true, then we must have APZ enabled and be
4152 // feeding it raw touch events. In that case we only want to
4153 // send touch-generated mouse events to content if they should
4154 // start a touch-based drag-and-drop gesture, such as on
4155 // double-tapping or when tapping elements marked with the
4156 // touchdownstartsdrag attribute in chrome UI.
4157 MOZ_ASSERT(mAPZC);
4158 if (TouchEventShouldStartDrag(aEventMessage, eventPoint)) {
4159 aEventMessage = eMouseTouchDrag;
4160 } else {
4161 return result;
4162 }
4163 }
4164 } else {
4165 // Fire an observer when the user initially uses a mouse or pen.
4166 if (sTouchInputActiveState != ePrecise) {
4167 sTouchInputActiveState = ePrecise;
4168 nsCOMPtr<nsIObserverService> obsServ =
4169 mozilla::services::GetObserverService();
4170 obsServ->NotifyObservers(nullptr, "precise-input-detected", nullptr);
4171 }
4172 }
4173
4174 uint32_t pointerId =
4175 aPointerInfo ? aPointerInfo->pointerId : MOUSE_POINTERID();
4176
4177 // Since it is unclear whether a user will use the digitizer,
4178 // Postpone initialization until first PEN message will be found.
4179 if (nsIDOMMouseEvent::MOZ_SOURCE_PEN == aInputSource
4180 // Messages should be only at topLevel window.
4181 && nsWindowType::eWindowType_toplevel == mWindowType
4182 // Currently this scheme is used only when pointer events is enabled.
4183 && gfxPrefs::PointerEventsEnabled() && InkCollector::sInkCollector) {
4184 InkCollector::sInkCollector->SetTarget(mWnd);
4185 InkCollector::sInkCollector->SetPointerId(pointerId);
4186 }
4187
4188 switch (aEventMessage) {
4189 case eMouseDown:
4190 CaptureMouse(true);
4191 break;
4192
4193 // eMouseMove and eMouseExitFromWidget are here because we need to make
4194 // sure capture flag isn't left on after a drag where we wouldn't see a
4195 // button up message (see bug 324131).
4196 case eMouseUp:
4197 case eMouseMove:
4198 case eMouseExitFromWidget:
4199 if (!(wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) &&
4200 sIsInMouseCapture)
4201 CaptureMouse(false);
4202 break;
4203
4204 default:
4205 break;
4206
4207 } // switch
4208
4209 WidgetMouseEvent event(true, aEventMessage, this, WidgetMouseEvent::eReal,
4210 aIsContextMenuKey ? WidgetMouseEvent::eContextMenuKey
4211 : WidgetMouseEvent::eNormal);
4212 if (aEventMessage == eContextMenu && aIsContextMenuKey) {
4213 LayoutDeviceIntPoint zero(0, 0);
4214 InitEvent(event, &zero);
4215 } else {
4216 InitEvent(event, &eventPoint);
4217 }
4218
4219 ModifierKeyState modifierKeyState;
4220 modifierKeyState.InitInputEvent(event);
4221
4222 // eContextMenu with Shift state is special. It won't fire "contextmenu"
4223 // event in the web content for blocking web content to prevent its default.
4224 // However, Shift+F10 is a standard shortcut key on Windows. Therefore,
4225 // this should not block web page to prevent its default. I.e., it should
4226 // behave same as ContextMenu key without Shift key.
4227 // XXX Should we allow to block web page to prevent its default with
4228 // Ctrl+Shift+F10 or Alt+Shift+F10 instead?
4229 if (aEventMessage == eContextMenu && aIsContextMenuKey && event.IsShift() &&
4230 NativeKey::LastKeyOrCharMSG().message == WM_SYSKEYDOWN &&
4231 NativeKey::LastKeyOrCharMSG().wParam == VK_F10) {
4232 event.mModifiers &= ~MODIFIER_SHIFT;
4233 }
4234
4235 event.button = aButton;
4236 event.inputSource = aInputSource;
4237 if (aPointerInfo) {
4238 // Mouse events from Windows WM_POINTER*. Fill more information in
4239 // WidgetMouseEvent.
4240 event.AssignPointerHelperData(*aPointerInfo);
4241 event.pressure = aPointerInfo->mPressure;
4242 event.buttons = aPointerInfo->mButtons;
4243 } else {
4244 // If we get here the mouse events must be from non-touch sources, so
4245 // convert it to pointer events as well
4246 event.convertToPointer = true;
4247 event.pointerId = pointerId;
4248 }
4249
4250 bool insideMovementThreshold =
4251 (DeprecatedAbs(sLastMousePoint.x - eventPoint.x) <
4252 (short)::GetSystemMetrics(SM_CXDOUBLECLK)) &&
4253 (DeprecatedAbs(sLastMousePoint.y - eventPoint.y) <
4254 (short)::GetSystemMetrics(SM_CYDOUBLECLK));
4255
4256 BYTE eventButton;
4257 switch (aButton) {
4258 case WidgetMouseEvent::eLeftButton:
4259 eventButton = VK_LBUTTON;
4260 break;
4261 case WidgetMouseEvent::eMiddleButton:
4262 eventButton = VK_MBUTTON;
4263 break;
4264 case WidgetMouseEvent::eRightButton:
4265 eventButton = VK_RBUTTON;
4266 break;
4267 default:
4268 eventButton = 0;
4269 break;
4270 }
4271
4272 // Doubleclicks are used to set the click count, then changed to mousedowns
4273 // We're going to time double-clicks from mouse *up* to next mouse *down*
4274 LONG curMsgTime = ::GetMessageTime();
4275
4276 switch (aEventMessage) {
4277 case eMouseDoubleClick:
4278 event.mMessage = eMouseDown;
4279 event.button = aButton;
4280 sLastClickCount = 2;
4281 sLastMouseDownTime = curMsgTime;
4282 break;
4283 case eMouseUp:
4284 // remember when this happened for the next mouse down
4285 sLastMousePoint.x = eventPoint.x;
4286 sLastMousePoint.y = eventPoint.y;
4287 sLastMouseButton = eventButton;
4288 break;
4289 case eMouseDown:
4290 // now look to see if we want to convert this to a double- or triple-click
4291 if (((curMsgTime - sLastMouseDownTime) < (LONG)::GetDoubleClickTime()) &&
4292 insideMovementThreshold && eventButton == sLastMouseButton) {
4293 sLastClickCount++;
4294 } else {
4295 // reset the click count, to count *this* click
4296 sLastClickCount = 1;
4297 }
4298 // Set last Click time on MouseDown only
4299 sLastMouseDownTime = curMsgTime;
4300 break;
4301 case eMouseMove:
4302 if (!insideMovementThreshold) {
4303 sLastClickCount = 0;
4304 }
4305 break;
4306 case eMouseExitFromWidget:
4307 event.mExitFrom = IsTopLevelMouseExit(mWnd) ? WidgetMouseEvent::eTopLevel
4308 : WidgetMouseEvent::eChild;
4309 break;
4310 default:
4311 break;
4312 }
4313 event.mClickCount = sLastClickCount;
4314
4315 #ifdef NS_DEBUG_XX
4316 MOZ_LOG(gWindowsLog, LogLevel::Info,
4317 ("Msg Time: %d Click Count: %d\n", curMsgTime, event.mClickCount));
4318 #endif
4319
4320 NPEvent pluginEvent;
4321
4322 switch (aEventMessage) {
4323 case eMouseDown:
4324 switch (aButton) {
4325 case WidgetMouseEvent::eLeftButton:
4326 pluginEvent.event = WM_LBUTTONDOWN;
4327 break;
4328 case WidgetMouseEvent::eMiddleButton:
4329 pluginEvent.event = WM_MBUTTONDOWN;
4330 break;
4331 case WidgetMouseEvent::eRightButton:
4332 pluginEvent.event = WM_RBUTTONDOWN;
4333 break;
4334 default:
4335 break;
4336 }
4337 break;
4338 case eMouseUp:
4339 switch (aButton) {
4340 case WidgetMouseEvent::eLeftButton:
4341 pluginEvent.event = WM_LBUTTONUP;
4342 break;
4343 case WidgetMouseEvent::eMiddleButton:
4344 pluginEvent.event = WM_MBUTTONUP;
4345 break;
4346 case WidgetMouseEvent::eRightButton:
4347 pluginEvent.event = WM_RBUTTONUP;
4348 break;
4349 default:
4350 break;
4351 }
4352 break;
4353 case eMouseDoubleClick:
4354 switch (aButton) {
4355 case WidgetMouseEvent::eLeftButton:
4356 pluginEvent.event = WM_LBUTTONDBLCLK;
4357 break;
4358 case WidgetMouseEvent::eMiddleButton:
4359 pluginEvent.event = WM_MBUTTONDBLCLK;
4360 break;
4361 case WidgetMouseEvent::eRightButton:
4362 pluginEvent.event = WM_RBUTTONDBLCLK;
4363 break;
4364 default:
4365 break;
4366 }
4367 break;
4368 case eMouseMove:
4369 pluginEvent.event = WM_MOUSEMOVE;
4370 break;
4371 case eMouseExitFromWidget:
4372 pluginEvent.event = WM_MOUSELEAVE;
4373 break;
4374 default:
4375 pluginEvent.event = WM_NULL;
4376 break;
4377 }
4378
4379 pluginEvent.wParam = wParam; // plugins NEED raw OS event flags!
4380 pluginEvent.lParam = lParam;
4381
4382 event.mPluginEvent.Copy(pluginEvent);
4383
4384 // call the event callback
4385 if (mWidgetListener) {
4386 if (aEventMessage == eMouseMove) {
4387 LayoutDeviceIntRect rect = GetBounds();
4388 rect.MoveTo(0, 0);
4389
4390 if (rect.Contains(event.mRefPoint)) {
4391 if (sCurrentWindow == nullptr || sCurrentWindow != this) {
4392 if ((nullptr != sCurrentWindow) && (!sCurrentWindow->mInDtor)) {
4393 LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
4394 sCurrentWindow->DispatchMouseEvent(
4395 eMouseExitFromWidget, wParam, pos, false,
4396 WidgetMouseEvent::eLeftButton, aInputSource, aPointerInfo);
4397 }
4398 sCurrentWindow = this;
4399 if (!mInDtor) {
4400 LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
4401 sCurrentWindow->DispatchMouseEvent(
4402 eMouseEnterIntoWidget, wParam, pos, false,
4403 WidgetMouseEvent::eLeftButton, aInputSource, aPointerInfo);
4404 }
4405 }
4406 }
4407 } else if (aEventMessage == eMouseExitFromWidget) {
4408 if (sCurrentWindow == this) {
4409 sCurrentWindow = nullptr;
4410 }
4411 }
4412
4413 result = ConvertStatus(DispatchInputEvent(&event));
4414
4415 // Release the widget with NS_IF_RELEASE() just in case
4416 // the context menu key code in EventListenerManager::HandleEvent()
4417 // released it already.
4418 return result;
4419 }
4420
4421 return result;
4422 }
4423
DispatchFocusToTopLevelWindow(bool aIsActivate)4424 void nsWindow::DispatchFocusToTopLevelWindow(bool aIsActivate) {
4425 if (aIsActivate) sJustGotActivate = false;
4426 sJustGotDeactivate = false;
4427
4428 // retrive the toplevel window or dialog
4429 HWND curWnd = mWnd;
4430 HWND toplevelWnd = nullptr;
4431 while (curWnd) {
4432 toplevelWnd = curWnd;
4433
4434 nsWindow* win = WinUtils::GetNSWindowPtr(curWnd);
4435 if (win) {
4436 nsWindowType wintype = win->WindowType();
4437 if (wintype == eWindowType_toplevel || wintype == eWindowType_dialog)
4438 break;
4439 }
4440
4441 curWnd = ::GetParent(curWnd); // Parent or owner (if has no parent)
4442 }
4443
4444 if (toplevelWnd) {
4445 nsWindow* win = WinUtils::GetNSWindowPtr(toplevelWnd);
4446 if (win && win->mWidgetListener) {
4447 if (aIsActivate) {
4448 win->mWidgetListener->WindowActivated();
4449 } else {
4450 if (!win->BlurEventsSuppressed()) {
4451 win->mWidgetListener->WindowDeactivated();
4452 }
4453 }
4454 }
4455 }
4456 }
4457
WindowAtMouse()4458 HWND nsWindow::WindowAtMouse() {
4459 DWORD pos = ::GetMessagePos();
4460 POINT mp;
4461 mp.x = GET_X_LPARAM(pos);
4462 mp.y = GET_Y_LPARAM(pos);
4463 return ::WindowFromPoint(mp);
4464 }
4465
IsTopLevelMouseExit(HWND aWnd)4466 bool nsWindow::IsTopLevelMouseExit(HWND aWnd) {
4467 HWND mouseWnd = WindowAtMouse();
4468
4469 // WinUtils::GetTopLevelHWND() will return a HWND for the window frame
4470 // (which includes the non-client area). If the mouse has moved into
4471 // the non-client area, we should treat it as a top-level exit.
4472 HWND mouseTopLevel = WinUtils::GetTopLevelHWND(mouseWnd);
4473 if (mouseWnd == mouseTopLevel) return true;
4474
4475 return WinUtils::GetTopLevelHWND(aWnd) != mouseTopLevel;
4476 }
4477
BlurEventsSuppressed()4478 bool nsWindow::BlurEventsSuppressed() {
4479 // are they suppressed in this window?
4480 if (mBlurSuppressLevel > 0) return true;
4481
4482 // are they suppressed by any container widget?
4483 HWND parentWnd = ::GetParent(mWnd);
4484 if (parentWnd) {
4485 nsWindow* parent = WinUtils::GetNSWindowPtr(parentWnd);
4486 if (parent) return parent->BlurEventsSuppressed();
4487 }
4488 return false;
4489 }
4490
4491 // In some circumstances (opening dependent windows) it makes more sense
4492 // (and fixes a crash bug) to not blur the parent window. Called from
4493 // nsFilePicker.
SuppressBlurEvents(bool aSuppress)4494 void nsWindow::SuppressBlurEvents(bool aSuppress) {
4495 if (aSuppress)
4496 ++mBlurSuppressLevel; // for this widget
4497 else {
4498 NS_ASSERTION(mBlurSuppressLevel > 0, "unbalanced blur event suppression");
4499 if (mBlurSuppressLevel > 0) --mBlurSuppressLevel;
4500 }
4501 }
4502
ConvertStatus(nsEventStatus aStatus)4503 bool nsWindow::ConvertStatus(nsEventStatus aStatus) {
4504 return aStatus == nsEventStatus_eConsumeNoDefault;
4505 }
4506
4507 /**************************************************************
4508 *
4509 * SECTION: IPC
4510 *
4511 * IPC related helpers.
4512 *
4513 **************************************************************/
4514
4515 // static
IsAsyncResponseEvent(UINT aMsg,LRESULT & aResult)4516 bool nsWindow::IsAsyncResponseEvent(UINT aMsg, LRESULT& aResult) {
4517 switch (aMsg) {
4518 case WM_SETFOCUS:
4519 case WM_KILLFOCUS:
4520 case WM_ENABLE:
4521 case WM_WINDOWPOSCHANGING:
4522 case WM_WINDOWPOSCHANGED:
4523 case WM_PARENTNOTIFY:
4524 case WM_ACTIVATEAPP:
4525 case WM_NCACTIVATE:
4526 case WM_ACTIVATE:
4527 case WM_CHILDACTIVATE:
4528 case WM_IME_SETCONTEXT:
4529 case WM_IME_NOTIFY:
4530 case WM_SHOWWINDOW:
4531 case WM_CANCELMODE:
4532 case WM_MOUSEACTIVATE:
4533 case WM_CONTEXTMENU:
4534 aResult = 0;
4535 return true;
4536
4537 case WM_SETTINGCHANGE:
4538 case WM_SETCURSOR:
4539 return false;
4540 }
4541
4542 #ifdef DEBUG
4543 char szBuf[200];
4544 sprintf(szBuf,
4545 "An unhandled ISMEX_SEND message was received during spin loop! (%X)",
4546 aMsg);
4547 NS_WARNING(szBuf);
4548 #endif
4549
4550 return false;
4551 }
4552
IPCWindowProcHandler(UINT & msg,WPARAM & wParam,LPARAM & lParam)4553 void nsWindow::IPCWindowProcHandler(UINT& msg, WPARAM& wParam, LPARAM& lParam) {
4554 MOZ_ASSERT_IF(
4555 msg != WM_GETOBJECT,
4556 !mozilla::ipc::MessageChannel::IsPumpingMessages() ||
4557 mozilla::ipc::SuppressedNeuteringRegion::IsNeuteringSuppressed());
4558
4559 // Modal UI being displayed in windowless plugins.
4560 if (mozilla::ipc::MessageChannel::IsSpinLoopActive() &&
4561 (InSendMessageEx(nullptr) & (ISMEX_REPLIED | ISMEX_SEND)) == ISMEX_SEND) {
4562 LRESULT res;
4563 if (IsAsyncResponseEvent(msg, res)) {
4564 ReplyMessage(res);
4565 }
4566 return;
4567 }
4568
4569 // Handle certain sync plugin events sent to the parent which
4570 // trigger ipc calls that result in deadlocks.
4571
4572 DWORD dwResult = 0;
4573 bool handled = false;
4574
4575 switch (msg) {
4576 // Windowless flash sending WM_ACTIVATE events to the main window
4577 // via calls to ShowWindow.
4578 case WM_ACTIVATE:
4579 if (lParam != 0 && LOWORD(wParam) == WA_ACTIVE &&
4580 IsWindow((HWND)lParam)) {
4581 // Check for Adobe Reader X sync activate message from their
4582 // helper window and ignore. Fixes an annoying focus problem.
4583 if ((InSendMessageEx(nullptr) & (ISMEX_REPLIED | ISMEX_SEND)) ==
4584 ISMEX_SEND) {
4585 wchar_t szClass[10];
4586 HWND focusWnd = (HWND)lParam;
4587 if (IsWindowVisible(focusWnd) &&
4588 GetClassNameW(focusWnd, szClass,
4589 sizeof(szClass) / sizeof(char16_t)) &&
4590 !wcscmp(szClass, L"Edit") &&
4591 !WinUtils::IsOurProcessWindow(focusWnd)) {
4592 break;
4593 }
4594 }
4595 handled = true;
4596 }
4597 break;
4598 // Plugins taking or losing focus triggering focus app messages.
4599 case WM_SETFOCUS:
4600 case WM_KILLFOCUS:
4601 // Windowed plugins that pass sys key events to defwndproc generate
4602 // WM_SYSCOMMAND events to the main window.
4603 case WM_SYSCOMMAND:
4604 // Windowed plugins that fire context menu selection events to parent
4605 // windows.
4606 case WM_CONTEXTMENU:
4607 // IME events fired as a result of synchronous focus changes
4608 case WM_IME_SETCONTEXT:
4609 handled = true;
4610 break;
4611 }
4612
4613 if (handled &&
4614 (InSendMessageEx(nullptr) & (ISMEX_REPLIED | ISMEX_SEND)) == ISMEX_SEND) {
4615 ReplyMessage(dwResult);
4616 }
4617 }
4618
4619 /**************************************************************
4620 **************************************************************
4621 **
4622 ** BLOCK: Native events
4623 **
4624 ** Main Windows message handlers and OnXXX handlers for
4625 ** Windows event handling.
4626 **
4627 **************************************************************
4628 **************************************************************/
4629
4630 /**************************************************************
4631 *
4632 * SECTION: Wind proc.
4633 *
4634 * The main Windows event procedures and associated
4635 * message processing methods.
4636 *
4637 **************************************************************/
4638
DisplaySystemMenu(HWND hWnd,nsSizeMode sizeMode,bool isRtl,int32_t x,int32_t y)4639 static bool DisplaySystemMenu(HWND hWnd, nsSizeMode sizeMode, bool isRtl,
4640 int32_t x, int32_t y) {
4641 HMENU hMenu = GetSystemMenu(hWnd, FALSE);
4642 if (hMenu) {
4643 MENUITEMINFO mii;
4644 mii.cbSize = sizeof(MENUITEMINFO);
4645 mii.fMask = MIIM_STATE;
4646 mii.fType = 0;
4647
4648 // update the options
4649 mii.fState = MF_ENABLED;
4650 SetMenuItemInfo(hMenu, SC_RESTORE, FALSE, &mii);
4651 SetMenuItemInfo(hMenu, SC_SIZE, FALSE, &mii);
4652 SetMenuItemInfo(hMenu, SC_MOVE, FALSE, &mii);
4653 SetMenuItemInfo(hMenu, SC_MAXIMIZE, FALSE, &mii);
4654 SetMenuItemInfo(hMenu, SC_MINIMIZE, FALSE, &mii);
4655
4656 mii.fState = MF_GRAYED;
4657 switch (sizeMode) {
4658 case nsSizeMode_Fullscreen:
4659 // intentional fall through
4660 case nsSizeMode_Maximized:
4661 SetMenuItemInfo(hMenu, SC_SIZE, FALSE, &mii);
4662 SetMenuItemInfo(hMenu, SC_MOVE, FALSE, &mii);
4663 SetMenuItemInfo(hMenu, SC_MAXIMIZE, FALSE, &mii);
4664 break;
4665 case nsSizeMode_Minimized:
4666 SetMenuItemInfo(hMenu, SC_MINIMIZE, FALSE, &mii);
4667 break;
4668 case nsSizeMode_Normal:
4669 SetMenuItemInfo(hMenu, SC_RESTORE, FALSE, &mii);
4670 break;
4671 }
4672 LPARAM cmd = TrackPopupMenu(
4673 hMenu,
4674 (TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_TOPALIGN |
4675 (isRtl ? TPM_RIGHTALIGN : TPM_LEFTALIGN)),
4676 x, y, 0, hWnd, nullptr);
4677 if (cmd) {
4678 PostMessage(hWnd, WM_SYSCOMMAND, cmd, 0);
4679 return true;
4680 }
4681 }
4682 return false;
4683 }
4684
ActivityTypeForMessage(UINT msg)4685 inline static mozilla::HangMonitor::ActivityType ActivityTypeForMessage(
4686 UINT msg) {
4687 if ((msg >= WM_KEYFIRST && msg <= WM_IME_KEYLAST) ||
4688 (msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST) ||
4689 (msg >= MOZ_WM_MOUSEWHEEL_FIRST && msg <= MOZ_WM_MOUSEWHEEL_LAST) ||
4690 (msg >= NS_WM_IMEFIRST && msg <= NS_WM_IMELAST)) {
4691 return mozilla::HangMonitor::kUIActivity;
4692 }
4693
4694 // This may not actually be right, but we don't want to reset the timer if
4695 // we're not actually processing a UI message.
4696 return mozilla::HangMonitor::kActivityUIAVail;
4697 }
4698
4699 // The WndProc procedure for all nsWindows in this toolkit. This merely catches
4700 // exceptions and passes the real work to WindowProcInternal. See bug 587406
4701 // and http://msdn.microsoft.com/en-us/library/ms633573%28VS.85%29.aspx
WindowProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)4702 LRESULT CALLBACK nsWindow::WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
4703 LPARAM lParam) {
4704 mozilla::ipc::CancelCPOWs();
4705
4706 HangMonitor::NotifyActivity(ActivityTypeForMessage(msg));
4707
4708 return mozilla::CallWindowProcCrashProtected(WindowProcInternal, hWnd, msg,
4709 wParam, lParam);
4710 }
4711
WindowProcInternal(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)4712 LRESULT CALLBACK nsWindow::WindowProcInternal(HWND hWnd, UINT msg,
4713 WPARAM wParam, LPARAM lParam) {
4714 if (::GetWindowLongPtrW(hWnd, GWLP_ID) == eFakeTrackPointScrollableID) {
4715 // This message was sent to the FAKETRACKPOINTSCROLLABLE.
4716 if (msg == WM_HSCROLL) {
4717 // Route WM_HSCROLL messages to the main window.
4718 hWnd = ::GetParent(::GetParent(hWnd));
4719 } else {
4720 // Handle all other messages with its original window procedure.
4721 WNDPROC prevWindowProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_USERDATA);
4722 return ::CallWindowProcW(prevWindowProc, hWnd, msg, wParam, lParam);
4723 }
4724 }
4725
4726 if (msg == MOZ_WM_TRACE) {
4727 // This is a tracer event for measuring event loop latency.
4728 // See WidgetTraceEvent.cpp for more details.
4729 mozilla::SignalTracerThread();
4730 return 0;
4731 }
4732
4733 // Get the window which caused the event and ask it to process the message
4734 nsWindow* targetWindow = WinUtils::GetNSWindowPtr(hWnd);
4735 NS_ASSERTION(targetWindow, "nsWindow* is null!");
4736 if (!targetWindow) return ::DefWindowProcW(hWnd, msg, wParam, lParam);
4737
4738 // Hold the window for the life of this method, in case it gets
4739 // destroyed during processing, unless we're in the dtor already.
4740 nsCOMPtr<nsIWidget> kungFuDeathGrip;
4741 if (!targetWindow->mInDtor) kungFuDeathGrip = targetWindow;
4742
4743 targetWindow->IPCWindowProcHandler(msg, wParam, lParam);
4744
4745 // Create this here so that we store the last rolled up popup until after
4746 // the event has been processed.
4747 nsAutoRollup autoRollup;
4748
4749 LRESULT popupHandlingResult;
4750 if (DealWithPopups(hWnd, msg, wParam, lParam, &popupHandlingResult))
4751 return popupHandlingResult;
4752
4753 // Call ProcessMessage
4754 LRESULT retValue;
4755 if (targetWindow->ProcessMessage(msg, wParam, lParam, &retValue)) {
4756 return retValue;
4757 }
4758
4759 LRESULT res = ::CallWindowProcW(targetWindow->GetPrevWindowProc(), hWnd, msg,
4760 wParam, lParam);
4761
4762 return res;
4763 }
4764
4765 // The main windows message processing method for plugins.
4766 // The result means whether this method processed the native
4767 // event for plugin. If false, the native event should be
4768 // processed by the caller self.
ProcessMessageForPlugin(const MSG & aMsg,MSGResult & aResult)4769 bool nsWindow::ProcessMessageForPlugin(const MSG& aMsg, MSGResult& aResult) {
4770 aResult.mResult = 0;
4771 aResult.mConsumed = true;
4772
4773 bool eventDispatched = false;
4774 switch (aMsg.message) {
4775 case WM_CHAR:
4776 case WM_SYSCHAR:
4777 aResult.mResult = ProcessCharMessage(aMsg, &eventDispatched);
4778 break;
4779
4780 case WM_KEYUP:
4781 case WM_SYSKEYUP:
4782 aResult.mResult = ProcessKeyUpMessage(aMsg, &eventDispatched);
4783 break;
4784
4785 case WM_KEYDOWN:
4786 case WM_SYSKEYDOWN:
4787 aResult.mResult = ProcessKeyDownMessage(aMsg, &eventDispatched);
4788 break;
4789
4790 case WM_DEADCHAR:
4791 case WM_SYSDEADCHAR:
4792
4793 case WM_CUT:
4794 case WM_COPY:
4795 case WM_PASTE:
4796 case WM_CLEAR:
4797 case WM_UNDO:
4798 break;
4799
4800 default:
4801 return false;
4802 }
4803
4804 if (!eventDispatched) {
4805 aResult.mConsumed = nsWindowBase::DispatchPluginEvent(aMsg);
4806 }
4807 if (!Destroyed()) {
4808 DispatchPendingEvents();
4809 }
4810 return true;
4811 }
4812
ForceFontUpdate()4813 static void ForceFontUpdate() {
4814 // update device context font cache
4815 // Dirty but easiest way:
4816 // Changing nsIPrefBranch entry which triggers callbacks
4817 // and flows into calling mDeviceContext->FlushFontCache()
4818 // to update the font cache in all the instance of Browsers
4819 static const char kPrefName[] = "font.internaluseonly.changed";
4820 bool fontInternalChange = Preferences::GetBool(kPrefName, false);
4821 Preferences::SetBool(kPrefName, !fontInternalChange);
4822 }
4823
CleartypeSettingChanged()4824 static bool CleartypeSettingChanged() {
4825 static int currentQuality = -1;
4826 BYTE quality = cairo_win32_get_system_text_quality();
4827
4828 if (currentQuality == quality) return false;
4829
4830 if (currentQuality < 0) {
4831 currentQuality = quality;
4832 return false;
4833 }
4834 currentQuality = quality;
4835 return true;
4836 }
4837
ExternalHandlerProcessMessage(UINT aMessage,WPARAM & aWParam,LPARAM & aLParam,MSGResult & aResult)4838 bool nsWindow::ExternalHandlerProcessMessage(UINT aMessage, WPARAM& aWParam,
4839 LPARAM& aLParam,
4840 MSGResult& aResult) {
4841 if (mWindowHook.Notify(mWnd, aMessage, aWParam, aLParam, aResult)) {
4842 return true;
4843 }
4844
4845 if (IMEHandler::ProcessMessage(this, aMessage, aWParam, aLParam, aResult)) {
4846 return true;
4847 }
4848
4849 if (MouseScrollHandler::ProcessMessage(this, aMessage, aWParam, aLParam,
4850 aResult)) {
4851 return true;
4852 }
4853
4854 if (PluginHasFocus()) {
4855 MSG nativeMsg = WinUtils::InitMSG(aMessage, aWParam, aLParam, mWnd);
4856 if (ProcessMessageForPlugin(nativeMsg, aResult)) {
4857 return true;
4858 }
4859 }
4860
4861 return false;
4862 }
4863
4864 /**
4865 * the _exit() call is not a safe way to terminate your own process on
4866 * Windows, because _exit runs DLL detach callbacks which run static
4867 * destructors for xul.dll.
4868 *
4869 * This method terminates the current process without those issues.
4870 */
ExitThisProcessSafely()4871 static void ExitThisProcessSafely() {
4872 HANDLE process = GetCurrentProcess();
4873 if (TerminateProcess(GetCurrentProcess(), 0)) {
4874 // TerminateProcess is asynchronous, so we wait on our own process handle
4875 WaitForSingleObject(process, INFINITE);
4876 }
4877 MOZ_CRASH("Just in case extremis crash in ExitThisProcessSafely.");
4878 }
4879
4880 // The main windows message processing method.
ProcessMessage(UINT msg,WPARAM & wParam,LPARAM & lParam,LRESULT * aRetValue)4881 bool nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
4882 LRESULT* aRetValue) {
4883 #if defined(EVENT_DEBUG_OUTPUT)
4884 // First param shows all events, second param indicates whether
4885 // to show mouse move events. See nsWindowDbg for details.
4886 PrintEvent(msg, SHOW_REPEAT_EVENTS, SHOW_MOUSEMOVE_EVENTS);
4887 #endif
4888
4889 MSGResult msgResult(aRetValue);
4890 if (ExternalHandlerProcessMessage(msg, wParam, lParam, msgResult)) {
4891 return (msgResult.mConsumed || !mWnd);
4892 }
4893
4894 bool result = false; // call the default nsWindow proc
4895 *aRetValue = 0;
4896
4897 // Glass hit testing w/custom transparent margins
4898 LRESULT dwmHitResult;
4899 if (mCustomNonClient && nsUXThemeData::CheckForCompositor() &&
4900 /* We don't do this for win10 glass with a custom titlebar,
4901 * in order to avoid the caption buttons breaking. */
4902 !(IsWin10OrLater() && HasGlass()) &&
4903 DwmDefWindowProc(mWnd, msg, wParam, lParam, &dwmHitResult)) {
4904 *aRetValue = dwmHitResult;
4905 return true;
4906 }
4907
4908 // (Large blocks of code should be broken out into OnEvent handlers.)
4909 switch (msg) {
4910 // WM_QUERYENDSESSION must be handled by all windows.
4911 // Otherwise Windows thinks the window can just be killed at will.
4912 case WM_QUERYENDSESSION:
4913 if (sCanQuit == TRI_UNKNOWN) {
4914 // Ask if it's ok to quit, and store the answer until we
4915 // get WM_ENDSESSION signaling the round is complete.
4916 nsCOMPtr<nsIObserverService> obsServ =
4917 mozilla::services::GetObserverService();
4918 nsCOMPtr<nsISupportsPRBool> cancelQuit =
4919 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
4920 cancelQuit->SetData(false);
4921 obsServ->NotifyObservers(cancelQuit, "quit-application-requested",
4922 nullptr);
4923
4924 bool abortQuit;
4925 cancelQuit->GetData(&abortQuit);
4926 sCanQuit = abortQuit ? TRI_FALSE : TRI_TRUE;
4927 }
4928 *aRetValue = sCanQuit ? TRUE : FALSE;
4929 result = true;
4930 break;
4931
4932 case MOZ_WM_STARTA11Y:
4933 #if defined(ACCESSIBILITY)
4934 (void*)GetAccessible();
4935 result = true;
4936 #else
4937 result = false;
4938 #endif
4939 break;
4940
4941 case WM_ENDSESSION:
4942 case MOZ_WM_APP_QUIT:
4943 if (msg == MOZ_WM_APP_QUIT || (wParam == TRUE && sCanQuit == TRI_TRUE)) {
4944 // Let's fake a shutdown sequence without actually closing windows etc.
4945 // to avoid Windows killing us in the middle. A proper shutdown would
4946 // require having a chance to pump some messages. Unfortunately
4947 // Windows won't let us do that. Bug 212316.
4948 nsCOMPtr<nsIObserverService> obsServ =
4949 mozilla::services::GetObserverService();
4950 const char16_t* context = u"shutdown-persist";
4951 const char16_t* syncShutdown = u"syncShutdown";
4952 obsServ->NotifyObservers(nullptr, "quit-application-granted",
4953 syncShutdown);
4954 obsServ->NotifyObservers(nullptr, "quit-application-forced", nullptr);
4955 obsServ->NotifyObservers(nullptr, "quit-application", nullptr);
4956 obsServ->NotifyObservers(nullptr, "profile-change-net-teardown",
4957 context);
4958 obsServ->NotifyObservers(nullptr, "profile-change-teardown", context);
4959 obsServ->NotifyObservers(nullptr, "profile-before-change", context);
4960 obsServ->NotifyObservers(nullptr, "profile-before-change-qm", context);
4961 obsServ->NotifyObservers(nullptr, "profile-before-change-telemetry",
4962 context);
4963 ExitThisProcessSafely();
4964 }
4965 sCanQuit = TRI_UNKNOWN;
4966 result = true;
4967 break;
4968
4969 case WM_SYSCOLORCHANGE:
4970 OnSysColorChanged();
4971 break;
4972
4973 case WM_THEMECHANGED: {
4974 // Update non-client margin offsets
4975 UpdateNonClientMargins();
4976 nsUXThemeData::UpdateNativeThemeInfo();
4977
4978 NotifyThemeChanged();
4979
4980 // Invalidate the window so that the repaint will
4981 // pick up the new theme.
4982 Invalidate(true, true, true);
4983 } break;
4984
4985 case WM_WTSSESSION_CHANGE: {
4986 switch (wParam) {
4987 case WTS_CONSOLE_CONNECT:
4988 case WTS_REMOTE_CONNECT:
4989 case WTS_SESSION_UNLOCK:
4990 // When a session becomes visible, we should invalidate.
4991 Invalidate(true, true, true);
4992 break;
4993 default:
4994 break;
4995 }
4996 } break;
4997
4998 case WM_FONTCHANGE: {
4999 // We only handle this message for the hidden window,
5000 // as we only need to update the (global) font list once
5001 // for any given change, not once per window!
5002 if (mWindowType != eWindowType_invisible) {
5003 break;
5004 }
5005
5006 nsresult rv;
5007 bool didChange = false;
5008
5009 // update the global font list
5010 nsCOMPtr<nsIFontEnumerator> fontEnum =
5011 do_GetService("@mozilla.org/gfx/fontenumerator;1", &rv);
5012 if (NS_SUCCEEDED(rv)) {
5013 fontEnum->UpdateFontList(&didChange);
5014 ForceFontUpdate();
5015 } // if (NS_SUCCEEDED(rv))
5016 } break;
5017
5018 case WM_SETTINGCHANGE: {
5019 if (wParam == SPI_SETKEYBOARDDELAY) {
5020 // CaretBlinkTime is cached in nsLookAndFeel
5021 NotifyThemeChanged();
5022 break;
5023 }
5024 if (lParam) {
5025 auto lParamString = reinterpret_cast<const wchar_t*>(lParam);
5026 if (!wcscmp(lParamString, L"ImmersiveColorSet")) {
5027 // WM_SYSCOLORCHANGE is not dispatched for accent color changes
5028 OnSysColorChanged();
5029 break;
5030 }
5031 if (IsWin10OrLater() && mWindowType == eWindowType_invisible) {
5032 if (!wcscmp(lParamString, L"UserInteractionMode")) {
5033 nsCOMPtr<nsIWindowsUIUtils> uiUtils(
5034 do_GetService("@mozilla.org/windows-ui-utils;1"));
5035 if (uiUtils) {
5036 uiUtils->UpdateTabletModeState();
5037 }
5038 }
5039 }
5040 }
5041 } break;
5042
5043 case WM_NCCALCSIZE: {
5044 if (mCustomNonClient) {
5045 // If `wParam` is `FALSE`, `lParam` points to a `RECT` that contains
5046 // the proposed window rectangle for our window. During our
5047 // processing of the `WM_NCCALCSIZE` message, we are expected to
5048 // modify the `RECT` that `lParam` points to, so that its value upon
5049 // our return is the new client area. We must return 0 if `wParam`
5050 // is `FALSE`.
5051 //
5052 // If `wParam` is `TRUE`, `lParam` points to a `NCCALCSIZE_PARAMS`
5053 // struct. This struct contains an array of 3 `RECT`s, the first of
5054 // which has the exact same meaning as the `RECT` that is pointed to
5055 // by `lParam` when `wParam` is `FALSE`. The remaining `RECT`s, in
5056 // conjunction with our return value, can
5057 // be used to specify portions of the source and destination window
5058 // rectangles that are valid and should be preserved. We opt not to
5059 // implement an elaborate client-area preservation technique, and
5060 // simply return 0, which means "preserve the entire old client area
5061 // and align it with the upper-left corner of our new client area".
5062 RECT* clientRect =
5063 wParam ? &(reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam))->rgrc[0]
5064 : (reinterpret_cast<RECT*>(lParam));
5065 double scale = WinUtils::IsPerMonitorDPIAware()
5066 ? WinUtils::LogToPhysFactor(mWnd) /
5067 WinUtils::SystemScaleFactor()
5068 : 1.0;
5069 clientRect->top +=
5070 NSToIntRound((mCaptionHeight - mNonClientOffset.top) * scale);
5071 clientRect->left +=
5072 NSToIntRound((mHorResizeMargin - mNonClientOffset.left) * scale);
5073 clientRect->right -=
5074 NSToIntRound((mHorResizeMargin - mNonClientOffset.right) * scale);
5075 clientRect->bottom -=
5076 NSToIntRound((mVertResizeMargin - mNonClientOffset.bottom) * scale);
5077 // Make client rect's width and height more than 0 to
5078 // avoid problems of webrender and angle.
5079 clientRect->right = std::max(clientRect->right, clientRect->left + 1);
5080 clientRect->bottom = std::max(clientRect->bottom, clientRect->top + 1);
5081
5082 result = true;
5083 *aRetValue = 0;
5084 }
5085 break;
5086 }
5087
5088 case WM_NCHITTEST: {
5089 if (mMouseTransparent) {
5090 // Treat this window as transparent.
5091 *aRetValue = HTTRANSPARENT;
5092 result = true;
5093 break;
5094 }
5095
5096 /*
5097 * If an nc client area margin has been moved, we are responsible
5098 * for calculating where the resize margins are and returning the
5099 * appropriate set of hit test constants. DwmDefWindowProc (above)
5100 * will handle hit testing on it's command buttons if we are on a
5101 * composited desktop.
5102 */
5103
5104 if (!mCustomNonClient) break;
5105
5106 *aRetValue =
5107 ClientMarginHitTestPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
5108 result = true;
5109 break;
5110 }
5111
5112 case WM_SETTEXT:
5113 /*
5114 * WM_SETTEXT paints the titlebar area. Avoid this if we have a
5115 * custom titlebar we paint ourselves, or if we're the ones
5116 * sending the message with an updated title
5117 */
5118
5119 if ((mSendingSetText && nsUXThemeData::CheckForCompositor()) ||
5120 !mCustomNonClient || mNonClientMargins.top == -1)
5121 break;
5122
5123 {
5124 // From msdn, the way around this is to disable the visible state
5125 // temporarily. We need the text to be set but we don't want the
5126 // redraw to occur. However, we need to make sure that we don't
5127 // do this at the same time that a Present is happening.
5128 //
5129 // To do this we take mPresentLock in nsWindow::PreRender and
5130 // if that lock is taken we wait before doing WM_SETTEXT
5131 if (mCompositorWidgetDelegate) {
5132 mCompositorWidgetDelegate->EnterPresentLock();
5133 }
5134 DWORD style = GetWindowLong(mWnd, GWL_STYLE);
5135 SetWindowLong(mWnd, GWL_STYLE, style & ~WS_VISIBLE);
5136 *aRetValue =
5137 CallWindowProcW(GetPrevWindowProc(), mWnd, msg, wParam, lParam);
5138 SetWindowLong(mWnd, GWL_STYLE, style);
5139 if (mCompositorWidgetDelegate) {
5140 mCompositorWidgetDelegate->LeavePresentLock();
5141 }
5142
5143 return true;
5144 }
5145
5146 case WM_NCACTIVATE: {
5147 /*
5148 * WM_NCACTIVATE paints nc areas. Avoid this and re-route painting
5149 * through WM_NCPAINT via InvalidateNonClientRegion.
5150 */
5151 UpdateGetWindowInfoCaptionStatus(FALSE != wParam);
5152
5153 if (!mCustomNonClient) break;
5154
5155 // There is a case that rendered result is not kept. Bug 1237617
5156 if (wParam == TRUE && !gfxEnv::DisableForcePresent() &&
5157 gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
5158 NS_DispatchToMainThread(NewRunnableMethod(
5159 "nsWindow::ForcePresent", this, &nsWindow::ForcePresent));
5160 }
5161
5162 // let the dwm handle nc painting on glass
5163 // Never allow native painting if we are on fullscreen
5164 if (mSizeMode != nsSizeMode_Fullscreen &&
5165 nsUXThemeData::CheckForCompositor())
5166 break;
5167
5168 if (wParam == TRUE) {
5169 // going active
5170 *aRetValue = FALSE; // ignored
5171 result = true;
5172 // invalidate to trigger a paint
5173 InvalidateNonClientRegion();
5174 break;
5175 } else {
5176 // going inactive
5177 *aRetValue = TRUE; // go ahead and deactive
5178 result = true;
5179 // invalidate to trigger a paint
5180 InvalidateNonClientRegion();
5181 break;
5182 }
5183 }
5184
5185 case WM_NCPAINT: {
5186 /*
5187 * Reset the non-client paint region so that it excludes the
5188 * non-client areas we paint manually. Then call defwndproc
5189 * to do the actual painting.
5190 */
5191
5192 if (!mCustomNonClient) break;
5193
5194 // let the dwm handle nc painting on glass
5195 if (nsUXThemeData::CheckForCompositor()) break;
5196
5197 HRGN paintRgn = ExcludeNonClientFromPaintRegion((HRGN)wParam);
5198 LRESULT res = CallWindowProcW(GetPrevWindowProc(), mWnd, msg,
5199 (WPARAM)paintRgn, lParam);
5200 if (paintRgn != (HRGN)wParam) DeleteObject(paintRgn);
5201 *aRetValue = res;
5202 result = true;
5203 } break;
5204
5205 case WM_POWERBROADCAST:
5206 switch (wParam) {
5207 case PBT_APMSUSPEND:
5208 PostSleepWakeNotification(true);
5209 break;
5210 case PBT_APMRESUMEAUTOMATIC:
5211 case PBT_APMRESUMECRITICAL:
5212 case PBT_APMRESUMESUSPEND:
5213 PostSleepWakeNotification(false);
5214 break;
5215 }
5216 break;
5217
5218 case WM_CLOSE: // close request
5219 if (mWidgetListener) mWidgetListener->RequestWindowClose(this);
5220 result = true; // abort window closure
5221 break;
5222
5223 case WM_DESTROY:
5224 // clean up.
5225 DestroyLayerManager();
5226 OnDestroy();
5227 result = true;
5228 break;
5229
5230 case WM_PAINT:
5231 if (CleartypeSettingChanged()) {
5232 ForceFontUpdate();
5233 gfxFontCache* fc = gfxFontCache::GetCache();
5234 if (fc) {
5235 fc->Flush();
5236 }
5237 }
5238 *aRetValue = (int)OnPaint(nullptr, 0);
5239 result = true;
5240 break;
5241
5242 case WM_PRINTCLIENT:
5243 result = OnPaint((HDC)wParam, 0);
5244 break;
5245
5246 case WM_HOTKEY:
5247 result = OnHotKey(wParam, lParam);
5248 break;
5249
5250 case WM_SYSCHAR:
5251 case WM_CHAR: {
5252 MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
5253 result = ProcessCharMessage(nativeMsg, nullptr);
5254 DispatchPendingEvents();
5255 } break;
5256
5257 case WM_SYSKEYUP:
5258 case WM_KEYUP: {
5259 MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
5260 nativeMsg.time = ::GetMessageTime();
5261 result = ProcessKeyUpMessage(nativeMsg, nullptr);
5262 DispatchPendingEvents();
5263 } break;
5264
5265 case WM_SYSKEYDOWN:
5266 case WM_KEYDOWN: {
5267 MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
5268 result = ProcessKeyDownMessage(nativeMsg, nullptr);
5269 DispatchPendingEvents();
5270 } break;
5271
5272 // say we've dealt with erase background if widget does
5273 // not need auto-erasing
5274 case WM_ERASEBKGND:
5275 if (!AutoErase((HDC)wParam)) {
5276 *aRetValue = 1;
5277 result = true;
5278 }
5279 break;
5280
5281 case WM_MOUSEMOVE: {
5282 if (!mMousePresent && !sIsInMouseCapture) {
5283 // First MOUSEMOVE over the client area. Ask for MOUSELEAVE
5284 TRACKMOUSEEVENT mTrack;
5285 mTrack.cbSize = sizeof(TRACKMOUSEEVENT);
5286 mTrack.dwFlags = TME_LEAVE;
5287 mTrack.dwHoverTime = 0;
5288 mTrack.hwndTrack = mWnd;
5289 TrackMouseEvent(&mTrack);
5290 }
5291 mMousePresent = true;
5292
5293 // Suppress dispatch of pending events
5294 // when mouse moves are generated by widget
5295 // creation instead of user input.
5296 LPARAM lParamScreen = lParamToScreen(lParam);
5297 POINT mp;
5298 mp.x = GET_X_LPARAM(lParamScreen);
5299 mp.y = GET_Y_LPARAM(lParamScreen);
5300 bool userMovedMouse = false;
5301 if ((sLastMouseMovePoint.x != mp.x) || (sLastMouseMovePoint.y != mp.y)) {
5302 userMovedMouse = true;
5303 }
5304
5305 result = DispatchMouseEvent(
5306 eMouseMove, wParam, lParam, false, WidgetMouseEvent::eLeftButton,
5307 MOUSE_INPUT_SOURCE(),
5308 mPointerEvents.GetCachedPointerInfo(msg, wParam));
5309 if (userMovedMouse) {
5310 DispatchPendingEvents();
5311 }
5312 } break;
5313
5314 case WM_NCMOUSEMOVE:
5315 // If we receive a mouse move event on non-client chrome, make sure and
5316 // send an eMouseExitFromWidget event as well.
5317 if (mMousePresent && !sIsInMouseCapture)
5318 SendMessage(mWnd, WM_MOUSELEAVE, 0, 0);
5319 break;
5320
5321 case WM_LBUTTONDOWN: {
5322 result = DispatchMouseEvent(
5323 eMouseDown, wParam, lParam, false, WidgetMouseEvent::eLeftButton,
5324 MOUSE_INPUT_SOURCE(),
5325 mPointerEvents.GetCachedPointerInfo(msg, wParam));
5326 DispatchPendingEvents();
5327 } break;
5328
5329 case WM_LBUTTONUP: {
5330 result = DispatchMouseEvent(
5331 eMouseUp, wParam, lParam, false, WidgetMouseEvent::eLeftButton,
5332 MOUSE_INPUT_SOURCE(),
5333 mPointerEvents.GetCachedPointerInfo(msg, wParam));
5334 DispatchPendingEvents();
5335 } break;
5336
5337 case WM_MOUSELEAVE: {
5338 if (!mMousePresent) break;
5339 mMousePresent = false;
5340
5341 // Check if the mouse is over the fullscreen transition window, if so
5342 // clear sLastMouseMovePoint. This way the WM_MOUSEMOVE we get after the
5343 // transition window disappears will not be ignored, even if the mouse
5344 // hasn't moved.
5345 if (mTransitionWnd && WindowAtMouse() == mTransitionWnd) {
5346 sLastMouseMovePoint = {0};
5347 }
5348
5349 // We need to check mouse button states and put them in for
5350 // wParam.
5351 WPARAM mouseState = (GetKeyState(VK_LBUTTON) ? MK_LBUTTON : 0) |
5352 (GetKeyState(VK_MBUTTON) ? MK_MBUTTON : 0) |
5353 (GetKeyState(VK_RBUTTON) ? MK_RBUTTON : 0);
5354 // Synthesize an event position because we don't get one from
5355 // WM_MOUSELEAVE.
5356 LPARAM pos = lParamToClient(::GetMessagePos());
5357 DispatchMouseEvent(eMouseExitFromWidget, mouseState, pos, false,
5358 WidgetMouseEvent::eLeftButton, MOUSE_INPUT_SOURCE());
5359 } break;
5360
5361 case MOZ_WM_PEN_LEAVES_HOVER_OF_DIGITIZER: {
5362 LPARAM pos = lParamToClient(::GetMessagePos());
5363 MOZ_ASSERT(InkCollector::sInkCollector);
5364 uint16_t pointerId = InkCollector::sInkCollector->GetPointerId();
5365 if (pointerId != 0) {
5366 WinPointerInfo pointerInfo;
5367 pointerInfo.pointerId = pointerId;
5368 DispatchMouseEvent(eMouseExitFromWidget, wParam, pos, false,
5369 WidgetMouseEvent::eLeftButton,
5370 nsIDOMMouseEvent::MOZ_SOURCE_PEN, &pointerInfo);
5371 InkCollector::sInkCollector->ClearTarget();
5372 InkCollector::sInkCollector->ClearPointerId();
5373 }
5374 } break;
5375
5376 case WM_CONTEXTMENU: {
5377 // If the context menu is brought up by a touch long-press, then
5378 // the APZ code is responsible for dealing with this, so we don't
5379 // need to do anything.
5380 if (mTouchWindow &&
5381 MOUSE_INPUT_SOURCE() == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
5382 MOZ_ASSERT(mAPZC); // since mTouchWindow is true, APZ must be enabled
5383 result = true;
5384 break;
5385 }
5386
5387 // if the context menu is brought up from the keyboard, |lParam|
5388 // will be -1.
5389 LPARAM pos;
5390 bool contextMenukey = false;
5391 if (lParam == -1) {
5392 contextMenukey = true;
5393 pos = lParamToClient(GetMessagePos());
5394 } else {
5395 pos = lParamToClient(lParam);
5396 }
5397
5398 result =
5399 DispatchMouseEvent(eContextMenu, wParam, pos, contextMenukey,
5400 contextMenukey ? WidgetMouseEvent::eLeftButton
5401 : WidgetMouseEvent::eRightButton,
5402 MOUSE_INPUT_SOURCE());
5403 if (lParam != -1 && !result && mCustomNonClient &&
5404 mDraggableRegion.Contains(GET_X_LPARAM(pos), GET_Y_LPARAM(pos))) {
5405 // Blank area hit, throw up the system menu.
5406 DisplaySystemMenu(mWnd, mSizeMode, mIsRTL, GET_X_LPARAM(lParam),
5407 GET_Y_LPARAM(lParam));
5408 result = true;
5409 }
5410 } break;
5411
5412 case WM_POINTERLEAVE:
5413 case WM_POINTERDOWN:
5414 case WM_POINTERUP:
5415 case WM_POINTERUPDATE:
5416 result = OnPointerEvents(msg, wParam, lParam);
5417 if (result) {
5418 DispatchPendingEvents();
5419 }
5420 break;
5421
5422 case WM_LBUTTONDBLCLK:
5423 result = DispatchMouseEvent(eMouseDoubleClick, wParam, lParam, false,
5424 WidgetMouseEvent::eLeftButton,
5425 MOUSE_INPUT_SOURCE());
5426 DispatchPendingEvents();
5427 break;
5428
5429 case WM_MBUTTONDOWN:
5430 result = DispatchMouseEvent(eMouseDown, wParam, lParam, false,
5431 WidgetMouseEvent::eMiddleButton,
5432 MOUSE_INPUT_SOURCE());
5433 DispatchPendingEvents();
5434 break;
5435
5436 case WM_MBUTTONUP:
5437 result = DispatchMouseEvent(eMouseUp, wParam, lParam, false,
5438 WidgetMouseEvent::eMiddleButton,
5439 MOUSE_INPUT_SOURCE());
5440 DispatchPendingEvents();
5441 break;
5442
5443 case WM_MBUTTONDBLCLK:
5444 result = DispatchMouseEvent(eMouseDoubleClick, wParam, lParam, false,
5445 WidgetMouseEvent::eMiddleButton,
5446 MOUSE_INPUT_SOURCE());
5447 DispatchPendingEvents();
5448 break;
5449
5450 case WM_NCMBUTTONDOWN:
5451 result = DispatchMouseEvent(eMouseDown, 0, lParamToClient(lParam), false,
5452 WidgetMouseEvent::eMiddleButton,
5453 MOUSE_INPUT_SOURCE());
5454 DispatchPendingEvents();
5455 break;
5456
5457 case WM_NCMBUTTONUP:
5458 result = DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam), false,
5459 WidgetMouseEvent::eMiddleButton,
5460 MOUSE_INPUT_SOURCE());
5461 DispatchPendingEvents();
5462 break;
5463
5464 case WM_NCMBUTTONDBLCLK:
5465 result = DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam),
5466 false, WidgetMouseEvent::eMiddleButton,
5467 MOUSE_INPUT_SOURCE());
5468 DispatchPendingEvents();
5469 break;
5470
5471 case WM_RBUTTONDOWN:
5472 result = DispatchMouseEvent(
5473 eMouseDown, wParam, lParam, false, WidgetMouseEvent::eRightButton,
5474 MOUSE_INPUT_SOURCE(),
5475 mPointerEvents.GetCachedPointerInfo(msg, wParam));
5476 DispatchPendingEvents();
5477 break;
5478
5479 case WM_RBUTTONUP:
5480 result = DispatchMouseEvent(
5481 eMouseUp, wParam, lParam, false, WidgetMouseEvent::eRightButton,
5482 MOUSE_INPUT_SOURCE(),
5483 mPointerEvents.GetCachedPointerInfo(msg, wParam));
5484 DispatchPendingEvents();
5485 break;
5486
5487 case WM_RBUTTONDBLCLK:
5488 result = DispatchMouseEvent(eMouseDoubleClick, wParam, lParam, false,
5489 WidgetMouseEvent::eRightButton,
5490 MOUSE_INPUT_SOURCE());
5491 DispatchPendingEvents();
5492 break;
5493
5494 case WM_NCRBUTTONDOWN:
5495 result = DispatchMouseEvent(eMouseDown, 0, lParamToClient(lParam), false,
5496 WidgetMouseEvent::eRightButton,
5497 MOUSE_INPUT_SOURCE());
5498 DispatchPendingEvents();
5499 break;
5500
5501 case WM_NCRBUTTONUP:
5502 result = DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam), false,
5503 WidgetMouseEvent::eRightButton,
5504 MOUSE_INPUT_SOURCE());
5505 DispatchPendingEvents();
5506 break;
5507
5508 case WM_NCRBUTTONDBLCLK:
5509 result = DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam),
5510 false, WidgetMouseEvent::eRightButton,
5511 MOUSE_INPUT_SOURCE());
5512 DispatchPendingEvents();
5513 break;
5514
5515 // Windows doesn't provide to customize the behavior of 4th nor 5th button
5516 // of mouse. If 5-button mouse works with standard mouse deriver of
5517 // Windows, users cannot disable 4th button (browser back) nor 5th button
5518 // (browser forward). We should allow to do it with our prefs since we can
5519 // prevent Windows to generate WM_APPCOMMAND message if WM_XBUTTONUP
5520 // messages are not sent to DefWindowProc.
5521 case WM_XBUTTONDOWN:
5522 case WM_XBUTTONUP:
5523 case WM_NCXBUTTONDOWN:
5524 case WM_NCXBUTTONUP:
5525 *aRetValue = TRUE;
5526 switch (GET_XBUTTON_WPARAM(wParam)) {
5527 case XBUTTON1:
5528 result = !Preferences::GetBool("mousebutton.4th.enabled", true);
5529 break;
5530 case XBUTTON2:
5531 result = !Preferences::GetBool("mousebutton.5th.enabled", true);
5532 break;
5533 default:
5534 break;
5535 }
5536 break;
5537
5538 case WM_SIZING: {
5539 // When we get WM_ENTERSIZEMOVE we don't know yet if we're in a live
5540 // resize or move event. Instead we wait for first VM_SIZING message
5541 // within a ENTERSIZEMOVE to consider this a live resize event.
5542 if (mResizeState == IN_SIZEMOVE) {
5543 mResizeState = RESIZING;
5544 NotifyLiveResizeStarted();
5545 }
5546 break;
5547 }
5548
5549 case WM_MOVING:
5550 FinishLiveResizing(MOVING);
5551 if (WinUtils::IsPerMonitorDPIAware()) {
5552 // Sometimes, we appear to miss a WM_DPICHANGED message while moving
5553 // a window around. Therefore, call ChangedDPI and ResetLayout here
5554 // if it appears that the window's scaling is not what we expect.
5555 // This causes the prescontext and appshell window management code to
5556 // check the appUnitsPerDevPixel value and current widget size, and
5557 // refresh them if necessary. If nothing has changed, these calls will
5558 // return without actually triggering any extra reflow or painting.
5559 if (WinUtils::LogToPhysFactor(mWnd) != mDefaultScale) {
5560 ChangedDPI();
5561 ResetLayout();
5562 }
5563 }
5564 break;
5565
5566 case WM_ENTERSIZEMOVE: {
5567 if (mResizeState == NOT_RESIZING) {
5568 mResizeState = IN_SIZEMOVE;
5569 }
5570 break;
5571 }
5572
5573 case WM_EXITSIZEMOVE: {
5574 FinishLiveResizing(NOT_RESIZING);
5575
5576 if (!sIsInMouseCapture) {
5577 NotifySizeMoveDone();
5578 }
5579
5580 break;
5581 }
5582
5583 case WM_DISPLAYCHANGE: {
5584 ScreenHelperWin::RefreshScreens();
5585 break;
5586 }
5587
5588 case WM_NCLBUTTONDBLCLK:
5589 DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam), false,
5590 WidgetMouseEvent::eLeftButton, MOUSE_INPUT_SOURCE());
5591 result = DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam), false,
5592 WidgetMouseEvent::eLeftButton,
5593 MOUSE_INPUT_SOURCE());
5594 DispatchPendingEvents();
5595 break;
5596
5597 case WM_APPCOMMAND: {
5598 MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
5599 result = HandleAppCommandMsg(nativeMsg, aRetValue);
5600 break;
5601 }
5602
5603 // The WM_ACTIVATE event is fired when a window is raised or lowered,
5604 // and the loword of wParam specifies which. But we don't want to tell
5605 // the focus system about this until the WM_SETFOCUS or WM_KILLFOCUS
5606 // events are fired. Instead, set either the sJustGotActivate or
5607 // gJustGotDeactivate flags and activate/deactivate once the focus
5608 // events arrive.
5609 case WM_ACTIVATE:
5610 if (mWidgetListener) {
5611 int32_t fActive = LOWORD(wParam);
5612
5613 if (WA_INACTIVE == fActive) {
5614 // when minimizing a window, the deactivation and focus events will
5615 // be fired in the reverse order. Instead, just deactivate right away.
5616 if (HIWORD(wParam))
5617 DispatchFocusToTopLevelWindow(false);
5618 else
5619 sJustGotDeactivate = true;
5620
5621 if (mIsTopWidgetWindow)
5622 mLastKeyboardLayout = KeyboardLayout::GetInstance()->GetLayout();
5623
5624 } else {
5625 StopFlashing();
5626
5627 sJustGotActivate = true;
5628 WidgetMouseEvent event(true, eMouseActivate, this,
5629 WidgetMouseEvent::eReal);
5630 InitEvent(event);
5631 ModifierKeyState modifierKeyState;
5632 modifierKeyState.InitInputEvent(event);
5633 DispatchInputEvent(&event);
5634 if (sSwitchKeyboardLayout && mLastKeyboardLayout)
5635 ActivateKeyboardLayout(mLastKeyboardLayout, 0);
5636 }
5637 }
5638 break;
5639
5640 case WM_MOUSEACTIVATE:
5641 // A popup with a parent owner should not be activated when clicked but
5642 // should still allow the mouse event to be fired, so the return value
5643 // is set to MA_NOACTIVATE. But if the owner isn't the frontmost window,
5644 // just use default processing so that the window is activated.
5645 if (IsPopup() && IsOwnerForegroundWindow()) {
5646 *aRetValue = MA_NOACTIVATE;
5647 result = true;
5648 }
5649 break;
5650
5651 case WM_WINDOWPOSCHANGING: {
5652 LPWINDOWPOS info = (LPWINDOWPOS)lParam;
5653 OnWindowPosChanging(info);
5654 result = true;
5655 } break;
5656
5657 case WM_GETMINMAXINFO: {
5658 MINMAXINFO* mmi = (MINMAXINFO*)lParam;
5659 // Set the constraints. The minimum size should also be constrained to the
5660 // default window maximum size so that it fits on screen.
5661 mmi->ptMinTrackSize.x =
5662 std::min((int32_t)mmi->ptMaxTrackSize.x,
5663 std::max((int32_t)mmi->ptMinTrackSize.x,
5664 mSizeConstraints.mMinSize.width));
5665 mmi->ptMinTrackSize.y =
5666 std::min((int32_t)mmi->ptMaxTrackSize.y,
5667 std::max((int32_t)mmi->ptMinTrackSize.y,
5668 mSizeConstraints.mMinSize.height));
5669 mmi->ptMaxTrackSize.x = std::min((int32_t)mmi->ptMaxTrackSize.x,
5670 mSizeConstraints.mMaxSize.width);
5671 mmi->ptMaxTrackSize.y = std::min((int32_t)mmi->ptMaxTrackSize.y,
5672 mSizeConstraints.mMaxSize.height);
5673 } break;
5674
5675 case WM_SETFOCUS:
5676 // If previous focused window isn't ours, it must have received the
5677 // redirected message. So, we should forget it.
5678 if (!WinUtils::IsOurProcessWindow(HWND(wParam))) {
5679 RedirectedKeyDownMessageManager::Forget();
5680 }
5681 if (sJustGotActivate) {
5682 DispatchFocusToTopLevelWindow(true);
5683 }
5684 break;
5685
5686 case WM_KILLFOCUS:
5687 if (sJustGotDeactivate) {
5688 DispatchFocusToTopLevelWindow(false);
5689 }
5690 break;
5691
5692 case WM_WINDOWPOSCHANGED: {
5693 WINDOWPOS* wp = (LPWINDOWPOS)lParam;
5694 OnWindowPosChanged(wp);
5695 result = true;
5696 } break;
5697
5698 case WM_INPUTLANGCHANGEREQUEST:
5699 *aRetValue = TRUE;
5700 result = false;
5701 break;
5702
5703 case WM_INPUTLANGCHANGE:
5704 KeyboardLayout::GetInstance()->OnLayoutChange(
5705 reinterpret_cast<HKL>(lParam));
5706 nsBidiKeyboard::OnLayoutChange();
5707 result = false; // always pass to child window
5708 break;
5709
5710 case WM_DESTROYCLIPBOARD: {
5711 nsIClipboard* clipboard;
5712 nsresult rv = CallGetService(kCClipboardCID, &clipboard);
5713 if (NS_SUCCEEDED(rv)) {
5714 clipboard->EmptyClipboard(nsIClipboard::kGlobalClipboard);
5715 NS_RELEASE(clipboard);
5716 }
5717 } break;
5718
5719 #ifdef ACCESSIBILITY
5720 case WM_GETOBJECT: {
5721 *aRetValue = 0;
5722 // Do explicit casting to make it working on 64bit systems (see bug 649236
5723 // for details).
5724 int32_t objId = static_cast<DWORD>(lParam);
5725 if (objId == OBJID_CLIENT) { // oleacc.dll will be loaded dynamically
5726 RefPtr<IAccessible> root(
5727 a11y::LazyInstantiator::GetRootAccessible(mWnd));
5728 if (root) {
5729 *aRetValue = LresultFromObject(IID_IAccessible, wParam, root);
5730 a11y::LazyInstantiator::EnableBlindAggregation(mWnd);
5731 result = true;
5732 }
5733 }
5734 } break;
5735 #endif
5736
5737 case WM_SYSCOMMAND: {
5738 WPARAM filteredWParam = (wParam & 0xFFF0);
5739 if (mSizeMode == nsSizeMode_Fullscreen && filteredWParam == SC_RESTORE &&
5740 GetCurrentShowCmd(mWnd) != SW_SHOWMINIMIZED) {
5741 MakeFullScreen(false);
5742 result = true;
5743 }
5744
5745 // Handle the system menu manually when we're in full screen mode
5746 // so we can set the appropriate options.
5747 if (filteredWParam == SC_KEYMENU && lParam == VK_SPACE &&
5748 mSizeMode == nsSizeMode_Fullscreen) {
5749 DisplaySystemMenu(mWnd, mSizeMode, mIsRTL, MOZ_SYSCONTEXT_X_POS,
5750 MOZ_SYSCONTEXT_Y_POS);
5751 result = true;
5752 }
5753 } break;
5754
5755 case WM_DWMCOMPOSITIONCHANGED:
5756 // First, update the compositor state to latest one. All other methods
5757 // should use same state as here for consistency painting.
5758 nsUXThemeData::CheckForCompositor(true);
5759
5760 UpdateNonClientMargins();
5761 BroadcastMsg(mWnd, WM_DWMCOMPOSITIONCHANGED);
5762 NotifyThemeChanged();
5763 UpdateGlass();
5764 Invalidate(true, true, true);
5765 break;
5766
5767 case WM_DPICHANGED: {
5768 LPRECT rect = (LPRECT)lParam;
5769 OnDPIChanged(rect->left, rect->top, rect->right - rect->left,
5770 rect->bottom - rect->top);
5771 break;
5772 }
5773
5774 case WM_UPDATEUISTATE: {
5775 // If the UI state has changed, fire an event so the UI updates the
5776 // keyboard cues based on the system setting and how the window was
5777 // opened. For example, a dialog opened via a keyboard press on a button
5778 // should enable cues, whereas the same dialog opened via a mouse click of
5779 // the button should not.
5780 if (mWindowType == eWindowType_toplevel ||
5781 mWindowType == eWindowType_dialog) {
5782 int32_t action = LOWORD(wParam);
5783 if (action == UIS_SET || action == UIS_CLEAR) {
5784 int32_t flags = HIWORD(wParam);
5785 UIStateChangeType showAccelerators = UIStateChangeType_NoChange;
5786 UIStateChangeType showFocusRings = UIStateChangeType_NoChange;
5787 if (flags & UISF_HIDEACCEL)
5788 showAccelerators = (action == UIS_SET) ? UIStateChangeType_Clear
5789 : UIStateChangeType_Set;
5790 if (flags & UISF_HIDEFOCUS)
5791 showFocusRings = (action == UIS_SET) ? UIStateChangeType_Clear
5792 : UIStateChangeType_Set;
5793
5794 NotifyUIStateChanged(showAccelerators, showFocusRings);
5795 }
5796 }
5797
5798 break;
5799 }
5800
5801 /* Gesture support events */
5802 case WM_TABLET_QUERYSYSTEMGESTURESTATUS:
5803 // According to MS samples, this must be handled to enable
5804 // rotational support in multi-touch drivers.
5805 result = true;
5806 *aRetValue = TABLET_ROTATE_GESTURE_ENABLE;
5807 break;
5808
5809 case WM_TOUCH:
5810 result = OnTouch(wParam, lParam);
5811 if (result) {
5812 *aRetValue = 0;
5813 }
5814 break;
5815
5816 case WM_GESTURE:
5817 result = OnGesture(wParam, lParam);
5818 break;
5819
5820 case WM_GESTURENOTIFY: {
5821 if (mWindowType != eWindowType_invisible && !IsPlugin()) {
5822 // A GestureNotify event is dispatched to decide which single-finger
5823 // panning direction should be active (including none) and if pan
5824 // feedback should be displayed. Java and plugin windows can make their
5825 // own calls.
5826
5827 GESTURENOTIFYSTRUCT* gestureinfo = (GESTURENOTIFYSTRUCT*)lParam;
5828 nsPointWin touchPoint;
5829 touchPoint = gestureinfo->ptsLocation;
5830 touchPoint.ScreenToClient(mWnd);
5831 WidgetGestureNotifyEvent gestureNotifyEvent(true, eGestureNotify, this);
5832 gestureNotifyEvent.mRefPoint =
5833 LayoutDeviceIntPoint::FromUnknownPoint(touchPoint);
5834 nsEventStatus status;
5835 DispatchEvent(&gestureNotifyEvent, status);
5836 mDisplayPanFeedback = gestureNotifyEvent.mDisplayPanFeedback;
5837 if (!mTouchWindow)
5838 mGesture.SetWinGestureSupport(mWnd, gestureNotifyEvent.mPanDirection);
5839 }
5840 result = false; // should always bubble to DefWindowProc
5841 } break;
5842
5843 case WM_CLEAR: {
5844 WidgetContentCommandEvent command(true, eContentCommandDelete, this);
5845 DispatchWindowEvent(&command);
5846 result = true;
5847 } break;
5848
5849 case WM_CUT: {
5850 WidgetContentCommandEvent command(true, eContentCommandCut, this);
5851 DispatchWindowEvent(&command);
5852 result = true;
5853 } break;
5854
5855 case WM_COPY: {
5856 WidgetContentCommandEvent command(true, eContentCommandCopy, this);
5857 DispatchWindowEvent(&command);
5858 result = true;
5859 } break;
5860
5861 case WM_PASTE: {
5862 WidgetContentCommandEvent command(true, eContentCommandPaste, this);
5863 DispatchWindowEvent(&command);
5864 result = true;
5865 } break;
5866
5867 case EM_UNDO: {
5868 WidgetContentCommandEvent command(true, eContentCommandUndo, this);
5869 DispatchWindowEvent(&command);
5870 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
5871 result = true;
5872 } break;
5873
5874 case EM_REDO: {
5875 WidgetContentCommandEvent command(true, eContentCommandRedo, this);
5876 DispatchWindowEvent(&command);
5877 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
5878 result = true;
5879 } break;
5880
5881 case EM_CANPASTE: {
5882 // Support EM_CANPASTE message only when wParam isn't specified or
5883 // is plain text format.
5884 if (wParam == 0 || wParam == CF_TEXT || wParam == CF_UNICODETEXT) {
5885 WidgetContentCommandEvent command(true, eContentCommandPaste, this,
5886 true);
5887 DispatchWindowEvent(&command);
5888 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
5889 result = true;
5890 }
5891 } break;
5892
5893 case EM_CANUNDO: {
5894 WidgetContentCommandEvent command(true, eContentCommandUndo, this, true);
5895 DispatchWindowEvent(&command);
5896 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
5897 result = true;
5898 } break;
5899
5900 case EM_CANREDO: {
5901 WidgetContentCommandEvent command(true, eContentCommandRedo, this, true);
5902 DispatchWindowEvent(&command);
5903 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
5904 result = true;
5905 } break;
5906
5907 case MOZ_WM_SKEWFIX: {
5908 TimeStamp skewStamp;
5909 if (CurrentWindowsTimeGetter::GetAndClearBackwardsSkewStamp(wParam,
5910 &skewStamp)) {
5911 TimeConverter().CompensateForBackwardsSkew(::GetMessageTime(),
5912 skewStamp);
5913 }
5914 } break;
5915
5916 default: {
5917 if (msg == nsAppShell::GetTaskbarButtonCreatedMessage()) {
5918 SetHasTaskbarIconBeenCreated();
5919 }
5920 } break;
5921 }
5922
5923 //*aRetValue = result;
5924 if (mWnd) {
5925 return result;
5926 } else {
5927 // Events which caused mWnd destruction and aren't consumed
5928 // will crash during the Windows default processing.
5929 return true;
5930 }
5931 }
5932
FinishLiveResizing(ResizeState aNewState)5933 void nsWindow::FinishLiveResizing(ResizeState aNewState) {
5934 if (mResizeState == RESIZING) {
5935 NotifyLiveResizeStopped();
5936 }
5937 mResizeState = aNewState;
5938 ForcePresent();
5939 }
5940
5941 /**************************************************************
5942 *
5943 * SECTION: Broadcast messaging
5944 *
5945 * Broadcast messages to all windows.
5946 *
5947 **************************************************************/
5948
5949 // Enumerate all child windows sending aMsg to each of them
BroadcastMsgToChildren(HWND aWnd,LPARAM aMsg)5950 BOOL CALLBACK nsWindow::BroadcastMsgToChildren(HWND aWnd, LPARAM aMsg) {
5951 WNDPROC winProc = (WNDPROC)::GetWindowLongPtrW(aWnd, GWLP_WNDPROC);
5952 if (winProc == &nsWindow::WindowProc) {
5953 // it's one of our windows so go ahead and send a message to it
5954 ::CallWindowProcW(winProc, aWnd, aMsg, 0, 0);
5955 }
5956 return TRUE;
5957 }
5958
5959 // Enumerate all top level windows specifying that the children of each
5960 // top level window should be enumerated. Do *not* send the message to
5961 // each top level window since it is assumed that the toolkit will send
5962 // aMsg to them directly.
BroadcastMsg(HWND aTopWindow,LPARAM aMsg)5963 BOOL CALLBACK nsWindow::BroadcastMsg(HWND aTopWindow, LPARAM aMsg) {
5964 // Iterate each of aTopWindows child windows sending the aMsg
5965 // to each of them.
5966 ::EnumChildWindows(aTopWindow, nsWindow::BroadcastMsgToChildren, aMsg);
5967 return TRUE;
5968 }
5969
5970 /**************************************************************
5971 *
5972 * SECTION: Event processing helpers
5973 *
5974 * Special processing for certain event types and
5975 * synthesized events.
5976 *
5977 **************************************************************/
5978
ClientMarginHitTestPoint(int32_t mx,int32_t my)5979 int32_t nsWindow::ClientMarginHitTestPoint(int32_t mx, int32_t my) {
5980 if (mSizeMode == nsSizeMode_Minimized || mSizeMode == nsSizeMode_Fullscreen) {
5981 return HTCLIENT;
5982 }
5983
5984 // Calculations are done in screen coords
5985 RECT winRect;
5986 GetWindowRect(mWnd, &winRect);
5987
5988 // hit return constants:
5989 // HTBORDER - non-resizable border
5990 // HTBOTTOM, HTLEFT, HTRIGHT, HTTOP - resizable border
5991 // HTBOTTOMLEFT, HTBOTTOMRIGHT - resizable corner
5992 // HTTOPLEFT, HTTOPRIGHT - resizable corner
5993 // HTCAPTION - general title bar area
5994 // HTCLIENT - area considered the client
5995 // HTCLOSE - hovering over the close button
5996 // HTMAXBUTTON - maximize button
5997 // HTMINBUTTON - minimize button
5998
5999 int32_t testResult = HTCLIENT;
6000
6001 bool isResizable = (mBorderStyle & (eBorderStyle_all | eBorderStyle_resizeh |
6002 eBorderStyle_default)) > 0
6003 ? true
6004 : false;
6005 if (mSizeMode == nsSizeMode_Maximized) isResizable = false;
6006
6007 // Ensure being accessible to borders of window. Even if contents are in
6008 // this area, the area must behave as border.
6009 nsIntMargin nonClientSize(
6010 std::max(mCaptionHeight - mNonClientOffset.top, kResizableBorderMinSize),
6011 std::max(mHorResizeMargin - mNonClientOffset.right,
6012 kResizableBorderMinSize),
6013 std::max(mVertResizeMargin - mNonClientOffset.bottom,
6014 kResizableBorderMinSize),
6015 std::max(mHorResizeMargin - mNonClientOffset.left,
6016 kResizableBorderMinSize));
6017
6018 bool allowContentOverride = mSizeMode == nsSizeMode_Maximized ||
6019 (mx >= winRect.left + nonClientSize.left &&
6020 mx <= winRect.right - nonClientSize.right &&
6021 my >= winRect.top + nonClientSize.top &&
6022 my <= winRect.bottom - nonClientSize.bottom);
6023
6024 // The border size. If there is no content under mouse cursor, the border
6025 // size should be larger than the values in system settings. Otherwise,
6026 // contents under the mouse cursor should be able to override the behavior.
6027 // E.g., user must expect that Firefox button always opens the popup menu
6028 // even when the user clicks on the above edge of it.
6029 nsIntMargin borderSize(std::max(nonClientSize.top, mVertResizeMargin),
6030 std::max(nonClientSize.right, mHorResizeMargin),
6031 std::max(nonClientSize.bottom, mVertResizeMargin),
6032 std::max(nonClientSize.left, mHorResizeMargin));
6033
6034 bool top = false;
6035 bool bottom = false;
6036 bool left = false;
6037 bool right = false;
6038
6039 if (my >= winRect.top && my < winRect.top + borderSize.top) {
6040 top = true;
6041 } else if (my <= winRect.bottom && my > winRect.bottom - borderSize.bottom) {
6042 bottom = true;
6043 }
6044
6045 // (the 2x case here doubles the resize area for corners)
6046 int multiplier = (top || bottom) ? 2 : 1;
6047 if (mx >= winRect.left &&
6048 mx < winRect.left + (multiplier * borderSize.left)) {
6049 left = true;
6050 } else if (mx <= winRect.right &&
6051 mx > winRect.right - (multiplier * borderSize.right)) {
6052 right = true;
6053 }
6054
6055 if (isResizable) {
6056 if (top) {
6057 testResult = HTTOP;
6058 if (left)
6059 testResult = HTTOPLEFT;
6060 else if (right)
6061 testResult = HTTOPRIGHT;
6062 } else if (bottom) {
6063 testResult = HTBOTTOM;
6064 if (left)
6065 testResult = HTBOTTOMLEFT;
6066 else if (right)
6067 testResult = HTBOTTOMRIGHT;
6068 } else {
6069 if (left) testResult = HTLEFT;
6070 if (right) testResult = HTRIGHT;
6071 }
6072 } else {
6073 if (top)
6074 testResult = HTCAPTION;
6075 else if (bottom || left || right)
6076 testResult = HTBORDER;
6077 }
6078
6079 if (!sIsInMouseCapture && allowContentOverride) {
6080 POINT pt = {mx, my};
6081 ::ScreenToClient(mWnd, &pt);
6082 if (pt.x == mCachedHitTestPoint.x && pt.y == mCachedHitTestPoint.y &&
6083 TimeStamp::Now() - mCachedHitTestTime <
6084 TimeDuration::FromMilliseconds(HITTEST_CACHE_LIFETIME_MS)) {
6085 return mCachedHitTestResult;
6086 }
6087 if (mDraggableRegion.Contains(pt.x, pt.y)) {
6088 testResult = HTCAPTION;
6089 } else {
6090 testResult = HTCLIENT;
6091 }
6092 mCachedHitTestPoint = pt;
6093 mCachedHitTestTime = TimeStamp::Now();
6094 mCachedHitTestResult = testResult;
6095 }
6096
6097 return testResult;
6098 }
6099
GetMessageTimeStamp(LONG aEventTime) const6100 TimeStamp nsWindow::GetMessageTimeStamp(LONG aEventTime) const {
6101 CurrentWindowsTimeGetter getCurrentTime(mWnd);
6102 return TimeConverter().GetTimeStampFromSystemTime(aEventTime, getCurrentTime);
6103 }
6104
PostSleepWakeNotification(const bool aIsSleepMode)6105 void nsWindow::PostSleepWakeNotification(const bool aIsSleepMode) {
6106 if (aIsSleepMode == gIsSleepMode) return;
6107
6108 gIsSleepMode = aIsSleepMode;
6109
6110 nsCOMPtr<nsIObserverService> observerService =
6111 mozilla::services::GetObserverService();
6112 if (observerService)
6113 observerService->NotifyObservers(nullptr,
6114 aIsSleepMode
6115 ? NS_WIDGET_SLEEP_OBSERVER_TOPIC
6116 : NS_WIDGET_WAKE_OBSERVER_TOPIC,
6117 nullptr);
6118 }
6119
ProcessCharMessage(const MSG & aMsg,bool * aEventDispatched)6120 LRESULT nsWindow::ProcessCharMessage(const MSG& aMsg, bool* aEventDispatched) {
6121 if (IMEHandler::IsComposingOn(this)) {
6122 IMEHandler::NotifyIME(this, REQUEST_TO_COMMIT_COMPOSITION);
6123 }
6124 // These must be checked here too as a lone WM_CHAR could be received
6125 // if a child window didn't handle it (for example Alt+Space in a content
6126 // window)
6127 ModifierKeyState modKeyState;
6128 NativeKey nativeKey(this, aMsg, modKeyState);
6129 return static_cast<LRESULT>(nativeKey.HandleCharMessage(aEventDispatched));
6130 }
6131
ProcessKeyUpMessage(const MSG & aMsg,bool * aEventDispatched)6132 LRESULT nsWindow::ProcessKeyUpMessage(const MSG& aMsg, bool* aEventDispatched) {
6133 ModifierKeyState modKeyState;
6134 NativeKey nativeKey(this, aMsg, modKeyState);
6135 bool result = nativeKey.HandleKeyUpMessage(aEventDispatched);
6136 if (aMsg.wParam == VK_F10) {
6137 // Bug 1382199: Windows default behavior will trigger the System menu bar
6138 // when F10 is released. Among other things, this causes the System menu bar
6139 // to appear when a web page overrides the contextmenu event. We *never*
6140 // want this default behavior, so eat this key (never pass it to Windows).
6141 return true;
6142 }
6143 return result;
6144 }
6145
ProcessKeyDownMessage(const MSG & aMsg,bool * aEventDispatched)6146 LRESULT nsWindow::ProcessKeyDownMessage(const MSG& aMsg,
6147 bool* aEventDispatched) {
6148 // If this method doesn't call NativeKey::HandleKeyDownMessage(), this method
6149 // must clean up the redirected message information itself. For more
6150 // information, see above comment of
6151 // RedirectedKeyDownMessageManager::AutoFlusher class definition in
6152 // KeyboardLayout.h.
6153 RedirectedKeyDownMessageManager::AutoFlusher redirectedMsgFlusher(this, aMsg);
6154
6155 ModifierKeyState modKeyState;
6156
6157 NativeKey nativeKey(this, aMsg, modKeyState);
6158 LRESULT result =
6159 static_cast<LRESULT>(nativeKey.HandleKeyDownMessage(aEventDispatched));
6160 // HandleKeyDownMessage cleaned up the redirected message information
6161 // itself, so, we should do nothing.
6162 redirectedMsgFlusher.Cancel();
6163
6164 if (aMsg.wParam == VK_MENU ||
6165 (aMsg.wParam == VK_F10 && !modKeyState.IsShift())) {
6166 // We need to let Windows handle this keypress,
6167 // by returning false, if there's a native menu
6168 // bar somewhere in our containing window hierarchy.
6169 // Otherwise we handle the keypress and don't pass
6170 // it on to Windows, by returning true.
6171 bool hasNativeMenu = false;
6172 HWND hWnd = mWnd;
6173 while (hWnd) {
6174 if (::GetMenu(hWnd)) {
6175 hasNativeMenu = true;
6176 break;
6177 }
6178 hWnd = ::GetParent(hWnd);
6179 }
6180 result = !hasNativeMenu;
6181 }
6182
6183 return result;
6184 }
6185
SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,int32_t aNativeKeyCode,uint32_t aModifierFlags,const nsAString & aCharacters,const nsAString & aUnmodifiedCharacters,nsIObserver * aObserver)6186 nsresult nsWindow::SynthesizeNativeKeyEvent(
6187 int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode,
6188 uint32_t aModifierFlags, const nsAString& aCharacters,
6189 const nsAString& aUnmodifiedCharacters, nsIObserver* aObserver) {
6190 AutoObserverNotifier notifier(aObserver, "keyevent");
6191
6192 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
6193 return keyboardLayout->SynthesizeNativeKeyEvent(
6194 this, aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags, aCharacters,
6195 aUnmodifiedCharacters);
6196 }
6197
SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,uint32_t aNativeMessage,uint32_t aModifierFlags,nsIObserver * aObserver)6198 nsresult nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
6199 uint32_t aNativeMessage,
6200 uint32_t aModifierFlags,
6201 nsIObserver* aObserver) {
6202 AutoObserverNotifier notifier(aObserver, "mouseevent");
6203
6204 if (aNativeMessage == MOUSEEVENTF_MOVE) {
6205 // Reset sLastMouseMovePoint so that even if we're moving the mouse
6206 // to the position it's already at, we still dispatch a mousemove
6207 // event, because the callers of this function expect that.
6208 sLastMouseMovePoint = {0};
6209 }
6210 ::SetCursorPos(aPoint.x, aPoint.y);
6211
6212 INPUT input;
6213 memset(&input, 0, sizeof(input));
6214
6215 input.type = INPUT_MOUSE;
6216 input.mi.dwFlags = aNativeMessage;
6217 ::SendInput(1, &input, sizeof(INPUT));
6218
6219 return NS_OK;
6220 }
6221
SynthesizeNativeMouseScrollEvent(LayoutDeviceIntPoint aPoint,uint32_t aNativeMessage,double aDeltaX,double aDeltaY,double aDeltaZ,uint32_t aModifierFlags,uint32_t aAdditionalFlags,nsIObserver * aObserver)6222 nsresult nsWindow::SynthesizeNativeMouseScrollEvent(
6223 LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage, double aDeltaX,
6224 double aDeltaY, double aDeltaZ, uint32_t aModifierFlags,
6225 uint32_t aAdditionalFlags, nsIObserver* aObserver) {
6226 AutoObserverNotifier notifier(aObserver, "mousescrollevent");
6227 return MouseScrollHandler::SynthesizeNativeMouseScrollEvent(
6228 this, aPoint, aNativeMessage,
6229 (aNativeMessage == WM_MOUSEWHEEL || aNativeMessage == WM_VSCROLL)
6230 ? static_cast<int32_t>(aDeltaY)
6231 : static_cast<int32_t>(aDeltaX),
6232 aModifierFlags, aAdditionalFlags);
6233 }
6234
6235 /**************************************************************
6236 *
6237 * SECTION: OnXXX message handlers
6238 *
6239 * For message handlers that need to be broken out or
6240 * implemented in specific platform code.
6241 *
6242 **************************************************************/
6243
OnWindowPosChanged(WINDOWPOS * wp)6244 void nsWindow::OnWindowPosChanged(WINDOWPOS* wp) {
6245 if (wp == nullptr) return;
6246
6247 #ifdef WINSTATE_DEBUG_OUTPUT
6248 if (mWnd == WinUtils::GetTopLevelHWND(mWnd)) {
6249 MOZ_LOG(gWindowsLog, LogLevel::Info, ("*** OnWindowPosChanged: [ top] "));
6250 } else {
6251 MOZ_LOG(gWindowsLog, LogLevel::Info, ("*** OnWindowPosChanged: [child] "));
6252 }
6253 MOZ_LOG(gWindowsLog, LogLevel::Info, ("WINDOWPOS flags:"));
6254 if (wp->flags & SWP_FRAMECHANGED) {
6255 MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_FRAMECHANGED "));
6256 }
6257 if (wp->flags & SWP_SHOWWINDOW) {
6258 MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_SHOWWINDOW "));
6259 }
6260 if (wp->flags & SWP_NOSIZE) {
6261 MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_NOSIZE "));
6262 }
6263 if (wp->flags & SWP_HIDEWINDOW) {
6264 MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_HIDEWINDOW "));
6265 }
6266 if (wp->flags & SWP_NOZORDER) {
6267 MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_NOZORDER "));
6268 }
6269 if (wp->flags & SWP_NOACTIVATE) {
6270 MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_NOACTIVATE "));
6271 }
6272 MOZ_LOG(gWindowsLog, LogLevel::Info, ("\n"));
6273 #endif
6274
6275 // Handle window size mode changes
6276 if (wp->flags & SWP_FRAMECHANGED && mSizeMode != nsSizeMode_Fullscreen) {
6277 // Bug 566135 - Windows theme code calls show window on SW_SHOWMINIMIZED
6278 // windows when fullscreen games disable desktop composition. If we're
6279 // minimized and not being activated, ignore the event and let windows
6280 // handle it.
6281 if (mSizeMode == nsSizeMode_Minimized && (wp->flags & SWP_NOACTIVATE))
6282 return;
6283
6284 WINDOWPLACEMENT pl;
6285 pl.length = sizeof(pl);
6286 ::GetWindowPlacement(mWnd, &pl);
6287
6288 nsSizeMode previousSizeMode = mSizeMode;
6289
6290 // Windows has just changed the size mode of this window. The call to
6291 // SizeModeChanged will trigger a call into SetSizeMode where we will
6292 // set the min/max window state again or for nsSizeMode_Normal, call
6293 // SetWindow with a parameter of SW_RESTORE. There's no need however as
6294 // this window's mode has already changed. Updating mSizeMode here
6295 // insures the SetSizeMode call is a no-op. Addresses a bug on Win7 related
6296 // to window docking. (bug 489258)
6297 if (pl.showCmd == SW_SHOWMAXIMIZED)
6298 mSizeMode =
6299 (mFullscreenMode ? nsSizeMode_Fullscreen : nsSizeMode_Maximized);
6300 else if (pl.showCmd == SW_SHOWMINIMIZED)
6301 mSizeMode = nsSizeMode_Minimized;
6302 else if (mFullscreenMode)
6303 mSizeMode = nsSizeMode_Fullscreen;
6304 else
6305 mSizeMode = nsSizeMode_Normal;
6306
6307 #ifdef WINSTATE_DEBUG_OUTPUT
6308 switch (mSizeMode) {
6309 case nsSizeMode_Normal:
6310 MOZ_LOG(gWindowsLog, LogLevel::Info,
6311 ("*** mSizeMode: nsSizeMode_Normal\n"));
6312 break;
6313 case nsSizeMode_Minimized:
6314 MOZ_LOG(gWindowsLog, LogLevel::Info,
6315 ("*** mSizeMode: nsSizeMode_Minimized\n"));
6316 break;
6317 case nsSizeMode_Maximized:
6318 MOZ_LOG(gWindowsLog, LogLevel::Info,
6319 ("*** mSizeMode: nsSizeMode_Maximized\n"));
6320 break;
6321 default:
6322 MOZ_LOG(gWindowsLog, LogLevel::Info, ("*** mSizeMode: ??????\n"));
6323 break;
6324 }
6325 #endif
6326
6327 if (mWidgetListener && mSizeMode != previousSizeMode)
6328 mWidgetListener->SizeModeChanged(mSizeMode);
6329
6330 // If window was restored, window activation was bypassed during the
6331 // SetSizeMode call originating from OnWindowPosChanging to avoid saving
6332 // pre-restore attributes. Force activation now to get correct attributes.
6333 if (mLastSizeMode != nsSizeMode_Normal && mSizeMode == nsSizeMode_Normal)
6334 DispatchFocusToTopLevelWindow(true);
6335
6336 mLastSizeMode = mSizeMode;
6337
6338 // Skip window size change events below on minimization.
6339 if (mSizeMode == nsSizeMode_Minimized) return;
6340 }
6341
6342 // Handle window position changes
6343 if (!(wp->flags & SWP_NOMOVE)) {
6344 mBounds.MoveTo(wp->x, wp->y);
6345 NotifyWindowMoved(wp->x, wp->y);
6346 }
6347
6348 // Handle window size changes
6349 if (!(wp->flags & SWP_NOSIZE)) {
6350 RECT r;
6351 int32_t newWidth, newHeight;
6352
6353 ::GetWindowRect(mWnd, &r);
6354
6355 newWidth = r.right - r.left;
6356 newHeight = r.bottom - r.top;
6357
6358 if (newWidth > mLastSize.width) {
6359 RECT drect;
6360
6361 // getting wider
6362 drect.left = wp->x + mLastSize.width;
6363 drect.top = wp->y;
6364 drect.right = drect.left + (newWidth - mLastSize.width);
6365 drect.bottom = drect.top + newHeight;
6366
6367 ::RedrawWindow(mWnd, &drect, nullptr,
6368 RDW_INVALIDATE | RDW_NOERASE | RDW_NOINTERNALPAINT |
6369 RDW_ERASENOW | RDW_ALLCHILDREN);
6370 }
6371 if (newHeight > mLastSize.height) {
6372 RECT drect;
6373
6374 // getting taller
6375 drect.left = wp->x;
6376 drect.top = wp->y + mLastSize.height;
6377 drect.right = drect.left + newWidth;
6378 drect.bottom = drect.top + (newHeight - mLastSize.height);
6379
6380 ::RedrawWindow(mWnd, &drect, nullptr,
6381 RDW_INVALIDATE | RDW_NOERASE | RDW_NOINTERNALPAINT |
6382 RDW_ERASENOW | RDW_ALLCHILDREN);
6383 }
6384
6385 mBounds.SizeTo(newWidth, newHeight);
6386 mLastSize.width = newWidth;
6387 mLastSize.height = newHeight;
6388
6389 #ifdef WINSTATE_DEBUG_OUTPUT
6390 MOZ_LOG(gWindowsLog, LogLevel::Info,
6391 ("*** Resize window: %d x %d x %d x %d\n", wp->x, wp->y, newWidth,
6392 newHeight));
6393 #endif
6394
6395 // If a maximized window is resized, recalculate the non-client margins.
6396 if (mSizeMode == nsSizeMode_Maximized) {
6397 if (UpdateNonClientMargins(nsSizeMode_Maximized, true)) {
6398 // gecko resize event already sent by UpdateNonClientMargins.
6399 return;
6400 }
6401 }
6402
6403 // Recalculate the width and height based on the client area for gecko
6404 // events.
6405 LayoutDeviceIntSize clientSize(newWidth, newHeight);
6406 if (::GetClientRect(mWnd, &r)) {
6407 clientSize = WinUtils::ToIntRect(r).Size();
6408 }
6409 // Send a gecko resize event
6410 OnResize(clientSize);
6411 }
6412 }
6413
OnWindowPosChanging(LPWINDOWPOS & info)6414 void nsWindow::OnWindowPosChanging(LPWINDOWPOS& info) {
6415 // Update non-client margins if the frame size is changing, and let the
6416 // browser know we are changing size modes, so alternative css can kick in.
6417 // If we're going into fullscreen mode, ignore this, since it'll reset
6418 // margins to normal mode.
6419 if ((info->flags & SWP_FRAMECHANGED && !(info->flags & SWP_NOSIZE)) &&
6420 mSizeMode != nsSizeMode_Fullscreen) {
6421 WINDOWPLACEMENT pl;
6422 pl.length = sizeof(pl);
6423 ::GetWindowPlacement(mWnd, &pl);
6424 nsSizeMode sizeMode;
6425 if (pl.showCmd == SW_SHOWMAXIMIZED)
6426 sizeMode =
6427 (mFullscreenMode ? nsSizeMode_Fullscreen : nsSizeMode_Maximized);
6428 else if (pl.showCmd == SW_SHOWMINIMIZED)
6429 sizeMode = nsSizeMode_Minimized;
6430 else if (mFullscreenMode)
6431 sizeMode = nsSizeMode_Fullscreen;
6432 else
6433 sizeMode = nsSizeMode_Normal;
6434
6435 if (mWidgetListener) mWidgetListener->SizeModeChanged(sizeMode);
6436
6437 UpdateNonClientMargins(sizeMode, false);
6438 }
6439
6440 // Force fullscreen. This works around a bug in Windows 10 1809 where
6441 // using fullscreen when a window is "snapped" causes a spurious resize
6442 // smaller than the full screen, see bug 1482920.
6443 if (mSizeMode == nsSizeMode_Fullscreen && !(info->flags & SWP_NOMOVE) &&
6444 !(info->flags & SWP_NOSIZE)) {
6445 nsCOMPtr<nsIScreenManager> screenmgr =
6446 do_GetService(sScreenManagerContractID);
6447 if (screenmgr) {
6448 LayoutDeviceIntRect bounds(info->x, info->y, info->cx, info->cy);
6449 DesktopIntRect deskBounds =
6450 RoundedToInt(bounds / GetDesktopToDeviceScale());
6451 nsCOMPtr<nsIScreen> screen;
6452 screenmgr->ScreenForRect(deskBounds.X(), deskBounds.Y(),
6453 deskBounds.Width(), deskBounds.Height(),
6454 getter_AddRefs(screen));
6455
6456 if (screen) {
6457 int32_t x, y, width, height;
6458 screen->GetRect(&x, &y, &width, &height);
6459
6460 info->x = x;
6461 info->y = y;
6462 info->cx = width;
6463 info->cy = height;
6464 }
6465 }
6466 }
6467
6468 // enforce local z-order rules
6469 if (!(info->flags & SWP_NOZORDER)) {
6470 HWND hwndAfter = info->hwndInsertAfter;
6471
6472 nsWindow* aboveWindow = 0;
6473 nsWindowZ placement;
6474
6475 if (hwndAfter == HWND_BOTTOM)
6476 placement = nsWindowZBottom;
6477 else if (hwndAfter == HWND_TOP || hwndAfter == HWND_TOPMOST ||
6478 hwndAfter == HWND_NOTOPMOST)
6479 placement = nsWindowZTop;
6480 else {
6481 placement = nsWindowZRelative;
6482 aboveWindow = WinUtils::GetNSWindowPtr(hwndAfter);
6483 }
6484
6485 if (mWidgetListener) {
6486 nsCOMPtr<nsIWidget> actualBelow = nullptr;
6487 if (mWidgetListener->ZLevelChanged(false, &placement, aboveWindow,
6488 getter_AddRefs(actualBelow))) {
6489 if (placement == nsWindowZBottom)
6490 info->hwndInsertAfter = HWND_BOTTOM;
6491 else if (placement == nsWindowZTop)
6492 info->hwndInsertAfter = HWND_TOP;
6493 else {
6494 info->hwndInsertAfter =
6495 (HWND)actualBelow->GetNativeData(NS_NATIVE_WINDOW);
6496 }
6497 }
6498 }
6499 }
6500 // prevent rude external programs from making hidden window visible
6501 if (mWindowType == eWindowType_invisible) info->flags &= ~SWP_SHOWWINDOW;
6502 }
6503
UserActivity()6504 void nsWindow::UserActivity() {
6505 // Check if we have the idle service, if not we try to get it.
6506 if (!mIdleService) {
6507 mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
6508 }
6509
6510 // Check that we now have the idle service.
6511 if (mIdleService) {
6512 mIdleService->ResetIdleTimeOut(0);
6513 }
6514 }
6515
GetTouchCoordinates(WPARAM wParam,LPARAM lParam)6516 nsIntPoint nsWindow::GetTouchCoordinates(WPARAM wParam, LPARAM lParam) {
6517 nsIntPoint ret;
6518 uint32_t cInputs = LOWORD(wParam);
6519 if (cInputs != 1) {
6520 // Just return 0,0 if there isn't exactly one touch point active
6521 return ret;
6522 }
6523 PTOUCHINPUT pInputs = new TOUCHINPUT[cInputs];
6524 if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs,
6525 sizeof(TOUCHINPUT))) {
6526 ret.x = TOUCH_COORD_TO_PIXEL(pInputs[0].x);
6527 ret.y = TOUCH_COORD_TO_PIXEL(pInputs[0].y);
6528 }
6529 delete[] pInputs;
6530 // Note that we don't call CloseTouchInputHandle here because we need
6531 // to read the touch input info again in OnTouch later.
6532 return ret;
6533 }
6534
OnTouch(WPARAM wParam,LPARAM lParam)6535 bool nsWindow::OnTouch(WPARAM wParam, LPARAM lParam) {
6536 uint32_t cInputs = LOWORD(wParam);
6537 PTOUCHINPUT pInputs = new TOUCHINPUT[cInputs];
6538
6539 if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs,
6540 sizeof(TOUCHINPUT))) {
6541 MultiTouchInput touchInput, touchEndInput;
6542
6543 // Walk across the touch point array processing each contact point.
6544 for (uint32_t i = 0; i < cInputs; i++) {
6545 bool addToEvent = false, addToEndEvent = false;
6546
6547 // N.B.: According with MS documentation
6548 // https://msdn.microsoft.com/en-us/library/windows/desktop/dd317334(v=vs.85).aspx
6549 // TOUCHEVENTF_DOWN cannot be combined with TOUCHEVENTF_MOVE or
6550 // TOUCHEVENTF_UP. Possibly, it means that TOUCHEVENTF_MOVE and
6551 // TOUCHEVENTF_UP can be combined together.
6552
6553 if (pInputs[i].dwFlags & (TOUCHEVENTF_DOWN | TOUCHEVENTF_MOVE)) {
6554 if (touchInput.mTimeStamp.IsNull()) {
6555 // Initialize a touch event to send.
6556 touchInput.mType = MultiTouchInput::MULTITOUCH_MOVE;
6557 touchInput.mTime = ::GetMessageTime();
6558 touchInput.mTimeStamp = GetMessageTimeStamp(touchInput.mTime);
6559 ModifierKeyState modifierKeyState;
6560 touchInput.modifiers = modifierKeyState.GetModifiers();
6561 }
6562 // Pres shell expects this event to be a eTouchStart
6563 // if any new contact points have been added since the last event sent.
6564 if (pInputs[i].dwFlags & TOUCHEVENTF_DOWN) {
6565 touchInput.mType = MultiTouchInput::MULTITOUCH_START;
6566 }
6567 addToEvent = true;
6568 }
6569 if (pInputs[i].dwFlags & TOUCHEVENTF_UP) {
6570 // Pres shell expects removed contacts points to be delivered in a
6571 // separate eTouchEnd event containing only the contact points that were
6572 // removed.
6573 if (touchEndInput.mTimeStamp.IsNull()) {
6574 // Initialize a touch event to send.
6575 touchEndInput.mType = MultiTouchInput::MULTITOUCH_END;
6576 touchEndInput.mTime = ::GetMessageTime();
6577 touchEndInput.mTimeStamp = GetMessageTimeStamp(touchEndInput.mTime);
6578 ModifierKeyState modifierKeyState;
6579 touchEndInput.modifiers = modifierKeyState.GetModifiers();
6580 }
6581 addToEndEvent = true;
6582 }
6583 if (!addToEvent && !addToEndEvent) {
6584 // Filter out spurious Windows events we don't understand, like palm
6585 // contact.
6586 continue;
6587 }
6588
6589 // Setup the touch point we'll append to the touch event array.
6590 nsPointWin touchPoint;
6591 touchPoint.x = TOUCH_COORD_TO_PIXEL(pInputs[i].x);
6592 touchPoint.y = TOUCH_COORD_TO_PIXEL(pInputs[i].y);
6593 touchPoint.ScreenToClient(mWnd);
6594
6595 // Initialize the touch data.
6596 SingleTouchData touchData(
6597 pInputs[i].dwID, // aIdentifier
6598 ScreenIntPoint::FromUnknownPoint(touchPoint), // aScreenPoint
6599 /* radius, if known */
6600 pInputs[i].dwFlags & TOUCHINPUTMASKF_CONTACTAREA
6601 ? ScreenSize(TOUCH_COORD_TO_PIXEL(pInputs[i].cxContact) / 2,
6602 TOUCH_COORD_TO_PIXEL(pInputs[i].cyContact) / 2)
6603 : ScreenSize(1, 1), // aRadius
6604 0.0f, // aRotationAngle
6605 0.0f); // aForce
6606
6607 // Append touch data to the appropriate event.
6608 if (addToEvent) {
6609 touchInput.mTouches.AppendElement(touchData);
6610 }
6611 if (addToEndEvent) {
6612 touchEndInput.mTouches.AppendElement(touchData);
6613 }
6614 }
6615
6616 // Dispatch touch start and touch move event if we have one.
6617 if (!touchInput.mTimeStamp.IsNull()) {
6618 DispatchTouchInput(touchInput);
6619 }
6620 // Dispatch touch end event if we have one.
6621 if (!touchEndInput.mTimeStamp.IsNull()) {
6622 DispatchTouchInput(touchEndInput);
6623 }
6624 }
6625
6626 delete[] pInputs;
6627 CloseTouchInputHandle((HTOUCHINPUT)lParam);
6628 return true;
6629 }
6630
6631 // Gesture event processing. Handles WM_GESTURE events.
OnGesture(WPARAM wParam,LPARAM lParam)6632 bool nsWindow::OnGesture(WPARAM wParam, LPARAM lParam) {
6633 // Treatment for pan events which translate into scroll events:
6634 if (mGesture.IsPanEvent(lParam)) {
6635 if (!mGesture.ProcessPanMessage(mWnd, wParam, lParam))
6636 return false; // ignore
6637
6638 nsEventStatus status;
6639
6640 WidgetWheelEvent wheelEvent(true, eWheel, this);
6641
6642 ModifierKeyState modifierKeyState;
6643 modifierKeyState.InitInputEvent(wheelEvent);
6644
6645 wheelEvent.button = 0;
6646 wheelEvent.mTime = ::GetMessageTime();
6647 wheelEvent.mTimeStamp = GetMessageTimeStamp(wheelEvent.mTime);
6648 wheelEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
6649
6650 bool endFeedback = true;
6651
6652 if (mGesture.PanDeltaToPixelScroll(wheelEvent)) {
6653 DispatchEvent(&wheelEvent, status);
6654 }
6655
6656 if (mDisplayPanFeedback) {
6657 mGesture.UpdatePanFeedbackX(
6658 mWnd, DeprecatedAbs(RoundDown(wheelEvent.mOverflowDeltaX)),
6659 endFeedback);
6660 mGesture.UpdatePanFeedbackY(
6661 mWnd, DeprecatedAbs(RoundDown(wheelEvent.mOverflowDeltaY)),
6662 endFeedback);
6663 mGesture.PanFeedbackFinalize(mWnd, endFeedback);
6664 }
6665
6666 CloseGestureInfoHandle((HGESTUREINFO)lParam);
6667
6668 return true;
6669 }
6670
6671 // Other gestures translate into simple gesture events:
6672 WidgetSimpleGestureEvent event(true, eVoidEvent, this);
6673 if (!mGesture.ProcessGestureMessage(mWnd, wParam, lParam, event)) {
6674 return false; // fall through to DefWndProc
6675 }
6676
6677 // Polish up and send off the new event
6678 ModifierKeyState modifierKeyState;
6679 modifierKeyState.InitInputEvent(event);
6680 event.button = 0;
6681 event.mTime = ::GetMessageTime();
6682 event.mTimeStamp = GetMessageTimeStamp(event.mTime);
6683 event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
6684
6685 nsEventStatus status;
6686 DispatchEvent(&event, status);
6687 if (status == nsEventStatus_eIgnore) {
6688 return false; // Ignored, fall through
6689 }
6690
6691 // Only close this if we process and return true.
6692 CloseGestureInfoHandle((HGESTUREINFO)lParam);
6693
6694 return true; // Handled
6695 }
6696
ConfigureChildren(const nsTArray<Configuration> & aConfigurations)6697 nsresult nsWindow::ConfigureChildren(
6698 const nsTArray<Configuration>& aConfigurations) {
6699 // If this is a remotely updated widget we receive clipping, position, and
6700 // size information from a source other than our owner. Don't let our parent
6701 // update this information.
6702 if (mWindowType == eWindowType_plugin_ipc_chrome) {
6703 return NS_OK;
6704 }
6705
6706 // XXXroc we could use BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos
6707 // here, if that helps in some situations. So far I haven't seen a
6708 // need.
6709 for (uint32_t i = 0; i < aConfigurations.Length(); ++i) {
6710 const Configuration& configuration = aConfigurations[i];
6711 nsWindow* w = static_cast<nsWindow*>(configuration.mChild.get());
6712 NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child");
6713 nsresult rv = w->SetWindowClipRegion(configuration.mClipRegion, true);
6714 NS_ENSURE_SUCCESS(rv, rv);
6715 LayoutDeviceIntRect bounds = w->GetBounds();
6716 if (bounds.Size() != configuration.mBounds.Size()) {
6717 w->Resize(configuration.mBounds.X(), configuration.mBounds.Y(),
6718 configuration.mBounds.Width(), configuration.mBounds.Height(),
6719 true);
6720 } else if (bounds.TopLeft() != configuration.mBounds.TopLeft()) {
6721 w->Move(configuration.mBounds.X(), configuration.mBounds.Y());
6722
6723 if (gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend() ||
6724 GetLayerManager()->GetBackendType() != LayersBackend::LAYERS_BASIC) {
6725 // XXX - Workaround for Bug 587508. This will invalidate the part of the
6726 // plugin window that might be touched by moving content somehow. The
6727 // underlying problem should be found and fixed!
6728 LayoutDeviceIntRegion r;
6729 r.Sub(bounds, configuration.mBounds);
6730 r.MoveBy(-bounds.X(), -bounds.Y());
6731 LayoutDeviceIntRect toInvalidate = r.GetBounds();
6732
6733 WinUtils::InvalidatePluginAsWorkaround(w, toInvalidate);
6734 }
6735 }
6736 rv = w->SetWindowClipRegion(configuration.mClipRegion, false);
6737 NS_ENSURE_SUCCESS(rv, rv);
6738 }
6739 return NS_OK;
6740 }
6741
CreateHRGNFromArray(const nsTArray<LayoutDeviceIntRect> & aRects)6742 static HRGN CreateHRGNFromArray(const nsTArray<LayoutDeviceIntRect>& aRects) {
6743 int32_t size = sizeof(RGNDATAHEADER) + sizeof(RECT) * aRects.Length();
6744 AutoTArray<uint8_t, 100> buf;
6745 buf.SetLength(size);
6746 RGNDATA* data = reinterpret_cast<RGNDATA*>(buf.Elements());
6747 RECT* rects = reinterpret_cast<RECT*>(data->Buffer);
6748 data->rdh.dwSize = sizeof(data->rdh);
6749 data->rdh.iType = RDH_RECTANGLES;
6750 data->rdh.nCount = aRects.Length();
6751 LayoutDeviceIntRect bounds;
6752 for (uint32_t i = 0; i < aRects.Length(); ++i) {
6753 const LayoutDeviceIntRect& r = aRects[i];
6754 bounds.UnionRect(bounds, r);
6755 ::SetRect(&rects[i], r.X(), r.Y(), r.XMost(), r.YMost());
6756 }
6757 ::SetRect(&data->rdh.rcBound, bounds.X(), bounds.Y(), bounds.XMost(),
6758 bounds.YMost());
6759 return ::ExtCreateRegion(nullptr, buf.Length(), data);
6760 }
6761
SetWindowClipRegion(const nsTArray<LayoutDeviceIntRect> & aRects,bool aIntersectWithExisting)6762 nsresult nsWindow::SetWindowClipRegion(
6763 const nsTArray<LayoutDeviceIntRect>& aRects, bool aIntersectWithExisting) {
6764 if (IsWindowClipRegionEqual(aRects)) {
6765 return NS_OK;
6766 }
6767
6768 nsBaseWidget::SetWindowClipRegion(aRects, aIntersectWithExisting);
6769
6770 HRGN dest = CreateHRGNFromArray(aRects);
6771 if (!dest) return NS_ERROR_OUT_OF_MEMORY;
6772
6773 if (aIntersectWithExisting) {
6774 HRGN current = ::CreateRectRgn(0, 0, 0, 0);
6775 if (current) {
6776 if (::GetWindowRgn(mWnd, current) != 0 /*ERROR*/) {
6777 ::CombineRgn(dest, dest, current, RGN_AND);
6778 }
6779 ::DeleteObject(current);
6780 }
6781 }
6782
6783 // If a plugin is not visible, especially if it is in a background tab,
6784 // it should not be able to steal keyboard focus. This code checks whether
6785 // the region that the plugin is being clipped to is NULLREGION. If it is,
6786 // the plugin window gets disabled.
6787 if (IsPlugin()) {
6788 if (NULLREGION == ::CombineRgn(dest, dest, dest, RGN_OR)) {
6789 ::ShowWindow(mWnd, SW_HIDE);
6790 ::EnableWindow(mWnd, FALSE);
6791 } else {
6792 ::EnableWindow(mWnd, TRUE);
6793 ::ShowWindow(mWnd, SW_SHOW);
6794 }
6795 }
6796 if (!::SetWindowRgn(mWnd, dest, TRUE)) {
6797 ::DeleteObject(dest);
6798 return NS_ERROR_FAILURE;
6799 }
6800 return NS_OK;
6801 }
6802
6803 // WM_DESTROY event handler
OnDestroy()6804 void nsWindow::OnDestroy() {
6805 mOnDestroyCalled = true;
6806
6807 // Make sure we don't get destroyed in the process of tearing down.
6808 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
6809
6810 // Dispatch the destroy notification.
6811 if (!mInDtor) NotifyWindowDestroyed();
6812
6813 // Prevent the widget from sending additional events.
6814 mWidgetListener = nullptr;
6815 mAttachedWidgetListener = nullptr;
6816
6817 // Unregister notifications from terminal services
6818 ::WTSUnRegisterSessionNotification(mWnd);
6819
6820 // Free our subclass and clear |this| stored in the window props. We will no
6821 // longer receive events from Windows after this point.
6822 SubclassWindow(FALSE);
6823
6824 // Once mWidgetListener is cleared and the subclass is reset, sCurrentWindow
6825 // can be cleared. (It's used in tracking windows for mouse events.)
6826 if (sCurrentWindow == this) sCurrentWindow = nullptr;
6827
6828 // Disconnects us from our parent, will call our GetParent().
6829 nsBaseWidget::Destroy();
6830
6831 // Release references to children, device context, toolkit, and app shell.
6832 nsBaseWidget::OnDestroy();
6833
6834 // Clear our native parent handle.
6835 // XXX Windows will take care of this in the proper order, and
6836 // SetParent(nullptr)'s remove child on the parent already took place in
6837 // nsBaseWidget's Destroy call above.
6838 // SetParent(nullptr);
6839 mParent = nullptr;
6840
6841 // We have to destroy the native drag target before we null out our window
6842 // pointer.
6843 EnableDragDrop(false);
6844
6845 // If we're going away and for some reason we're still the rollup widget,
6846 // rollup and turn off capture.
6847 nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
6848 nsCOMPtr<nsIWidget> rollupWidget;
6849 if (rollupListener) {
6850 rollupWidget = rollupListener->GetRollupWidget();
6851 }
6852 if (this == rollupWidget) {
6853 if (rollupListener) rollupListener->Rollup(0, false, nullptr, nullptr);
6854 CaptureRollupEvents(nullptr, false);
6855 }
6856
6857 IMEHandler::OnDestroyWindow(this);
6858
6859 // Free GDI window class objects
6860 if (mBrush) {
6861 VERIFY(::DeleteObject(mBrush));
6862 mBrush = nullptr;
6863 }
6864
6865 // Destroy any custom cursor resources.
6866 if (mCursor == eCursorInvalid) SetCursor(eCursor_standard);
6867
6868 if (mCompositorWidgetDelegate) {
6869 mCompositorWidgetDelegate->OnDestroyWindow();
6870 }
6871 mBasicLayersSurface = nullptr;
6872
6873 // Finalize panning feedback to possibly restore window displacement
6874 mGesture.PanFeedbackFinalize(mWnd, true);
6875
6876 // Clear the main HWND.
6877 mWnd = nullptr;
6878 }
6879
6880 // Send a resize message to the listener
OnResize(const LayoutDeviceIntSize & aSize)6881 bool nsWindow::OnResize(const LayoutDeviceIntSize& aSize) {
6882 bool result = false;
6883 if (mWidgetListener) {
6884 result = mWidgetListener->WindowResized(this, aSize.width, aSize.height);
6885 }
6886
6887 // If there is an attached view, inform it as well as the normal widget
6888 // listener.
6889 if (mAttachedWidgetListener) {
6890 return mAttachedWidgetListener->WindowResized(this, aSize.width,
6891 aSize.height);
6892 }
6893
6894 return result;
6895 }
6896
OnHotKey(WPARAM wParam,LPARAM lParam)6897 bool nsWindow::OnHotKey(WPARAM wParam, LPARAM lParam) { return true; }
6898
6899 // Can be overriden. Controls auto-erase of background.
AutoErase(HDC dc)6900 bool nsWindow::AutoErase(HDC dc) { return false; }
6901
IsPopup()6902 bool nsWindow::IsPopup() { return mWindowType == eWindowType_popup; }
6903
ShouldUseOffMainThreadCompositing()6904 bool nsWindow::ShouldUseOffMainThreadCompositing() {
6905 if (IsSmallPopup()) {
6906 return false;
6907 }
6908
6909 return nsBaseWidget::ShouldUseOffMainThreadCompositing();
6910 }
6911
WindowUsesOMTC()6912 void nsWindow::WindowUsesOMTC() {
6913 ULONG_PTR style = ::GetClassLongPtr(mWnd, GCL_STYLE);
6914 if (!style) {
6915 NS_WARNING("Could not get window class style");
6916 return;
6917 }
6918 style |= CS_HREDRAW | CS_VREDRAW;
6919 DebugOnly<ULONG_PTR> result = ::SetClassLongPtr(mWnd, GCL_STYLE, style);
6920 NS_WARNING_ASSERTION(result, "Could not reset window class style");
6921 }
6922
HasBogusPopupsDropShadowOnMultiMonitor()6923 bool nsWindow::HasBogusPopupsDropShadowOnMultiMonitor() {
6924 if (sHasBogusPopupsDropShadowOnMultiMonitor == TRI_UNKNOWN) {
6925 // Since any change in the preferences requires a restart, this can be
6926 // done just once.
6927 // Check for Direct2D first.
6928 sHasBogusPopupsDropShadowOnMultiMonitor =
6929 gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend() ? TRI_TRUE
6930 : TRI_FALSE;
6931 if (!sHasBogusPopupsDropShadowOnMultiMonitor) {
6932 // Otherwise check if Direct3D 9 may be used.
6933 if (gfxConfig::IsEnabled(Feature::HW_COMPOSITING) &&
6934 !gfxConfig::IsEnabled(Feature::OPENGL_COMPOSITING)) {
6935 nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
6936 if (gfxInfo) {
6937 int32_t status;
6938 nsCString discardFailureId;
6939 if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(
6940 nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, discardFailureId,
6941 &status))) {
6942 if (status == nsIGfxInfo::FEATURE_STATUS_OK ||
6943 gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING)) {
6944 sHasBogusPopupsDropShadowOnMultiMonitor = TRI_TRUE;
6945 }
6946 }
6947 }
6948 }
6949 }
6950 }
6951 return !!sHasBogusPopupsDropShadowOnMultiMonitor;
6952 }
6953
OnSysColorChanged()6954 void nsWindow::OnSysColorChanged() {
6955 if (mWindowType == eWindowType_invisible) {
6956 ::EnumThreadWindows(GetCurrentThreadId(), nsWindow::BroadcastMsg,
6957 WM_SYSCOLORCHANGE);
6958 } else {
6959 // Note: This is sent for child windows as well as top-level windows.
6960 // The Win32 toolkit normally only sends these events to top-level windows.
6961 // But we cycle through all of the childwindows and send it to them as well
6962 // so all presentations get notified properly.
6963 // See nsWindow::GlobalMsgWindowProc.
6964 NotifySysColorChanged();
6965 }
6966 }
6967
OnDPIChanged(int32_t x,int32_t y,int32_t width,int32_t height)6968 void nsWindow::OnDPIChanged(int32_t x, int32_t y, int32_t width,
6969 int32_t height) {
6970 // Don't try to handle WM_DPICHANGED for popup windows (see bug 1239353);
6971 // they remain tied to their original parent's resolution.
6972 if (mWindowType == eWindowType_popup) {
6973 return;
6974 }
6975 if (DefaultScaleOverride() > 0.0) {
6976 return;
6977 }
6978 mDefaultScale = -1.0; // force recomputation of scale factor
6979
6980 if (mResizeState != RESIZING && mSizeMode == nsSizeMode_Normal) {
6981 // Limit the position (if not in the middle of a drag-move) & size,
6982 // if it would overflow the destination screen
6983 nsCOMPtr<nsIScreenManager> sm = do_GetService(sScreenManagerContractID);
6984 if (sm) {
6985 nsCOMPtr<nsIScreen> screen;
6986 sm->ScreenForRect(x, y, width, height, getter_AddRefs(screen));
6987 if (screen) {
6988 int32_t availLeft, availTop, availWidth, availHeight;
6989 screen->GetAvailRect(&availLeft, &availTop, &availWidth, &availHeight);
6990 if (mResizeState != MOVING) {
6991 x = std::max(x, availLeft);
6992 y = std::max(y, availTop);
6993 }
6994 width = std::min(width, availWidth);
6995 height = std::min(height, availHeight);
6996 }
6997 }
6998
6999 Resize(x, y, width, height, true);
7000 }
7001 ChangedDPI();
7002 ResetLayout();
7003 }
7004
7005 /**************************************************************
7006 **************************************************************
7007 **
7008 ** BLOCK: IME management and accessibility
7009 **
7010 ** Handles managing IME input and accessibility.
7011 **
7012 **************************************************************
7013 **************************************************************/
7014
SetInputContext(const InputContext & aContext,const InputContextAction & aAction)7015 void nsWindow::SetInputContext(const InputContext& aContext,
7016 const InputContextAction& aAction) {
7017 InputContext newInputContext = aContext;
7018 IMEHandler::SetInputContext(this, newInputContext, aAction);
7019 mInputContext = newInputContext;
7020 }
7021
GetInputContext()7022 InputContext nsWindow::GetInputContext() {
7023 mInputContext.mIMEState.mOpen = IMEState::CLOSED;
7024 if (WinUtils::IsIMEEnabled(mInputContext) && IMEHandler::GetOpenState(this)) {
7025 mInputContext.mIMEState.mOpen = IMEState::OPEN;
7026 } else {
7027 mInputContext.mIMEState.mOpen = IMEState::CLOSED;
7028 }
7029 return mInputContext;
7030 }
7031
GetNativeTextEventDispatcherListener()7032 TextEventDispatcherListener* nsWindow::GetNativeTextEventDispatcherListener() {
7033 return IMEHandler::GetNativeTextEventDispatcherListener();
7034 }
7035
7036 #ifdef ACCESSIBILITY
7037 #ifdef DEBUG
7038 #define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc) \
7039 if (a11y::logging::IsEnabled(a11y::logging::ePlatforms)) { \
7040 printf( \
7041 "Get the window:\n {\n HWND: %p, parent HWND: %p, wndobj: %p,\n", \
7042 aHwnd, ::GetParent(aHwnd), aWnd); \
7043 printf(" acc: %p", aAcc); \
7044 if (aAcc) { \
7045 nsAutoString name; \
7046 aAcc->Name(name); \
7047 printf(", accname: %s", NS_ConvertUTF16toUTF8(name).get()); \
7048 } \
7049 printf("\n }\n"); \
7050 }
7051
7052 #else
7053 #define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc)
7054 #endif
7055
GetAccessible()7056 a11y::Accessible* nsWindow::GetAccessible() {
7057 // If the pref was ePlatformIsDisabled, return null here, disabling a11y.
7058 if (a11y::PlatformDisabledState() == a11y::ePlatformIsDisabled)
7059 return nullptr;
7060
7061 if (mInDtor || mOnDestroyCalled || mWindowType == eWindowType_invisible) {
7062 return nullptr;
7063 }
7064
7065 // In case of popup window return a popup accessible.
7066 nsView* view = nsView::GetViewFor(this);
7067 if (view) {
7068 nsIFrame* frame = view->GetFrame();
7069 if (frame && nsLayoutUtils::IsPopup(frame)) {
7070 nsAccessibilityService* accService = GetOrCreateAccService();
7071 if (accService) {
7072 a11y::DocAccessible* docAcc =
7073 GetAccService()->GetDocAccessible(frame->PresShell());
7074 if (docAcc) {
7075 NS_LOG_WMGETOBJECT(
7076 this, mWnd,
7077 docAcc->GetAccessibleOrDescendant(frame->GetContent()));
7078 return docAcc->GetAccessibleOrDescendant(frame->GetContent());
7079 }
7080 }
7081 }
7082 }
7083
7084 // otherwise root document accessible.
7085 NS_LOG_WMGETOBJECT(this, mWnd, GetRootAccessible());
7086 return GetRootAccessible();
7087 }
7088 #endif
7089
7090 /**************************************************************
7091 **************************************************************
7092 **
7093 ** BLOCK: Transparency
7094 **
7095 ** Window transparency helpers.
7096 **
7097 **************************************************************
7098 **************************************************************/
7099
7100 #ifdef MOZ_XUL
7101
SetWindowTranslucencyInner(nsTransparencyMode aMode)7102 void nsWindow::SetWindowTranslucencyInner(nsTransparencyMode aMode) {
7103 if (aMode == mTransparencyMode) return;
7104
7105 // stop on dialogs and popups!
7106 HWND hWnd = WinUtils::GetTopLevelHWND(mWnd, true);
7107 nsWindow* parent = WinUtils::GetNSWindowPtr(hWnd);
7108
7109 if (!parent) {
7110 NS_WARNING("Trying to use transparent chrome in an embedded context");
7111 return;
7112 }
7113
7114 if (parent != this) {
7115 NS_WARNING(
7116 "Setting SetWindowTranslucencyInner on a parent this is not us!");
7117 }
7118
7119 if (aMode == eTransparencyTransparent) {
7120 // If we're switching to the use of a transparent window, hide the chrome
7121 // on our parent.
7122 HideWindowChrome(true);
7123 } else if (mHideChrome && mTransparencyMode == eTransparencyTransparent) {
7124 // if we're switching out of transparent, re-enable our parent's chrome.
7125 HideWindowChrome(false);
7126 }
7127
7128 LONG_PTR style = ::GetWindowLongPtrW(hWnd, GWL_STYLE),
7129 exStyle = ::GetWindowLongPtr(hWnd, GWL_EXSTYLE);
7130
7131 if (parent->mIsVisible) style |= WS_VISIBLE;
7132 if (parent->mSizeMode == nsSizeMode_Maximized)
7133 style |= WS_MAXIMIZE;
7134 else if (parent->mSizeMode == nsSizeMode_Minimized)
7135 style |= WS_MINIMIZE;
7136
7137 if (aMode == eTransparencyTransparent)
7138 exStyle |= WS_EX_LAYERED;
7139 else
7140 exStyle &= ~WS_EX_LAYERED;
7141
7142 VERIFY_WINDOW_STYLE(style);
7143 ::SetWindowLongPtrW(hWnd, GWL_STYLE, style);
7144 ::SetWindowLongPtrW(hWnd, GWL_EXSTYLE, exStyle);
7145
7146 if (HasGlass()) memset(&mGlassMargins, 0, sizeof mGlassMargins);
7147 mTransparencyMode = aMode;
7148
7149 if (mCompositorWidgetDelegate) {
7150 mCompositorWidgetDelegate->UpdateTransparency(aMode);
7151 }
7152 UpdateGlass();
7153 }
7154
7155 #endif // MOZ_XUL
7156
7157 /**************************************************************
7158 **************************************************************
7159 **
7160 ** BLOCK: Popup rollup hooks
7161 **
7162 ** Deals with CaptureRollup on popup windows.
7163 **
7164 **************************************************************
7165 **************************************************************/
7166
7167 // Schedules a timer for a window, so we can rollup after processing the hook
7168 // event
ScheduleHookTimer(HWND aWnd,UINT aMsgId)7169 void nsWindow::ScheduleHookTimer(HWND aWnd, UINT aMsgId) {
7170 // In some cases multiple hooks may be scheduled
7171 // so ignore any other requests once one timer is scheduled
7172 if (sHookTimerId == 0) {
7173 // Remember the window handle and the message ID to be used later
7174 sRollupMsgId = aMsgId;
7175 sRollupMsgWnd = aWnd;
7176 // Schedule native timer for doing the rollup after
7177 // this event is done being processed
7178 sHookTimerId = ::SetTimer(nullptr, 0, 0, (TIMERPROC)HookTimerForPopups);
7179 NS_ASSERTION(sHookTimerId, "Timer couldn't be created.");
7180 }
7181 }
7182
7183 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7184 int gLastMsgCode = 0;
7185 extern MSGFEventMsgInfo gMSGFEvents[];
7186 #endif
7187
7188 // Process Menu messages, rollup when popup is clicked.
MozSpecialMsgFilter(int code,WPARAM wParam,LPARAM lParam)7189 LRESULT CALLBACK nsWindow::MozSpecialMsgFilter(int code, WPARAM wParam,
7190 LPARAM lParam) {
7191 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7192 if (sProcessHook) {
7193 MSG* pMsg = (MSG*)lParam;
7194
7195 int inx = 0;
7196 while (gMSGFEvents[inx].mId != code && gMSGFEvents[inx].mStr != nullptr) {
7197 inx++;
7198 }
7199 if (code != gLastMsgCode) {
7200 if (gMSGFEvents[inx].mId == code) {
7201 #ifdef DEBUG
7202 MOZ_LOG(gWindowsLog, LogLevel::Info,
7203 ("MozSpecialMessageProc - code: 0x%X - %s hw: %p\n", code,
7204 gMSGFEvents[inx].mStr, pMsg->hwnd));
7205 #endif
7206 } else {
7207 #ifdef DEBUG
7208 MOZ_LOG(gWindowsLog, LogLevel::Info,
7209 ("MozSpecialMessageProc - code: 0x%X - %d hw: %p\n", code,
7210 gMSGFEvents[inx].mId, pMsg->hwnd));
7211 #endif
7212 }
7213 gLastMsgCode = code;
7214 }
7215 PrintEvent(pMsg->message, FALSE, FALSE);
7216 }
7217 #endif // #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7218
7219 if (sProcessHook && code == MSGF_MENU) {
7220 MSG* pMsg = (MSG*)lParam;
7221 ScheduleHookTimer(pMsg->hwnd, pMsg->message);
7222 }
7223
7224 return ::CallNextHookEx(sMsgFilterHook, code, wParam, lParam);
7225 }
7226
7227 // Process all mouse messages. Roll up when a click is in a native window
7228 // that doesn't have an nsIWidget.
MozSpecialMouseProc(int code,WPARAM wParam,LPARAM lParam)7229 LRESULT CALLBACK nsWindow::MozSpecialMouseProc(int code, WPARAM wParam,
7230 LPARAM lParam) {
7231 if (sProcessHook) {
7232 switch (WinUtils::GetNativeMessage(wParam)) {
7233 case WM_LBUTTONDOWN:
7234 case WM_RBUTTONDOWN:
7235 case WM_MBUTTONDOWN:
7236 case WM_MOUSEWHEEL:
7237 case WM_MOUSEHWHEEL: {
7238 MOUSEHOOKSTRUCT* ms = (MOUSEHOOKSTRUCT*)lParam;
7239 nsIWidget* mozWin = WinUtils::GetNSWindowPtr(ms->hwnd);
7240 if (mozWin) {
7241 // If this window is windowed plugin window, the mouse events are not
7242 // sent to us.
7243 if (static_cast<nsWindow*>(mozWin)->IsPlugin())
7244 ScheduleHookTimer(ms->hwnd, (UINT)wParam);
7245 } else {
7246 ScheduleHookTimer(ms->hwnd, (UINT)wParam);
7247 }
7248 break;
7249 }
7250 }
7251 }
7252 return ::CallNextHookEx(sCallMouseHook, code, wParam, lParam);
7253 }
7254
7255 // Process all messages. Roll up when the window is moving, or
7256 // is resizing or when maximized or mininized.
MozSpecialWndProc(int code,WPARAM wParam,LPARAM lParam)7257 LRESULT CALLBACK nsWindow::MozSpecialWndProc(int code, WPARAM wParam,
7258 LPARAM lParam) {
7259 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7260 if (sProcessHook) {
7261 CWPSTRUCT* cwpt = (CWPSTRUCT*)lParam;
7262 PrintEvent(cwpt->message, FALSE, FALSE);
7263 }
7264 #endif
7265
7266 if (sProcessHook) {
7267 CWPSTRUCT* cwpt = (CWPSTRUCT*)lParam;
7268 if (cwpt->message == WM_MOVING || cwpt->message == WM_SIZING ||
7269 cwpt->message == WM_GETMINMAXINFO) {
7270 ScheduleHookTimer(cwpt->hwnd, (UINT)cwpt->message);
7271 }
7272 }
7273
7274 return ::CallNextHookEx(sCallProcHook, code, wParam, lParam);
7275 }
7276
7277 // Register the special "hooks" for dropdown processing.
RegisterSpecialDropdownHooks()7278 void nsWindow::RegisterSpecialDropdownHooks() {
7279 NS_ASSERTION(!sMsgFilterHook, "sMsgFilterHook must be NULL!");
7280 NS_ASSERTION(!sCallProcHook, "sCallProcHook must be NULL!");
7281
7282 DISPLAY_NMM_PRT("***************** Installing Msg Hooks ***************\n");
7283
7284 // Install msg hook for moving the window and resizing
7285 if (!sMsgFilterHook) {
7286 DISPLAY_NMM_PRT("***** Hooking sMsgFilterHook!\n");
7287 sMsgFilterHook = SetWindowsHookEx(WH_MSGFILTER, MozSpecialMsgFilter,
7288 nullptr, GetCurrentThreadId());
7289 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7290 if (!sMsgFilterHook) {
7291 MOZ_LOG(gWindowsLog, LogLevel::Info,
7292 ("***** SetWindowsHookEx is NOT installed for WH_MSGFILTER!\n"));
7293 }
7294 #endif
7295 }
7296
7297 // Install msg hook for menus
7298 if (!sCallProcHook) {
7299 DISPLAY_NMM_PRT("***** Hooking sCallProcHook!\n");
7300 sCallProcHook = SetWindowsHookEx(WH_CALLWNDPROC, MozSpecialWndProc, nullptr,
7301 GetCurrentThreadId());
7302 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7303 if (!sCallProcHook) {
7304 MOZ_LOG(
7305 gWindowsLog, LogLevel::Info,
7306 ("***** SetWindowsHookEx is NOT installed for WH_CALLWNDPROC!\n"));
7307 }
7308 #endif
7309 }
7310
7311 // Install msg hook for the mouse
7312 if (!sCallMouseHook) {
7313 DISPLAY_NMM_PRT("***** Hooking sCallMouseHook!\n");
7314 sCallMouseHook = SetWindowsHookEx(WH_MOUSE, MozSpecialMouseProc, nullptr,
7315 GetCurrentThreadId());
7316 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7317 if (!sCallMouseHook) {
7318 MOZ_LOG(gWindowsLog, LogLevel::Info,
7319 ("***** SetWindowsHookEx is NOT installed for WH_MOUSE!\n"));
7320 }
7321 #endif
7322 }
7323 }
7324
7325 // Unhook special message hooks for dropdowns.
UnregisterSpecialDropdownHooks()7326 void nsWindow::UnregisterSpecialDropdownHooks() {
7327 DISPLAY_NMM_PRT(
7328 "***************** De-installing Msg Hooks ***************\n");
7329
7330 if (sCallProcHook) {
7331 DISPLAY_NMM_PRT("***** Unhooking sCallProcHook!\n");
7332 if (!::UnhookWindowsHookEx(sCallProcHook)) {
7333 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallProcHook!\n");
7334 }
7335 sCallProcHook = nullptr;
7336 }
7337
7338 if (sMsgFilterHook) {
7339 DISPLAY_NMM_PRT("***** Unhooking sMsgFilterHook!\n");
7340 if (!::UnhookWindowsHookEx(sMsgFilterHook)) {
7341 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sMsgFilterHook!\n");
7342 }
7343 sMsgFilterHook = nullptr;
7344 }
7345
7346 if (sCallMouseHook) {
7347 DISPLAY_NMM_PRT("***** Unhooking sCallMouseHook!\n");
7348 if (!::UnhookWindowsHookEx(sCallMouseHook)) {
7349 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallMouseHook!\n");
7350 }
7351 sCallMouseHook = nullptr;
7352 }
7353 }
7354
7355 // This timer is designed to only fire one time at most each time a "hook"
7356 // function is used to rollup the dropdown. In some cases, the timer may be
7357 // scheduled from the hook, but that hook event or a subsequent event may roll
7358 // up the dropdown before this timer function is executed.
7359 //
7360 // For example, if an MFC control takes focus, the combobox will lose focus and
7361 // rollup before this function fires.
HookTimerForPopups(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)7362 VOID CALLBACK nsWindow::HookTimerForPopups(HWND hwnd, UINT uMsg, UINT idEvent,
7363 DWORD dwTime) {
7364 if (sHookTimerId != 0) {
7365 // if the window is nullptr then we need to use the ID to kill the timer
7366 DebugOnly<BOOL> status = ::KillTimer(nullptr, sHookTimerId);
7367 NS_ASSERTION(status, "Hook Timer was not killed.");
7368 sHookTimerId = 0;
7369 }
7370
7371 if (sRollupMsgId != 0) {
7372 // Note: DealWithPopups does the check to make sure that the rollup widget
7373 // is set.
7374 LRESULT popupHandlingResult;
7375 nsAutoRollup autoRollup;
7376 DealWithPopups(sRollupMsgWnd, sRollupMsgId, 0, 0, &popupHandlingResult);
7377 sRollupMsgId = 0;
7378 sRollupMsgWnd = nullptr;
7379 }
7380 }
7381
ClearResourcesCallback(HWND aWnd,LPARAM aMsg)7382 BOOL CALLBACK nsWindow::ClearResourcesCallback(HWND aWnd, LPARAM aMsg) {
7383 nsWindow* window = WinUtils::GetNSWindowPtr(aWnd);
7384 if (window) {
7385 window->ClearCachedResources();
7386 }
7387 return TRUE;
7388 }
7389
ClearCachedResources()7390 void nsWindow::ClearCachedResources() {
7391 if (mLayerManager &&
7392 mLayerManager->GetBackendType() == LayersBackend::LAYERS_BASIC) {
7393 mLayerManager->ClearCachedResources();
7394 }
7395 ::EnumChildWindows(mWnd, nsWindow::ClearResourcesCallback, 0);
7396 }
7397
IsDifferentThreadWindow(HWND aWnd)7398 static bool IsDifferentThreadWindow(HWND aWnd) {
7399 return ::GetCurrentThreadId() != ::GetWindowThreadProcessId(aWnd, nullptr);
7400 }
7401
7402 // static
EventIsInsideWindow(nsWindow * aWindow)7403 bool nsWindow::EventIsInsideWindow(nsWindow* aWindow) {
7404 RECT r;
7405 ::GetWindowRect(aWindow->mWnd, &r);
7406 DWORD pos = ::GetMessagePos();
7407 POINT mp;
7408 mp.x = GET_X_LPARAM(pos);
7409 mp.y = GET_Y_LPARAM(pos);
7410
7411 // was the event inside this window?
7412 return static_cast<bool>(::PtInRect(&r, mp));
7413 }
7414
7415 // static
GetPopupsToRollup(nsIRollupListener * aRollupListener,uint32_t * aPopupsToRollup)7416 bool nsWindow::GetPopupsToRollup(nsIRollupListener* aRollupListener,
7417 uint32_t* aPopupsToRollup) {
7418 // If we're dealing with menus, we probably have submenus and we don't want
7419 // to rollup some of them if the click is in a parent menu of the current
7420 // submenu.
7421 *aPopupsToRollup = UINT32_MAX;
7422 AutoTArray<nsIWidget*, 5> widgetChain;
7423 uint32_t sameTypeCount = aRollupListener->GetSubmenuWidgetChain(&widgetChain);
7424 for (uint32_t i = 0; i < widgetChain.Length(); ++i) {
7425 nsIWidget* widget = widgetChain[i];
7426 if (EventIsInsideWindow(static_cast<nsWindow*>(widget))) {
7427 // Don't roll up if the mouse event occurred within a menu of the
7428 // same type. If the mouse event occurred in a menu higher than that,
7429 // roll up, but pass the number of popups to Rollup so that only those
7430 // of the same type close up.
7431 if (i < sameTypeCount) {
7432 return false;
7433 }
7434
7435 *aPopupsToRollup = sameTypeCount;
7436 break;
7437 }
7438 }
7439 return true;
7440 }
7441
7442 // static
NeedsToHandleNCActivateDelayed(HWND aWnd)7443 bool nsWindow::NeedsToHandleNCActivateDelayed(HWND aWnd) {
7444 // While popup is open, popup window might be activated by other application.
7445 // At this time, we need to take back focus to the previous window but it
7446 // causes flickering its nonclient area because WM_NCACTIVATE comes before
7447 // WM_ACTIVATE and we cannot know which window will take focus at receiving
7448 // WM_NCACTIVATE. Therefore, we need a hack for preventing the flickerling.
7449 //
7450 // If non-popup window receives WM_NCACTIVATE at deactivating, default
7451 // wndproc shouldn't handle it as deactivating. Instead, at receiving
7452 // WM_ACTIVIATE after that, WM_NCACTIVATE should be sent again manually.
7453 // This returns true if the window needs to handle WM_NCACTIVATE later.
7454
7455 nsWindow* window = WinUtils::GetNSWindowPtr(aWnd);
7456 return window && !window->IsPopup();
7457 }
7458
IsTouchSupportEnabled(HWND aWnd)7459 static bool IsTouchSupportEnabled(HWND aWnd) {
7460 nsWindow* topWindow =
7461 WinUtils::GetNSWindowPtr(WinUtils::GetTopLevelHWND(aWnd, true));
7462 return topWindow ? topWindow->IsTouchWindow() : false;
7463 }
7464
7465 // static
DealWithPopups(HWND aWnd,UINT aMessage,WPARAM aWParam,LPARAM aLParam,LRESULT * aResult)7466 bool nsWindow::DealWithPopups(HWND aWnd, UINT aMessage, WPARAM aWParam,
7467 LPARAM aLParam, LRESULT* aResult) {
7468 NS_ASSERTION(aResult, "Bad outResult");
7469
7470 // XXX Why do we use the return value of WM_MOUSEACTIVATE for all messages?
7471 *aResult = MA_NOACTIVATE;
7472
7473 if (!::IsWindowVisible(aWnd)) {
7474 return false;
7475 }
7476
7477 nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
7478 NS_ENSURE_TRUE(rollupListener, false);
7479
7480 nsCOMPtr<nsIWidget> popup = rollupListener->GetRollupWidget();
7481 if (!popup) {
7482 return false;
7483 }
7484
7485 static bool sSendingNCACTIVATE = false;
7486 static bool sPendingNCACTIVATE = false;
7487 uint32_t popupsToRollup = UINT32_MAX;
7488
7489 bool consumeRollupEvent = false;
7490
7491 nsWindow* popupWindow = static_cast<nsWindow*>(popup.get());
7492 UINT nativeMessage = WinUtils::GetNativeMessage(aMessage);
7493 switch (nativeMessage) {
7494 case WM_TOUCH:
7495 if (!IsTouchSupportEnabled(aWnd)) {
7496 // If APZ is disabled, don't allow touch inputs to dismiss popups. The
7497 // compatibility mouse events will do it instead.
7498 return false;
7499 }
7500 MOZ_FALLTHROUGH;
7501 case WM_LBUTTONDOWN:
7502 case WM_RBUTTONDOWN:
7503 case WM_MBUTTONDOWN:
7504 case WM_NCLBUTTONDOWN:
7505 case WM_NCRBUTTONDOWN:
7506 case WM_NCMBUTTONDOWN:
7507 if (nativeMessage != WM_TOUCH && IsTouchSupportEnabled(aWnd) &&
7508 MOUSE_INPUT_SOURCE() == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
7509 // If any of these mouse events are really compatibility events that
7510 // Windows is sending for touch inputs, then don't allow them to dismiss
7511 // popups when APZ is enabled (instead we do the dismissing as part of
7512 // WM_TOUCH handling which is more correct).
7513 // If we don't do this, then when the user lifts their finger after a
7514 // long-press, the WM_RBUTTONDOWN compatibility event that Windows sends
7515 // us will dismiss the contextmenu popup that we displayed as part of
7516 // handling the long-tap-up.
7517 return false;
7518 }
7519 if (!EventIsInsideWindow(popupWindow) &&
7520 GetPopupsToRollup(rollupListener, &popupsToRollup)) {
7521 break;
7522 }
7523 return false;
7524 case WM_POINTERDOWN: {
7525 WinPointerEvents pointerEvents;
7526 if (!pointerEvents.ShouldRollupOnPointerEvent(nativeMessage, aWParam)) {
7527 return false;
7528 }
7529 if (!GetPopupsToRollup(rollupListener, &popupsToRollup)) {
7530 return false;
7531 }
7532 // Can't use EventIsInsideWindow to check whether the event is inside
7533 // the popup window. It's because EventIsInsideWindow gets message
7534 // coordinates by GetMessagePos, which returns physical screen
7535 // coordinates at WM_POINTERDOWN.
7536 POINT pt;
7537 pt.x = GET_X_LPARAM(aLParam);
7538 pt.y = GET_Y_LPARAM(aLParam);
7539 RECT r;
7540 ::GetWindowRect(popupWindow->mWnd, &r);
7541 if (::PtInRect(&r, pt) != 0) {
7542 // Don't roll up if the event is inside the popup window.
7543 return false;
7544 }
7545 } break;
7546 case WM_MOUSEWHEEL:
7547 case WM_MOUSEHWHEEL:
7548 // We need to check if the popup thinks that it should cause closing
7549 // itself when mouse wheel events are fired outside the rollup widget.
7550 if (!EventIsInsideWindow(popupWindow)) {
7551 // Check if we should consume this event even if we don't roll-up:
7552 consumeRollupEvent = rollupListener->ShouldConsumeOnMouseWheelEvent();
7553 *aResult = MA_ACTIVATE;
7554 if (rollupListener->ShouldRollupOnMouseWheelEvent() &&
7555 GetPopupsToRollup(rollupListener, &popupsToRollup)) {
7556 break;
7557 }
7558 }
7559 return consumeRollupEvent;
7560
7561 case WM_ACTIVATEAPP:
7562 break;
7563
7564 case WM_ACTIVATE:
7565 // NOTE: Don't handle WA_INACTIVE for preventing popup taking focus
7566 // because we cannot distinguish it's caused by mouse or not.
7567 if (LOWORD(aWParam) == WA_ACTIVE && aLParam) {
7568 nsWindow* window = WinUtils::GetNSWindowPtr(aWnd);
7569 if (window && window->IsPopup()) {
7570 // Cancel notifying widget listeners of deactivating the previous
7571 // active window (see WM_KILLFOCUS case in ProcessMessage()).
7572 sJustGotDeactivate = false;
7573 // Reactivate the window later.
7574 ::PostMessageW(aWnd, MOZ_WM_REACTIVATE, aWParam, aLParam);
7575 return true;
7576 }
7577 // Don't rollup the popup when focus moves back to the parent window
7578 // from a popup because such case is caused by strange mouse drivers.
7579 nsWindow* prevWindow =
7580 WinUtils::GetNSWindowPtr(reinterpret_cast<HWND>(aLParam));
7581 if (prevWindow && prevWindow->IsPopup()) {
7582 return false;
7583 }
7584 } else if (LOWORD(aWParam) == WA_INACTIVE) {
7585 nsWindow* activeWindow =
7586 WinUtils::GetNSWindowPtr(reinterpret_cast<HWND>(aLParam));
7587 if (sPendingNCACTIVATE && NeedsToHandleNCActivateDelayed(aWnd)) {
7588 // If focus moves to non-popup widget or focusable popup, the window
7589 // needs to update its nonclient area.
7590 if (!activeWindow || !activeWindow->IsPopup()) {
7591 sSendingNCACTIVATE = true;
7592 ::SendMessageW(aWnd, WM_NCACTIVATE, false, 0);
7593 sSendingNCACTIVATE = false;
7594 }
7595 sPendingNCACTIVATE = false;
7596 }
7597 // If focus moves from/to popup, we don't need to rollup the popup
7598 // because such case is caused by strange mouse drivers.
7599 if (activeWindow) {
7600 if (activeWindow->IsPopup()) {
7601 return false;
7602 }
7603 nsWindow* deactiveWindow = WinUtils::GetNSWindowPtr(aWnd);
7604 if (deactiveWindow && deactiveWindow->IsPopup()) {
7605 return false;
7606 }
7607 }
7608 } else if (LOWORD(aWParam) == WA_CLICKACTIVE) {
7609 // If the WM_ACTIVATE message is caused by a click in a popup,
7610 // we should not rollup any popups.
7611 nsWindow* window = WinUtils::GetNSWindowPtr(aWnd);
7612 if ((window && window->IsPopup()) ||
7613 !GetPopupsToRollup(rollupListener, &popupsToRollup)) {
7614 return false;
7615 }
7616 }
7617 break;
7618
7619 case MOZ_WM_REACTIVATE:
7620 // The previous active window should take back focus.
7621 if (::IsWindow(reinterpret_cast<HWND>(aLParam))) {
7622 ::SetForegroundWindow(reinterpret_cast<HWND>(aLParam));
7623 }
7624 return true;
7625
7626 case WM_NCACTIVATE:
7627 if (!aWParam && !sSendingNCACTIVATE &&
7628 NeedsToHandleNCActivateDelayed(aWnd)) {
7629 // Don't just consume WM_NCACTIVATE. It doesn't handle only the
7630 // nonclient area state change.
7631 ::DefWindowProcW(aWnd, aMessage, TRUE, aLParam);
7632 // Accept the deactivating because it's necessary to receive following
7633 // WM_ACTIVATE.
7634 *aResult = TRUE;
7635 sPendingNCACTIVATE = true;
7636 return true;
7637 }
7638 return false;
7639
7640 case WM_MOUSEACTIVATE:
7641 if (!EventIsInsideWindow(popupWindow) &&
7642 GetPopupsToRollup(rollupListener, &popupsToRollup)) {
7643 // WM_MOUSEACTIVATE may be caused by moving the mouse (e.g., X-mouse
7644 // of TweakUI is enabled. Then, check if the popup should be rolled up
7645 // with rollup listener. If not, just consume the message.
7646 if (HIWORD(aLParam) == WM_MOUSEMOVE &&
7647 !rollupListener->ShouldRollupOnMouseActivate()) {
7648 return true;
7649 }
7650 // Otherwise, it should be handled by wndproc.
7651 return false;
7652 }
7653
7654 // Prevent the click inside the popup from causing a change in window
7655 // activation. Since the popup is shown non-activated, we need to eat any
7656 // requests to activate the window while it is displayed. Windows will
7657 // automatically activate the popup on the mousedown otherwise.
7658 return true;
7659
7660 case WM_SHOWWINDOW:
7661 // If the window is being minimized, close popups.
7662 if (aLParam == SW_PARENTCLOSING) {
7663 break;
7664 }
7665 return false;
7666
7667 case WM_KILLFOCUS:
7668 // If focus moves to other window created in different process/thread,
7669 // e.g., a plugin window, popups should be rolled up.
7670 if (IsDifferentThreadWindow(reinterpret_cast<HWND>(aWParam))) {
7671 break;
7672 }
7673 return false;
7674
7675 case WM_MOVING:
7676 case WM_MENUSELECT:
7677 break;
7678
7679 default:
7680 return false;
7681 }
7682
7683 // Only need to deal with the last rollup for left mouse down events.
7684 NS_ASSERTION(!nsAutoRollup::GetLastRollup(), "last rollup is null");
7685
7686 if (nativeMessage == WM_TOUCH || nativeMessage == WM_LBUTTONDOWN ||
7687 nativeMessage == WM_POINTERDOWN) {
7688 nsIntPoint pos;
7689 if (nativeMessage == WM_TOUCH) {
7690 if (nsWindow* win = WinUtils::GetNSWindowPtr(aWnd)) {
7691 pos = win->GetTouchCoordinates(aWParam, aLParam);
7692 }
7693 } else {
7694 POINT pt;
7695 pt.x = GET_X_LPARAM(aLParam);
7696 pt.y = GET_Y_LPARAM(aLParam);
7697 ::ClientToScreen(aWnd, &pt);
7698 pos = nsIntPoint(pt.x, pt.y);
7699 }
7700
7701 nsIContent* lastRollup;
7702 consumeRollupEvent =
7703 rollupListener->Rollup(popupsToRollup, true, &pos, &lastRollup);
7704 nsAutoRollup::SetLastRollup(lastRollup);
7705 } else {
7706 consumeRollupEvent =
7707 rollupListener->Rollup(popupsToRollup, true, nullptr, nullptr);
7708 }
7709
7710 // Tell hook to stop processing messages
7711 sProcessHook = false;
7712 sRollupMsgId = 0;
7713 sRollupMsgWnd = nullptr;
7714
7715 // If we are NOT supposed to be consuming events, let it go through
7716 if (consumeRollupEvent && nativeMessage != WM_RBUTTONDOWN) {
7717 *aResult = MA_ACTIVATE;
7718 return true;
7719 }
7720
7721 return false;
7722 }
7723
7724 /**************************************************************
7725 **************************************************************
7726 **
7727 ** BLOCK: Misc. utility methods and functions.
7728 **
7729 ** General use.
7730 **
7731 **************************************************************
7732 **************************************************************/
7733
7734 // Note that the result of GetTopLevelWindow method can be different from the
7735 // result of WinUtils::GetTopLevelHWND(). The result can be non-floating
7736 // window. Because our top level window may be contained in another window
7737 // which is not managed by us.
GetTopLevelWindow(bool aStopOnDialogOrPopup)7738 nsWindow* nsWindow::GetTopLevelWindow(bool aStopOnDialogOrPopup) {
7739 nsWindow* curWindow = this;
7740
7741 while (true) {
7742 if (aStopOnDialogOrPopup) {
7743 switch (curWindow->mWindowType) {
7744 case eWindowType_dialog:
7745 case eWindowType_popup:
7746 return curWindow;
7747 default:
7748 break;
7749 }
7750 }
7751
7752 // Retrieve the top level parent or owner window
7753 nsWindow* parentWindow = curWindow->GetParentWindow(true);
7754
7755 if (!parentWindow) return curWindow;
7756
7757 curWindow = parentWindow;
7758 }
7759 }
7760
gEnumWindowsProc(HWND hwnd,LPARAM lParam)7761 static BOOL CALLBACK gEnumWindowsProc(HWND hwnd, LPARAM lParam) {
7762 DWORD pid;
7763 ::GetWindowThreadProcessId(hwnd, &pid);
7764 if (pid == GetCurrentProcessId() && ::IsWindowVisible(hwnd)) {
7765 gWindowsVisible = true;
7766 return FALSE;
7767 }
7768 return TRUE;
7769 }
7770
CanTakeFocus()7771 bool nsWindow::CanTakeFocus() {
7772 gWindowsVisible = false;
7773 EnumWindows(gEnumWindowsProc, 0);
7774 if (!gWindowsVisible) {
7775 return true;
7776 } else {
7777 HWND fgWnd = ::GetForegroundWindow();
7778 if (!fgWnd) {
7779 return true;
7780 }
7781 DWORD pid;
7782 GetWindowThreadProcessId(fgWnd, &pid);
7783 if (pid == GetCurrentProcessId()) {
7784 return true;
7785 }
7786 }
7787 return false;
7788 }
7789
GetMainWindowClass()7790 /* static */ const wchar_t* nsWindow::GetMainWindowClass() {
7791 static const wchar_t* sMainWindowClass = nullptr;
7792 if (!sMainWindowClass) {
7793 nsAutoString className;
7794 Preferences::GetString("ui.window_class_override", className);
7795 if (!className.IsEmpty()) {
7796 sMainWindowClass = wcsdup(className.get());
7797 } else {
7798 sMainWindowClass = kClassNameGeneral;
7799 }
7800 }
7801 return sMainWindowClass;
7802 }
7803
lParamToScreen(LPARAM lParam)7804 LPARAM nsWindow::lParamToScreen(LPARAM lParam) {
7805 POINT pt;
7806 pt.x = GET_X_LPARAM(lParam);
7807 pt.y = GET_Y_LPARAM(lParam);
7808 ::ClientToScreen(mWnd, &pt);
7809 return MAKELPARAM(pt.x, pt.y);
7810 }
7811
lParamToClient(LPARAM lParam)7812 LPARAM nsWindow::lParamToClient(LPARAM lParam) {
7813 POINT pt;
7814 pt.x = GET_X_LPARAM(lParam);
7815 pt.y = GET_Y_LPARAM(lParam);
7816 ::ScreenToClient(mWnd, &pt);
7817 return MAKELPARAM(pt.x, pt.y);
7818 }
7819
PickerOpen()7820 void nsWindow::PickerOpen() { mPickerDisplayCount++; }
7821
PickerClosed()7822 void nsWindow::PickerClosed() {
7823 NS_ASSERTION(mPickerDisplayCount > 0, "mPickerDisplayCount out of sync!");
7824 if (!mPickerDisplayCount) return;
7825 mPickerDisplayCount--;
7826 if (!mPickerDisplayCount && mDestroyCalled) {
7827 Destroy();
7828 }
7829 }
7830
WidgetTypeSupportsAcceleration()7831 bool nsWindow::WidgetTypeSupportsAcceleration() {
7832 // We don't currently support using an accelerated layer manager with
7833 // transparent windows so don't even try. I'm also not sure if we even
7834 // want to support this case. See bug 593471.
7835 //
7836 // Also see bug 1150376, D3D11 composition can cause issues on some devices
7837 // on Windows 7 where presentation fails randomly for windows with drop
7838 // shadows.
7839 return mTransparencyMode != eTransparencyTransparent &&
7840 !(IsPopup() && DeviceManagerDx::Get()->IsWARP());
7841 }
7842
SetCandidateWindowForPlugin(const CandidateWindowPosition & aPosition)7843 void nsWindow::SetCandidateWindowForPlugin(
7844 const CandidateWindowPosition& aPosition) {
7845 CANDIDATEFORM form;
7846 form.dwIndex = 0;
7847 if (aPosition.mExcludeRect) {
7848 form.dwStyle = CFS_EXCLUDE;
7849 form.rcArea.left = aPosition.mRect.X();
7850 form.rcArea.top = aPosition.mRect.Y();
7851 form.rcArea.right = aPosition.mRect.XMost();
7852 form.rcArea.bottom = aPosition.mRect.YMost();
7853 } else {
7854 form.dwStyle = CFS_CANDIDATEPOS;
7855 }
7856 form.ptCurrentPos.x = aPosition.mPoint.x;
7857 form.ptCurrentPos.y = aPosition.mPoint.y;
7858
7859 IMEHandler::SetCandidateWindow(this, &form);
7860 }
7861
DefaultProcOfPluginEvent(const WidgetPluginEvent & aEvent)7862 void nsWindow::DefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent) {
7863 const NPEvent* pPluginEvent =
7864 static_cast<const NPEvent*>(aEvent.mPluginEvent);
7865
7866 if (NS_WARN_IF(!pPluginEvent)) {
7867 return;
7868 }
7869
7870 if (!mWnd) {
7871 return;
7872 }
7873
7874 // For WM_IME_*COMPOSITION
7875 IMEHandler::DefaultProcOfPluginEvent(this, pPluginEvent);
7876
7877 CallWindowProcW(GetPrevWindowProc(), mWnd, pPluginEvent->event,
7878 pPluginEvent->wParam, pPluginEvent->lParam);
7879 }
7880
EnableIMEForPlugin(bool aEnable)7881 void nsWindow::EnableIMEForPlugin(bool aEnable) {
7882 // Current IME state isn't plugin, ignore this call
7883 if (NS_WARN_IF(mInputContext.mIMEState.mEnabled != IMEState::PLUGIN)) {
7884 return;
7885 }
7886
7887 InputContext inputContext = GetInputContext();
7888 if (aEnable) {
7889 inputContext.mHTMLInputType.AssignLiteral("text");
7890 } else {
7891 inputContext.mHTMLInputType.AssignLiteral("password");
7892 }
7893 SetInputContext(inputContext, InputContextAction());
7894 }
7895
OnWindowedPluginKeyEvent(const NativeEventData & aKeyEventData,nsIKeyEventInPluginCallback * aCallback)7896 nsresult nsWindow::OnWindowedPluginKeyEvent(
7897 const NativeEventData& aKeyEventData,
7898 nsIKeyEventInPluginCallback* aCallback) {
7899 if (NS_WARN_IF(!mWnd)) {
7900 return NS_OK;
7901 }
7902 const WinNativeKeyEventData* eventData =
7903 static_cast<const WinNativeKeyEventData*>(aKeyEventData);
7904 switch (eventData->mMessage) {
7905 case WM_KEYDOWN:
7906 case WM_SYSKEYDOWN: {
7907 MSG mozMsg = WinUtils::InitMSG(MOZ_WM_KEYDOWN, eventData->mWParam,
7908 eventData->mLParam, mWnd);
7909 ModifierKeyState modifierKeyState(eventData->mModifiers);
7910 NativeKey nativeKey(this, mozMsg, modifierKeyState,
7911 eventData->GetKeyboardLayout());
7912 return nativeKey.HandleKeyDownMessage() ? NS_SUCCESS_EVENT_CONSUMED
7913 : NS_OK;
7914 }
7915 case WM_KEYUP:
7916 case WM_SYSKEYUP: {
7917 MSG mozMsg = WinUtils::InitMSG(MOZ_WM_KEYUP, eventData->mWParam,
7918 eventData->mLParam, mWnd);
7919 ModifierKeyState modifierKeyState(eventData->mModifiers);
7920 NativeKey nativeKey(this, mozMsg, modifierKeyState,
7921 eventData->GetKeyboardLayout());
7922 return nativeKey.HandleKeyUpMessage() ? NS_SUCCESS_EVENT_CONSUMED : NS_OK;
7923 }
7924 default:
7925 // We shouldn't consume WM_*CHAR messages here even if the preceding
7926 // keydown or keyup event on the plugin is consumed. It should be
7927 // managed in each plugin window rather than top level window.
7928 return NS_OK;
7929 }
7930 }
7931
OnPointerEvents(UINT msg,WPARAM aWParam,LPARAM aLParam)7932 bool nsWindow::OnPointerEvents(UINT msg, WPARAM aWParam, LPARAM aLParam) {
7933 if (!mPointerEvents.ShouldHandleWinPointerMessages(msg, aWParam)) {
7934 return false;
7935 }
7936 if (!mPointerEvents.ShouldFirePointerEventByWinPointerMessages()) {
7937 // We have to handle WM_POINTER* to fetch and cache pen related information
7938 // and fire WidgetMouseEvent with the cached information the WM_*BUTTONDOWN
7939 // handler. This is because Windows doesn't support ::DoDragDrop in the
7940 // touch or pen message handlers.
7941 mPointerEvents.ConvertAndCachePointerInfo(msg, aWParam);
7942 // Don't consume the Windows WM_POINTER* messages
7943 return false;
7944 }
7945 // When dispatching mouse events with pen, there may be some
7946 // WM_POINTERUPDATE messages between WM_POINTERDOWN and WM_POINTERUP with
7947 // small movements. Those events will reset sLastMousePoint and reset
7948 // sLastClickCount. To prevent that, we keep the last pen down position
7949 // and compare it with the subsequent WM_POINTERUPDATE. If the movement is
7950 // smaller than GetSystemMetrics(SM_CXDRAG), then we suppress firing
7951 // eMouseMove for WM_POINTERUPDATE.
7952 static POINT sLastPointerDownPoint = {0};
7953
7954 // We don't support chorded buttons for pen. Keep the button at
7955 // WM_POINTERDOWN.
7956 static WidgetMouseEvent::buttonType sLastPenDownButton =
7957 WidgetMouseEvent::eLeftButton;
7958 static bool sPointerDown = false;
7959
7960 EventMessage message;
7961 WidgetMouseEvent::buttonType button = WidgetMouseEvent::eLeftButton;
7962 switch (msg) {
7963 case WM_POINTERDOWN: {
7964 LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(aLParam),
7965 GET_Y_LPARAM(aLParam));
7966 sLastPointerDownPoint.x = eventPoint.x;
7967 sLastPointerDownPoint.y = eventPoint.y;
7968 message = eMouseDown;
7969 button = IS_POINTER_SECONDBUTTON_WPARAM(aWParam)
7970 ? WidgetMouseEvent::eRightButton
7971 : WidgetMouseEvent::eLeftButton;
7972 sLastPenDownButton = button;
7973 sPointerDown = true;
7974 } break;
7975 case WM_POINTERUP:
7976 message = eMouseUp;
7977 MOZ_ASSERT(sPointerDown, "receive WM_POINTERUP w/o WM_POINTERDOWN");
7978 button =
7979 sPointerDown ? sLastPenDownButton : WidgetMouseEvent::eLeftButton;
7980 sPointerDown = false;
7981 break;
7982 case WM_POINTERUPDATE:
7983 message = eMouseMove;
7984 if (sPointerDown) {
7985 LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(aLParam),
7986 GET_Y_LPARAM(aLParam));
7987 int32_t movementX = sLastPointerDownPoint.x > eventPoint.x
7988 ? sLastPointerDownPoint.x - eventPoint.x
7989 : eventPoint.x - sLastPointerDownPoint.x;
7990 int32_t movementY = sLastPointerDownPoint.y > eventPoint.y
7991 ? sLastPointerDownPoint.y - eventPoint.y
7992 : eventPoint.y - sLastPointerDownPoint.y;
7993 bool insideMovementThreshold =
7994 movementX < (int32_t)::GetSystemMetrics(SM_CXDRAG) &&
7995 movementY < (int32_t)::GetSystemMetrics(SM_CYDRAG);
7996
7997 if (insideMovementThreshold) {
7998 // Suppress firing eMouseMove for WM_POINTERUPDATE if the movement
7999 // from last WM_POINTERDOWN is smaller than SM_CXDRAG / SM_CYDRAG
8000 return false;
8001 }
8002 button = sLastPenDownButton;
8003 }
8004 break;
8005 case WM_POINTERLEAVE:
8006 message = eMouseExitFromWidget;
8007 break;
8008 default:
8009 return false;
8010 }
8011 uint32_t pointerId = mPointerEvents.GetPointerId(aWParam);
8012 POINTER_PEN_INFO penInfo;
8013 mPointerEvents.GetPointerPenInfo(pointerId, &penInfo);
8014
8015 // Windows defines the pen pressure is normalized to a range between 0 and
8016 // 1024. Convert it to float.
8017 float pressure = penInfo.pressure ? (float)penInfo.pressure / 1024 : 0;
8018 int16_t buttons = sPointerDown ? button == WidgetMouseEvent::eLeftButton
8019 ? WidgetMouseEvent::eLeftButtonFlag
8020 : WidgetMouseEvent::eRightButtonFlag
8021 : WidgetMouseEvent::eNoButtonFlag;
8022 WinPointerInfo pointerInfo(pointerId, penInfo.tiltX, penInfo.tiltY, pressure,
8023 buttons);
8024
8025 // The aLParam of WM_POINTER* is the screen location. Convert it to client
8026 // location
8027 LPARAM newLParam = lParamToClient(aLParam);
8028 DispatchMouseEvent(message, aWParam, newLParam, false, button,
8029 nsIDOMMouseEvent::MOZ_SOURCE_PEN, &pointerInfo);
8030 // Consume WM_POINTER* to stop Windows fires WM_*BUTTONDOWN / WM_*BUTTONUP
8031 // WM_MOUSEMOVE.
8032 return true;
8033 }
8034
GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData * aInitData)8035 void nsWindow::GetCompositorWidgetInitData(
8036 mozilla::widget::CompositorWidgetInitData* aInitData) {
8037 *aInitData = WinCompositorWidgetInitData(
8038 reinterpret_cast<uintptr_t>(mWnd),
8039 reinterpret_cast<uintptr_t>(static_cast<nsIWidget*>(this)),
8040 mTransparencyMode);
8041 }
8042
SynchronouslyRepaintOnResize()8043 bool nsWindow::SynchronouslyRepaintOnResize() {
8044 return !gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled();
8045 }
8046