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