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