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