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