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