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/. */
8 * nsWindow - Native window management and event handling.
10 * nsWindow is organized into a set of major blocks and
11 * block subsections. The layout is as follows:
16 * nsIWidget methods and utilities
17 * nsSwitchToUIThread impl.
18 * nsSwitchToUIThread methods and utilities
20 * Event initialization
25 * OnEvent event handlers
26 * IME management and accessibility
32 * Search for "BLOCK:" to find major blocks.
33 * Search for "SECTION:" to find specific sections.
35 * Blocks should be split out into separate files if they
36 * become unmanageable.
38 * Notable related sources:
40 * nsWindowDefs.h - Definitions, macros, structs, enums
42 * nsWindowDbg.h/.cpp - Debug related code and directives.
43 * nsWindowGfx.h/.cpp - Graphics and painting.
47 /**************************************************************
48 **************************************************************
54 **************************************************************
55 **************************************************************/
57 #include "gfx2DGlue.h"
59 #include "gfxPlatform.h"
61 #include "mozilla/AppShutdown.h"
62 #include "mozilla/AutoRestore.h"
63 #include "mozilla/Likely.h"
64 #include "mozilla/PreXULSkeletonUI.h"
65 #include "mozilla/Logging.h"
66 #include "mozilla/MathAlgorithms.h"
67 #include "mozilla/MiscEvents.h"
68 #include "mozilla/MouseEvents.h"
69 #include "mozilla/PresShell.h"
70 #include "mozilla/ScopeExit.h"
71 #include "mozilla/StaticPrefs_browser.h"
72 #include "mozilla/SwipeTracker.h"
73 #include "mozilla/TouchEvents.h"
74 #include "mozilla/TimeStamp.h"
76 #include "mozilla/ipc/MessageChannel.h"
81 #include "nsWindowTaskbarConcealer.h"
82 #include "nsAppRunner.h"
93 #include <propvarutil.h>
96 #include "mozilla/Logging.h"
100 #include "mozilla/WidgetTraceEvent.h"
101 #include "nsContentUtils.h"
102 #include "nsISupportsPrimitives.h"
103 #include "nsITheme.h"
104 #include "nsIObserverService.h"
105 #include "nsIScreenManager.h"
106 #include "imgIContainer.h"
108 #include "nsIRollupListener.h"
109 #include "nsIClipboard.h"
110 #include "WinMouseScrollHandler.h"
111 #include "nsFontMetrics.h"
112 #include "nsIFontEnumerator.h"
115 #include "nsThreadUtils.h"
116 #include "nsNativeCharsetUtils.h"
117 #include "nsGkAtoms.h"
119 #include "nsAppDirectoryServiceDefs.h"
120 #include "nsWidgetsCID.h"
121 #include "nsTHashtable.h"
122 #include "nsHashKeys.h"
123 #include "nsString.h"
124 #include "mozilla/Components.h"
125 #include "nsNativeThemeWin.h"
126 #include "nsXULPopupManager.h"
127 #include "nsWindowsDllInterceptor.h"
128 #include "nsLayoutUtils.h"
130 #include "nsWindowGfx.h"
131 #include "gfxWindowsPlatform.h"
132 #include "gfxDWriteFonts.h"
133 #include "nsPrintfCString.h"
134 #include "mozilla/Preferences.h"
135 #include "SystemTimeConverter.h"
136 #include "WinTaskbar.h"
137 #include "WidgetUtils.h"
138 #include "WinWindowOcclusionTracker.h"
139 #include "nsIWidgetListener.h"
140 #include "mozilla/dom/Document.h"
141 #include "mozilla/dom/MouseEventBinding.h"
142 #include "mozilla/dom/Touch.h"
143 #include "mozilla/gfx/2D.h"
144 #include "mozilla/gfx/GPUProcessManager.h"
145 #include "mozilla/intl/LocaleService.h"
146 #include "mozilla/layers/WebRenderLayerManager.h"
147 #include "mozilla/WindowsVersion.h"
148 #include "mozilla/TextEvents.h" // For WidgetKeyboardEvent
149 #include "mozilla/TextEventDispatcherListener.h"
150 #include "mozilla/widget/nsAutoRollup.h"
151 #include "mozilla/widget/PlatformWidgetTypes.h"
152 #include "mozilla/widget/Screen.h"
153 #include "nsStyleConsts.h"
154 #include "nsBidiKeyboard.h"
155 #include "nsStyleConsts.h"
156 #include "gfxConfig.h"
157 #include "InProcessWinCompositorWidget.h"
158 #include "InputDeviceUtils.h"
159 #include "ScreenHelperWin.h"
160 #include "mozilla/StaticPrefs_apz.h"
161 #include "mozilla/StaticPrefs_dom.h"
162 #include "mozilla/StaticPrefs_gfx.h"
163 #include "mozilla/StaticPrefs_layout.h"
164 #include "mozilla/StaticPrefs_widget.h"
165 #include "nsNativeAppSupportWin.h"
166 #include "mozilla/browser/NimbusFeatures.h"
168 #include "nsIGfxInfo.h"
169 #include "nsUXThemeConstants.h"
170 #include "KeyboardLayout.h"
171 #include "nsNativeDragTarget.h"
172 #include <mmsystem.h> // needed for WIN32_LEAN_AND_MEAN
174 #include <richedit.h>
176 #if defined(ACCESSIBILITY)
179 # include "mozilla/a11y/Logging.h"
183 # include <winuser.h>
184 # include "nsAccessibilityService.h"
185 # include "mozilla/a11y/DocAccessible.h"
186 # include "mozilla/a11y/LazyInstantiator.h"
187 # include "mozilla/a11y/Platform.h"
188 # if !defined(WINABLEAPI)
189 # include <winable.h>
190 # endif // !defined(WINABLEAPI)
191 #endif // defined(ACCESSIBILITY)
193 #include "WindowsUIUtils.h"
195 #include "nsWindowDefs.h"
197 #include "nsCrashOnException.h"
199 #include "nsIContent.h"
201 #include "mozilla/BackgroundHangMonitor.h"
202 #include "WinIMEHandler.h"
208 #include "InkCollector.h"
210 // ERROR from wingdi.h (below) gets undefined by some code.
212 // #define RGN_ERROR ERROR
215 #if !defined(SM_CONVERTIBLESLATEMODE)
216 # define SM_CONVERTIBLESLATEMODE 0x2003
219 // Win 8.1+ (_WIN32_WINNT_WINBLUE)
220 #if !defined(WM_DPICHANGED)
221 # define WM_DPICHANGED 0x02E0
224 // Win 8+ (_WIN32_WINNT_WIN8)
225 #if !defined(EVENT_OBJECT_CLOAKED)
226 # define EVENT_OBJECT_CLOAKED 0x8017
227 # define EVENT_OBJECT_UNCLOAKED 0x8018
230 #include "mozilla/gfx/DeviceManagerDx.h"
231 #include "mozilla/layers/APZInputBridge.h"
232 #include "mozilla/layers/InputAPZContext.h"
233 #include "mozilla/layers/KnowsCompositor.h"
234 #include "InputData.h"
236 #include "mozilla/TaskController.h"
237 #include "mozilla/Telemetry.h"
238 #include "mozilla/webrender/WebRenderAPI.h"
239 #include "mozilla/layers/IAPZCTreeManager.h"
241 #include "DirectManipulationOwner.h"
243 using namespace mozilla
;
244 using namespace mozilla::dom
;
245 using namespace mozilla::gfx
;
246 using namespace mozilla::layers
;
247 using namespace mozilla::widget
;
248 using namespace mozilla::plugins
;
250 /**************************************************************
251 **************************************************************
255 ** nsWindow Class static initializations and global variables.
257 **************************************************************
258 **************************************************************/
260 /**************************************************************
262 * SECTION: nsWindow statics
264 **************************************************************/
265 static const wchar_t kUser32LibName
[] = L
"user32.dll";
267 uint32_t nsWindow::sInstanceCount
= 0;
268 bool nsWindow::sIsOleInitialized
= false;
269 nsIWidget::Cursor
nsWindow::sCurrentCursor
= {};
270 nsWindow
* nsWindow::sCurrentWindow
= nullptr;
271 bool nsWindow::sJustGotDeactivate
= false;
272 bool nsWindow::sJustGotActivate
= false;
273 bool nsWindow::sIsInMouseCapture
= false;
275 // Hook Data Members for Dropdowns. sProcessHook Tells the
276 // hook methods whether they should be processing the hook
278 HHOOK
nsWindow::sMsgFilterHook
= nullptr;
279 HHOOK
nsWindow::sCallProcHook
= nullptr;
280 HHOOK
nsWindow::sCallMouseHook
= nullptr;
281 bool nsWindow::sProcessHook
= false;
282 UINT
nsWindow::sRollupMsgId
= 0;
283 HWND
nsWindow::sRollupMsgWnd
= nullptr;
284 UINT
nsWindow::sHookTimerId
= 0;
286 // Used to prevent dispatching mouse events that do not originate from user
288 POINT
nsWindow::sLastMouseMovePoint
= {0};
290 bool nsWindow::sIsRestoringSession
= false;
292 bool nsWindow::sTouchInjectInitialized
= false;
293 InjectTouchInputPtr
nsWindow::sInjectTouchFuncPtr
;
295 static SystemTimeConverter
<DWORD
>& TimeConverter() {
296 static SystemTimeConverter
<DWORD
> timeConverterSingleton
;
297 return timeConverterSingleton
;
300 // Global event hook for window cloaking. Never deregistered.
301 // - `Nothing` if not yet set.
302 // - `Some(nullptr)` if no attempt should be made to set it.
303 static mozilla::Maybe
<HWINEVENTHOOK
> sWinCloakEventHook
=
304 IsWin8OrLater() ? Nothing() : Some(HWINEVENTHOOK(nullptr));
305 static mozilla::LazyLogModule
sCloakingLog("DWMCloaking");
309 class CurrentWindowsTimeGetter
{
311 explicit CurrentWindowsTimeGetter(HWND aWnd
) : mWnd(aWnd
) {}
313 DWORD
GetCurrentTime() const { return ::GetTickCount(); }
315 void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp
& aNow
) {
316 DWORD currentTime
= GetCurrentTime();
317 if (sBackwardsSkewStamp
&& currentTime
== sLastPostTime
) {
318 // There's already one inflight with this timestamp. Don't
322 sBackwardsSkewStamp
= Some(aNow
);
323 sLastPostTime
= currentTime
;
324 static_assert(sizeof(WPARAM
) >= sizeof(DWORD
),
325 "Can't fit a DWORD in a WPARAM");
326 ::PostMessage(mWnd
, MOZ_WM_SKEWFIX
, sLastPostTime
, 0);
329 static bool GetAndClearBackwardsSkewStamp(DWORD aPostTime
,
330 TimeStamp
* aOutSkewStamp
) {
331 if (aPostTime
!= sLastPostTime
) {
332 // The SKEWFIX message is stale; we've sent a new one since then.
336 MOZ_ASSERT(sBackwardsSkewStamp
);
337 *aOutSkewStamp
= sBackwardsSkewStamp
.value();
338 sBackwardsSkewStamp
= Nothing();
343 static Maybe
<TimeStamp
> sBackwardsSkewStamp
;
344 static DWORD sLastPostTime
;
348 Maybe
<TimeStamp
> CurrentWindowsTimeGetter::sBackwardsSkewStamp
;
349 DWORD
CurrentWindowsTimeGetter::sLastPostTime
= 0;
351 } // namespace mozilla
353 /**************************************************************
355 * SECTION: globals variables
357 **************************************************************/
359 static const char* sScreenManagerContractID
=
360 "@mozilla.org/gfx/screenmanager;1";
362 extern mozilla::LazyLogModule gWindowsLog
;
364 static NS_DEFINE_CID(kCClipboardCID
, NS_CLIPBOARD_CID
);
366 // General purpose user32.dll hook object
367 static WindowsDllInterceptor sUser32Intercept
;
369 // When the client area is extended out into the default window frame area,
370 // this is the minimum amount of space along the edge of resizable windows
371 // we will always display a resize cursor in, regardless of the underlying
373 static const int32_t kResizableBorderMinSize
= 3;
375 // Getting this object from the window server can be expensive. Keep it
376 // around, also get it off the main thread. (See bug 1640852)
377 StaticRefPtr
<IVirtualDesktopManager
> gVirtualDesktopManager
;
378 static bool gInitializedVirtualDesktopManager
= false;
380 // We should never really try to accelerate windows bigger than this. In some
381 // cases this might lead to no D3D9 acceleration where we could have had it
382 // but D3D9 does not reliably report when it supports bigger windows. 8192
383 // is as safe as we can get, we know at least D3D10 hardware always supports
384 // this, other hardware we expect to report correctly in D3D9.
385 #define MAX_ACCELERATED_DIMENSION 8192
387 // On window open (as well as after), Windows has an unfortunate habit of
388 // sending rather a lot of WM_NCHITTEST messages. Because we have to do point
389 // to DOM target conversions for these, we cache responses for a given
390 // coordinate this many milliseconds:
391 #define HITTEST_CACHE_LIFETIME_MS 50
393 #if defined(ACCESSIBILITY)
398 * Windows touchscreen code works by setting a global WH_GETMESSAGE hook and
399 * injecting tiptsf.dll. The touchscreen process then posts registered messages
400 * to our main thread. The tiptsf hook picks up those registered messages and
401 * uses them as commands, some of which call into UIA, which then calls into
402 * MSAA, which then sends WM_GETOBJECT to us.
404 * We can get ahead of this by installing our own thread-local WH_GETMESSAGE
405 * hook. Since thread-local hooks are called ahead of global hooks, we will
406 * see these registered messages before tiptsf does. At this point we can then
407 * raise a flag that blocks a11y before invoking CallNextHookEx which will then
408 * invoke the global tiptsf hook. Then when we see WM_GETOBJECT, we check the
409 * flag by calling TIPMessageHandler::IsA11yBlocked().
411 * For Windows 8, we also hook tiptsf!ProcessCaretEvents, which is an a11y hook
412 * function that also calls into UIA.
414 class TIPMessageHandler
{
416 ~TIPMessageHandler() {
418 ::UnhookWindowsHookEx(mHook
);
422 static void Initialize() {
423 if (!IsWin8OrLater()) {
431 sInstance
= new TIPMessageHandler();
432 ClearOnShutdown(&sInstance
);
435 static bool IsA11yBlocked() {
440 return sInstance
->mA11yBlockCount
> 0;
444 TIPMessageHandler() : mHook(nullptr), mA11yBlockCount(0) {
445 MOZ_ASSERT(NS_IsMainThread());
447 // Registered messages used by tiptsf
448 mMessages
[0] = ::RegisterWindowMessage(L
"ImmersiveFocusNotification");
449 mMessages
[1] = ::RegisterWindowMessage(L
"TipCloseMenus");
450 mMessages
[2] = ::RegisterWindowMessage(L
"TabletInputPanelOpening");
451 mMessages
[3] = ::RegisterWindowMessage(L
"IHM Pen or Touch Event noticed");
452 mMessages
[4] = ::RegisterWindowMessage(L
"ProgrammabilityCaretVisibility");
453 mMessages
[5] = ::RegisterWindowMessage(L
"CaretTrackingUpdateIPHidden");
454 mMessages
[6] = ::RegisterWindowMessage(L
"CaretTrackingUpdateIPInfo");
456 mHook
= ::SetWindowsHookEx(WH_GETMESSAGE
, &TIPHook
, nullptr,
457 ::GetCurrentThreadId());
460 // On touchscreen devices, tiptsf.dll will have been loaded when STA COM was
461 // first initialized.
462 if (!IsWin10OrLater() && GetModuleHandle(L
"tiptsf.dll") &&
463 !sProcessCaretEventsStub
) {
464 sTipTsfInterceptor
.Init("tiptsf.dll");
465 DebugOnly
<bool> ok
= sProcessCaretEventsStub
.Set(
466 sTipTsfInterceptor
, "ProcessCaretEvents", &ProcessCaretEventsHook
);
470 if (!sSendMessageTimeoutWStub
) {
471 sUser32Intercept
.Init("user32.dll");
472 DebugOnly
<bool> hooked
= sSendMessageTimeoutWStub
.Set(
473 sUser32Intercept
, "SendMessageTimeoutW", &SendMessageTimeoutWHook
);
478 class MOZ_RAII A11yInstantiationBlocker
{
480 A11yInstantiationBlocker() {
481 if (!TIPMessageHandler::sInstance
) {
484 ++TIPMessageHandler::sInstance
->mA11yBlockCount
;
487 ~A11yInstantiationBlocker() {
488 if (!TIPMessageHandler::sInstance
) {
491 MOZ_ASSERT(TIPMessageHandler::sInstance
->mA11yBlockCount
> 0);
492 --TIPMessageHandler::sInstance
->mA11yBlockCount
;
496 friend class A11yInstantiationBlocker
;
498 static LRESULT CALLBACK
TIPHook(int aCode
, WPARAM aWParam
, LPARAM aLParam
) {
499 if (aCode
< 0 || !sInstance
) {
500 return ::CallNextHookEx(nullptr, aCode
, aWParam
, aLParam
);
503 MSG
* msg
= reinterpret_cast<MSG
*>(aLParam
);
504 UINT
& msgCode
= msg
->message
;
506 for (uint32_t i
= 0; i
< ArrayLength(sInstance
->mMessages
); ++i
) {
507 if (msgCode
== sInstance
->mMessages
[i
]) {
508 A11yInstantiationBlocker block
;
509 return ::CallNextHookEx(nullptr, aCode
, aWParam
, aLParam
);
513 return ::CallNextHookEx(nullptr, aCode
, aWParam
, aLParam
);
516 static void CALLBACK
ProcessCaretEventsHook(HWINEVENTHOOK aWinEventHook
,
517 DWORD aEvent
, HWND aHwnd
,
518 LONG aObjectId
, LONG aChildId
,
519 DWORD aGeneratingTid
,
521 A11yInstantiationBlocker block
;
522 sProcessCaretEventsStub(aWinEventHook
, aEvent
, aHwnd
, aObjectId
, aChildId
,
523 aGeneratingTid
, aEventTime
);
526 static LRESULT WINAPI
SendMessageTimeoutWHook(HWND aHwnd
, UINT aMsgCode
,
527 WPARAM aWParam
, LPARAM aLParam
,
528 UINT aFlags
, UINT aTimeout
,
529 PDWORD_PTR aMsgResult
) {
530 // We don't want to handle this unless the message is a WM_GETOBJECT that we
531 // want to block, and the aHwnd is a nsWindow that belongs to the current
532 // (i.e., main) thread.
533 if (!aMsgResult
|| aMsgCode
!= WM_GETOBJECT
||
534 static_cast<LONG
>(aLParam
) != OBJID_CLIENT
|| !::NS_IsMainThread() ||
535 !WinUtils::GetNSWindowPtr(aHwnd
) || !IsA11yBlocked()) {
536 return sSendMessageTimeoutWStub(aHwnd
, aMsgCode
, aWParam
, aLParam
, aFlags
,
537 aTimeout
, aMsgResult
);
540 // In this case we want to fake the result that would happen if we had
541 // decided not to handle WM_GETOBJECT in our WndProc. We hand the message
542 // off to DefWindowProc to accomplish this.
543 *aMsgResult
= static_cast<DWORD_PTR
>(
544 ::DefWindowProcW(aHwnd
, aMsgCode
, aWParam
, aLParam
));
546 return static_cast<LRESULT
>(TRUE
);
549 static WindowsDllInterceptor sTipTsfInterceptor
;
550 static WindowsDllInterceptor::FuncHookType
<WINEVENTPROC
>
551 sProcessCaretEventsStub
;
552 static WindowsDllInterceptor::FuncHookType
<decltype(&SendMessageTimeoutW
)>
553 sSendMessageTimeoutWStub
;
554 static StaticAutoPtr
<TIPMessageHandler
> sInstance
;
558 uint32_t mA11yBlockCount
;
561 WindowsDllInterceptor
TIPMessageHandler::sTipTsfInterceptor
;
562 WindowsDllInterceptor::FuncHookType
<WINEVENTPROC
>
563 TIPMessageHandler::sProcessCaretEventsStub
;
564 WindowsDllInterceptor::FuncHookType
<decltype(&SendMessageTimeoutW
)>
565 TIPMessageHandler::sSendMessageTimeoutWStub
;
566 StaticAutoPtr
<TIPMessageHandler
> TIPMessageHandler::sInstance
;
568 } // namespace mozilla
570 #endif // defined(ACCESSIBILITY)
574 // This task will get the VirtualDesktopManager from the generic thread pool
575 // since doing this on the main thread on startup causes performance issues.
579 // This should be fine and should not require any locking, as when the main
580 // thread will access it, if it races with this function it will either find
581 // it to be null or to have a valid value.
582 class InitializeVirtualDesktopManagerTask
: public Task
{
584 InitializeVirtualDesktopManagerTask() : Task(false, kDefaultPriorityValue
) {}
586 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
587 bool GetName(nsACString
& aName
) override
{
588 aName
.AssignLiteral("InitializeVirtualDesktopManagerTask");
593 virtual bool Run() override
{
594 if (!IsWin10OrLater()) {
598 RefPtr
<IVirtualDesktopManager
> desktopManager
;
599 HRESULT hr
= ::CoCreateInstance(
600 CLSID_VirtualDesktopManager
, NULL
, CLSCTX_INPROC_SERVER
,
601 __uuidof(IVirtualDesktopManager
), getter_AddRefs(desktopManager
));
606 gVirtualDesktopManager
= desktopManager
;
611 static bool GetMouseVanishSystemPref(bool aShouldUpdate
) {
612 static Maybe
<bool> sCachedMouseVanishSystemPref
;
615 sCachedMouseVanishSystemPref
.reset();
618 if (sCachedMouseVanishSystemPref
.isNothing()) {
619 BOOL mouseVanishSystemPref
;
620 BOOL ok
= ::SystemParametersInfo(SPI_GETMOUSEVANISH
, 0,
621 &mouseVanishSystemPref
, 0);
622 // If getting system pref failed, just use user pref.
623 sCachedMouseVanishSystemPref
.emplace(
624 ok
? mouseVanishSystemPref
625 : StaticPrefs::widget_windows_hide_cursor_when_typing());
628 return *sCachedMouseVanishSystemPref
;
631 static bool IsMouseVanishKey(WPARAM aVirtKey
) {
654 case VK_PRIOR
: // PgUp
655 case VK_NEXT
: // PgDn
656 case 0xff: // Undefined. May be sent for Fn key.
659 // Vanish unless Ctrl or Alt is also pressed, or if a key in
660 // a relevant range is pressed.
661 // The range between VK_F1 and VK_LAUNCH_APP2 includes control,
662 // function, browser, volume and media keys, all of which we ignore.
663 return (GetKeyState(VK_CONTROL
) & 0x8000) != 0x8000 &&
664 (GetKeyState(VK_MENU
) & 0x8000) != 0x8000 &&
665 (aVirtKey
< VK_F1
|| aVirtKey
> VK_LAUNCH_APP2
);
670 * Hide/unhide the cursor if the correct Windows and Firefox settings are set.
672 static void MaybeHideCursor(bool aShouldHide
) {
673 static bool sMouseExists
= [] {
674 // Before the first call to ShowCursor, the visibility count is 0
675 // if there is a mouse installed and -1 if not.
676 int count
= ::ShowCursor(FALSE
);
685 static bool sIsHidden
= false;
686 bool shouldHide
= aShouldHide
&&
687 StaticPrefs::widget_windows_hide_cursor_when_typing() &&
688 GetMouseVanishSystemPref(false);
690 if (shouldHide
!= sIsHidden
) {
691 [[maybe_unused
]] int count
= ::ShowCursor(aShouldHide
? FALSE
: TRUE
);
692 MOZ_ASSERT(count
== (aShouldHide
? -1 : 0));
693 sIsHidden
= shouldHide
;
697 // Ground-truth query: does Windows claim the window is cloaked right now?
698 static bool IsCloaked(HWND hwnd
) {
700 HRESULT hr
= ::DwmGetWindowAttribute(hwnd
, DWMWA_CLOAKED
, &cloakedState
,
701 sizeof(cloakedState
));
704 MOZ_LOG(sCloakingLog
, LogLevel::Warning
,
705 ("failed (%08lX) to query cloaking state for HWND %p", hr
, hwnd
));
709 return cloakedState
!= 0;
712 } // namespace mozilla
714 /**************************************************************
715 **************************************************************
717 ** BLOCK: nsIWidget impl.
719 ** nsIWidget interface implementation, broken down into
722 **************************************************************
723 **************************************************************/
725 /**************************************************************
727 * SECTION: nsWindow construction and destruction
729 **************************************************************/
731 nsWindow::nsWindow(bool aIsChildWindow
)
732 : nsBaseWidget(BorderStyle::Default
),
733 mBrush(::CreateSolidBrush(NSRGB_2_COLOREF(::GetSysColor(COLOR_BTNFACE
)))),
734 mFrameState(std::in_place
, this),
735 mIsChildWindow(aIsChildWindow
),
736 mLastPaintEndTime(TimeStamp::Now()),
737 mCachedHitTestTime(TimeStamp::Now()),
738 mSizeConstraintsScale(GetDefaultScale().scale
),
739 mDesktopId("DesktopIdMutex") {
740 MOZ_ASSERT(mWindowType
== WindowType::Child
);
742 if (!gInitializedVirtualDesktopManager
) {
743 TaskController::Get()->AddTask(
744 MakeAndAddRef
<InitializeVirtualDesktopManagerTask
>());
745 gInitializedVirtualDesktopManager
= true;
748 // Global initialization
749 if (!sInstanceCount
) {
750 // Global app registration id for Win7 and up. See
751 // WinTaskbar.cpp for details.
752 // MSIX packages explicitly do not support setting the appid from within
753 // the app, as it is set in the package manifest instead.
754 if (!WinUtils::HasPackageIdentity()) {
755 mozilla::widget::WinTaskbar::RegisterAppUserModelID();
757 KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0));
758 #if defined(ACCESSIBILITY)
759 mozilla::TIPMessageHandler::Initialize();
760 #endif // defined(ACCESSIBILITY)
761 if (SUCCEEDED(::OleInitialize(nullptr))) {
762 sIsOleInitialized
= true;
764 NS_ASSERTION(sIsOleInitialized
, "***** OLE is not initialized!\n");
765 MouseScrollHandler::Initialize();
767 nsUXThemeData::UpdateNativeThemeInfo();
768 RedirectedKeyDownMessageManager::Forget();
769 if (mPointerEvents
.ShouldEnableInkCollector()) {
770 InkCollector::sInkCollector
= new InkCollector();
777 nsWindow::~nsWindow() {
780 // If the widget was released without calling Destroy() then the native window
781 // still exists, and we need to destroy it. Destroy() will early-return if it
782 // was already called. In any case it is important to call it before
783 // destroying mPresentLock (cf. 1156182).
786 // Free app icon resources. This must happen after `OnDestroy` (see bug
788 if (mIconSmall
) ::DestroyIcon(mIconSmall
);
790 if (mIconBig
) ::DestroyIcon(mIconBig
);
795 if (sInstanceCount
== 0) {
796 if (InkCollector::sInkCollector
) {
797 InkCollector::sInkCollector
->Shutdown();
798 InkCollector::sInkCollector
= nullptr;
800 IMEHandler::Terminate();
802 if (sIsOleInitialized
) {
803 ::OleFlushClipboard();
805 sIsOleInitialized
= false;
809 NS_IF_RELEASE(mNativeDragTarget
);
812 /**************************************************************
814 * SECTION: nsIWidget::Create, nsIWidget::Destroy
816 * Creating and destroying windows for this widget.
818 **************************************************************/
820 // Allow Derived classes to modify the height that is passed
821 // when the window is created or resized.
822 int32_t nsWindow::GetHeight(int32_t aProposedHeight
) { return aProposedHeight
; }
824 void nsWindow::SendAnAPZEvent(InputData
& aEvent
) {
825 LRESULT popupHandlingResult
;
826 if (DealWithPopups(mWnd
, MOZ_WM_DMANIP
, 0, 0, &popupHandlingResult
)) {
827 // We need to consume the event after using it to roll up the popup(s).
831 if (mSwipeTracker
&& aEvent
.mInputType
== PANGESTURE_INPUT
) {
832 // Give the swipe tracker a first pass at the event. If a new pan gesture
833 // has been started since the beginning of the swipe, the swipe tracker
834 // will know to ignore the event.
835 nsEventStatus status
=
836 mSwipeTracker
->ProcessEvent(aEvent
.AsPanGestureInput());
837 if (status
== nsEventStatus_eConsumeNoDefault
) {
842 APZEventResult result
;
844 result
= mAPZC
->InputBridge()->ReceiveInputEvent(aEvent
);
846 if (result
.GetStatus() == nsEventStatus_eConsumeNoDefault
) {
850 MOZ_ASSERT(aEvent
.mInputType
== PANGESTURE_INPUT
||
851 aEvent
.mInputType
== PINCHGESTURE_INPUT
);
853 if (aEvent
.mInputType
== PANGESTURE_INPUT
) {
854 PanGestureInput
& panInput
= aEvent
.AsPanGestureInput();
855 WidgetWheelEvent event
= panInput
.ToWidgetEvent(this);
857 if (MayStartSwipeForNonAPZ(panInput
)) {
861 event
= MayStartSwipeForAPZ(panInput
, result
);
864 ProcessUntransformedAPZEvent(&event
, result
);
869 PinchGestureInput
& pinchInput
= aEvent
.AsPinchGestureInput();
870 WidgetWheelEvent event
= pinchInput
.ToWidgetEvent(this);
871 ProcessUntransformedAPZEvent(&event
, result
);
874 void nsWindow::RecreateDirectManipulationIfNeeded() {
875 DestroyDirectManipulation();
877 if (mWindowType
!= WindowType::TopLevel
&& mWindowType
!= WindowType::Popup
) {
881 if (!(StaticPrefs::apz_allow_zooming() ||
882 StaticPrefs::apz_windows_use_direct_manipulation()) ||
883 StaticPrefs::apz_windows_force_disable_direct_manipulation()) {
887 if (!IsWin10OrLater()) {
888 // Chrome source said the Windows Direct Manipulation implementation had
889 // important bugs until Windows 10 (although IE on Windows 8.1 seems to use
890 // Direct Manipulation).
894 mDmOwner
= MakeUnique
<DirectManipulationOwner
>(this);
896 LayoutDeviceIntRect
bounds(mBounds
.X(), mBounds
.Y(), mBounds
.Width(),
897 GetHeight(mBounds
.Height()));
898 mDmOwner
->Init(bounds
);
901 void nsWindow::ResizeDirectManipulationViewport() {
903 LayoutDeviceIntRect
bounds(mBounds
.X(), mBounds
.Y(), mBounds
.Width(),
904 GetHeight(mBounds
.Height()));
905 mDmOwner
->ResizeViewport(bounds
);
909 void nsWindow::DestroyDirectManipulation() {
916 // Create the proper widget
917 nsresult
nsWindow::Create(nsIWidget
* aParent
, nsNativeWidget aNativeParent
,
918 const LayoutDeviceIntRect
& aRect
,
919 widget::InitData
* aInitData
) {
920 // Historical note: there was once some belief and/or intent that nsWindows
921 // could be created on arbitrary threads, and this may still be reflected in
923 MOZ_ASSERT(NS_IsMainThread());
925 widget::InitData defaultInitData
;
926 if (!aInitData
) aInitData
= &defaultInitData
;
928 nsIWidget
* baseParent
=
929 aInitData
->mWindowType
== WindowType::Dialog
||
930 aInitData
->mWindowType
== WindowType::TopLevel
||
931 aInitData
->mWindowType
== WindowType::Invisible
935 mIsTopWidgetWindow
= (nullptr == baseParent
);
938 // Ensure that the toolkit is created.
939 nsToolkit::GetToolkit();
941 BaseCreate(baseParent
, aInitData
);
944 if (aParent
) { // has a nsIWidget parent
945 parent
= aParent
? (HWND
)aParent
->GetNativeData(NS_NATIVE_WINDOW
) : nullptr;
947 } else { // has a nsNative parent
948 parent
= (HWND
)aNativeParent
;
950 aNativeParent
? WinUtils::GetNSWindowPtr((HWND
)aNativeParent
) : nullptr;
953 mIsRTL
= aInitData
->mRTL
;
954 mForMenupopupFrame
= aInitData
->mForMenupopupFrame
;
955 mOpeningAnimationSuppressed
= aInitData
->mIsAnimationSuppressed
;
956 mAlwaysOnTop
= aInitData
->mAlwaysOnTop
;
957 mResizable
= aInitData
->mResizable
;
959 DWORD style
= WindowStyle();
960 DWORD extendedStyle
= WindowExStyle();
962 // When window is PiP window on Windows7, WS_EX_COMPOSITED is set to suppress
963 // flickering during resizing with hardware acceleration.
964 bool isPIPWindow
= aInitData
&& aInitData
->mPIPWindow
;
965 if (isPIPWindow
&& !IsWin8OrLater() &&
966 gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING
) &&
967 WidgetTypeSupportsAcceleration()) {
968 extendedStyle
|= WS_EX_COMPOSITED
;
971 if (mWindowType
== WindowType::Popup
) {
976 if (!IsWin8OrLater() && HasBogusPopupsDropShadowOnMultiMonitor() &&
977 ShouldUseOffMainThreadCompositing()) {
978 extendedStyle
|= WS_EX_COMPOSITED
;
980 } else if (mWindowType
== WindowType::Invisible
) {
981 // Make sure CreateWindowEx succeeds at creating a toplevel window
982 style
&= ~0x40000000; // WS_CHILDWINDOW
984 // See if the caller wants to explictly set clip children and clip siblings
985 if (aInitData
->mClipChildren
) {
986 style
|= WS_CLIPCHILDREN
;
988 style
&= ~WS_CLIPCHILDREN
;
990 if (aInitData
->mClipSiblings
) {
991 style
|= WS_CLIPSIBLINGS
;
995 const wchar_t* className
= ChooseWindowClass(mWindowType
, mForMenupopupFrame
);
997 // Take specific actions when creating the first top-level window
998 static bool sFirstTopLevelWindowCreated
= false;
999 if (aInitData
->mWindowType
== WindowType::TopLevel
&& !aParent
&&
1000 !sFirstTopLevelWindowCreated
) {
1001 sFirstTopLevelWindowCreated
= true;
1002 mWnd
= ConsumePreXULSkeletonUIHandle();
1003 auto skeletonUIError
= GetPreXULSkeletonUIErrorReason();
1004 if (skeletonUIError
) {
1005 nsAutoString
errorString(
1006 GetPreXULSkeletonUIErrorString(skeletonUIError
.value()));
1007 Telemetry::ScalarSet(
1008 Telemetry::ScalarID::STARTUP_SKELETON_UI_DISABLED_REASON
,
1012 MOZ_ASSERT(style
== kPreXULSkeletonUIWindowStyle
,
1013 "The skeleton UI window style should match the expected "
1014 "style for the first window created");
1015 MOZ_ASSERT(extendedStyle
== kPreXULSkeletonUIWindowStyleEx
,
1016 "The skeleton UI window extended style should match the "
1017 "expected extended style for the first window created");
1019 ::GetWindowThreadProcessId(mWnd
, nullptr) == ::GetCurrentThreadId(),
1020 "The skeleton UI window should be created on the same thread as "
1022 mIsShowingPreXULSkeletonUI
= true;
1024 // If we successfully consumed the pre-XUL skeleton UI, just update
1025 // our internal state to match what is currently being displayed.
1027 mIsCloaked
= mozilla::IsCloaked(mWnd
);
1028 mFrameState
->ConsumePreXULSkeletonState(WasPreXULSkeletonUIMaximized());
1030 // These match the margins set in browser-tabsintitlebar.js with
1031 // default prefs on Windows. Bug 1673092 tracks lining this up with
1032 // that more correctly instead of hard-coding it.
1033 SetNonClientMargins(LayoutDeviceIntMargin(0, 2, 2, 2));
1035 // Reset the WNDPROC for this window and its whole class, as we had
1036 // to use our own WNDPROC when creating the the skeleton UI window.
1037 ::SetWindowLongPtrW(mWnd
, GWLP_WNDPROC
,
1038 reinterpret_cast<LONG_PTR
>(
1039 WinUtils::NonClientDpiScalingDefWindowProcW
));
1040 ::SetClassLongPtrW(mWnd
, GCLP_WNDPROC
,
1041 reinterpret_cast<LONG_PTR
>(
1042 WinUtils::NonClientDpiScalingDefWindowProcW
));
1048 ::CreateWindowExW(extendedStyle
, className
, L
"", style
, aRect
.X(),
1049 aRect
.Y(), aRect
.Width(), GetHeight(aRect
.Height()),
1050 parent
, nullptr, nsToolkit::mDllInstance
, nullptr);
1054 NS_WARNING("nsWindow CreateWindowEx failed.");
1055 return NS_ERROR_FAILURE
;
1058 if (!sWinCloakEventHook
) {
1059 MOZ_LOG(sCloakingLog
, LogLevel::Info
, ("Registering cloaking event hook"));
1061 // C++03 lambda approximation until P2173R1 is available (-std=c++2b)
1062 struct StdcallLambda
{
1063 static void CALLBACK
OnCloakUncloakHook(HWINEVENTHOOK hWinEventHook
,
1064 DWORD event
, HWND hwnd
,
1065 LONG idObject
, LONG idChild
,
1066 DWORD idEventThread
,
1067 DWORD dwmsEventTime
) {
1068 const bool isCloaked
= event
== EVENT_OBJECT_CLOAKED
? true : false;
1069 nsWindow::OnCloakEvent(hwnd
, isCloaked
);
1073 const HWINEVENTHOOK hook
= ::SetWinEventHook(
1074 EVENT_OBJECT_CLOAKED
, EVENT_OBJECT_UNCLOAKED
, HMODULE(nullptr),
1075 &StdcallLambda::OnCloakUncloakHook
, ::GetCurrentProcessId(),
1076 ::GetCurrentThreadId(), WINEVENT_OUTOFCONTEXT
);
1077 sWinCloakEventHook
= Some(hook
);
1080 const DWORD err
= ::GetLastError();
1081 MOZ_LOG(sCloakingLog
, LogLevel::Error
,
1082 ("Failed to register cloaking event hook! GLE = %lu (0x%lX)", err
,
1087 if (aInitData
->mIsPrivate
) {
1088 if (NimbusFeatures::GetBool("majorRelease2022"_ns
,
1089 "feltPrivacyWindowSeparation"_ns
, true) &&
1090 // Although permanent Private Browsing mode is indeed Private Browsing,
1091 // we choose to make it look like regular Firefox in terms of the icon
1092 // it uses (which also means we shouldn't use the Private Browsing
1094 !StaticPrefs::browser_privatebrowsing_autostart()) {
1095 RefPtr
<IPropertyStore
> pPropStore
;
1096 if (!FAILED(SHGetPropertyStoreForWindow(mWnd
, IID_IPropertyStore
,
1097 getter_AddRefs(pPropStore
)))) {
1100 // make sure we're using the private browsing AUMID so that taskbar
1101 // grouping works properly
1102 Unused
<< NS_WARN_IF(
1103 !mozilla::widget::WinTaskbar::GenerateAppUserModelID(aumid
, true));
1104 if (!FAILED(InitPropVariantFromString(aumid
.get(), &pv
))) {
1105 if (!FAILED(pPropStore
->SetValue(PKEY_AppUserModel_ID
, pv
))) {
1106 pPropStore
->Commit();
1109 PropVariantClear(&pv
);
1112 HICON icon
= ::LoadIconW(::GetModuleHandleW(nullptr),
1113 MAKEINTRESOURCEW(IDI_PBMODE
));
1119 mDeviceNotifyHandle
= InputDeviceUtils::RegisterNotification(mWnd
);
1121 // If mDefaultScale is set before mWnd has been set, it will have the scale of
1122 // the primary monitor, rather than the monitor that the window is actually
1123 // on. For non-popup windows this gets corrected by the WM_DPICHANGED message
1124 // which resets mDefaultScale, but for popup windows we don't reset
1125 // mDefaultScale on that message. In order to ensure that popup windows
1126 // spawned on a non-primary monitor end up with the correct scale, we reset
1127 // mDefaultScale here so that it gets recomputed using the correct monitor now
1128 // that we have a mWnd.
1129 mDefaultScale
= -1.0;
1132 DWORD dwAttribute
= TRUE
;
1133 DwmSetWindowAttribute(mWnd
, DWMWA_NONCLIENT_RTL_LAYOUT
, &dwAttribute
,
1134 sizeof dwAttribute
);
1137 UpdateDarkModeToolbar();
1139 if (mOpeningAnimationSuppressed
) {
1140 SuppressAnimation(true);
1144 ::SetWindowPos(mWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1145 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
1148 if (mWindowType
!= WindowType::Invisible
&&
1149 MouseScrollHandler::Device::IsFakeScrollableWindowNeeded()) {
1150 // Ugly Thinkpad Driver Hack (Bugs 507222 and 594977)
1152 // We create two zero-sized windows as descendants of the top-level window,
1155 // Top-level window (MozillaWindowClass)
1156 // FAKETRACKPOINTSCROLLCONTAINER (MozillaWindowClass)
1157 // FAKETRACKPOINTSCROLLABLE (MozillaWindowClass)
1159 // We need to have the middle window, otherwise the Trackpoint driver
1160 // will fail to deliver scroll messages. WM_MOUSEWHEEL messages are
1161 // sent to the FAKETRACKPOINTSCROLLABLE, which then propagate up the
1162 // window hierarchy until they are handled by nsWindow::WindowProc.
1163 // WM_HSCROLL messages are also sent to the FAKETRACKPOINTSCROLLABLE,
1164 // but these do not propagate automatically, so we have the window
1165 // procedure pretend that they were dispatched to the top-level window
1168 // The FAKETRACKPOINTSCROLLABLE needs to have the specific window styles it
1169 // is given below so that it catches the Trackpoint driver's heuristics.
1170 HWND scrollContainerWnd
= ::CreateWindowW(
1171 className
, L
"FAKETRACKPOINTSCROLLCONTAINER", WS_CHILD
| WS_VISIBLE
, 0,
1172 0, 0, 0, mWnd
, nullptr, nsToolkit::mDllInstance
, nullptr);
1173 HWND scrollableWnd
= ::CreateWindowW(
1174 className
, L
"FAKETRACKPOINTSCROLLABLE",
1175 WS_CHILD
| WS_VISIBLE
| WS_VSCROLL
| WS_TABSTOP
| 0x30, 0, 0, 0, 0,
1176 scrollContainerWnd
, nullptr, nsToolkit::mDllInstance
, nullptr);
1178 // Give the FAKETRACKPOINTSCROLLABLE window a specific ID so that
1179 // WindowProcInternal can distinguish it from the top-level window
1181 ::SetWindowLongPtrW(scrollableWnd
, GWLP_ID
, eFakeTrackPointScrollableID
);
1183 // Make FAKETRACKPOINTSCROLLABLE use nsWindow::WindowProc, and store the
1184 // old window procedure in its "user data".
1185 WNDPROC oldWndProc
= (WNDPROC
)::SetWindowLongPtrW(
1186 scrollableWnd
, GWLP_WNDPROC
, (LONG_PTR
)nsWindow::WindowProc
);
1187 ::SetWindowLongPtrW(scrollableWnd
, GWLP_USERDATA
, (LONG_PTR
)oldWndProc
);
1190 // We will start receiving native events after associating with our native
1191 // window. We will also become the output of WinUtils::GetNSWindowPtr for that
1193 if (!AssociateWithNativeWindow()) {
1194 return NS_ERROR_FAILURE
;
1197 // Starting with Windows XP, a process always runs within a terminal services
1198 // session. In order to play nicely with RDP, fast user switching, and the
1199 // lock screen, we should be handling WM_WTSSESSION_CHANGE. We must register
1200 // our HWND in order to receive this message.
1201 DebugOnly
<BOOL
> wtsRegistered
=
1202 ::WTSRegisterSessionNotification(mWnd
, NOTIFY_FOR_THIS_SESSION
);
1203 NS_ASSERTION(wtsRegistered
, "WTSRegisterSessionNotification failed!\n");
1205 mDefaultIMC
.Init(this);
1206 IMEHandler::InitInputContext(this, mInputContext
);
1208 static bool a11yPrimed
= false;
1209 if (!a11yPrimed
&& mWindowType
== WindowType::TopLevel
) {
1211 if (Preferences::GetInt("accessibility.force_disabled", 0) == -1) {
1212 ::PostMessage(mWnd
, MOZ_WM_STARTA11Y
, 0, 0);
1216 RecreateDirectManipulationIfNeeded();
1221 void nsWindow::LocalesChanged() {
1222 bool isRTL
= intl::LocaleService::GetInstance()->IsAppLocaleRTL();
1223 if (mIsRTL
!= isRTL
) {
1224 DWORD dwAttribute
= isRTL
;
1225 DwmSetWindowAttribute(mWnd
, DWMWA_NONCLIENT_RTL_LAYOUT
, &dwAttribute
,
1226 sizeof dwAttribute
);
1231 // Close this nsWindow
1232 void nsWindow::Destroy() {
1233 // WM_DESTROY has already fired, avoid calling it twice
1234 if (mOnDestroyCalled
) return;
1236 // Don't destroy windows that have file pickers open, we'll tear these down
1237 // later once the picker is closed.
1238 mDestroyCalled
= true;
1239 if (mPickerDisplayCount
) return;
1241 // During the destruction of all of our children, make sure we don't get
1243 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
1245 DestroyDirectManipulation();
1248 * On windows the LayerManagerOGL destructor wants the widget to be around for
1249 * cleanup. It also would like to have the HWND intact, so we nullptr it here.
1251 DestroyLayerManager();
1253 InputDeviceUtils::UnregisterNotification(mDeviceNotifyHandle
);
1254 mDeviceNotifyHandle
= nullptr;
1256 // The DestroyWindow function destroys the specified window. The function
1257 // sends WM_DESTROY and WM_NCDESTROY messages to the window to deactivate it
1258 // and remove the keyboard focus from it. The function also destroys the
1259 // window's menu, flushes the thread message queue, destroys timers, removes
1260 // clipboard ownership, and breaks the clipboard viewer chain (if the window
1261 // is at the top of the viewer chain).
1263 // If the specified window is a parent or owner window, DestroyWindow
1264 // automatically destroys the associated child or owned windows when it
1265 // destroys the parent or owner window. The function first destroys child or
1266 // owned windows, and then it destroys the parent or owner window.
1267 VERIFY(::DestroyWindow(mWnd
));
1269 // Our windows can be subclassed which may prevent us receiving WM_DESTROY. If
1270 // OnDestroy() didn't get called, call it now.
1271 if (false == mOnDestroyCalled
) {
1272 MSGResult msgResult
;
1273 mWindowHook
.Notify(mWnd
, WM_DESTROY
, 0, 0, msgResult
);
1278 /**************************************************************
1280 * SECTION: Window class utilities
1282 * Utilities for calculating the proper window class name for
1285 **************************************************************/
1288 const wchar_t* nsWindow::RegisterWindowClass(const wchar_t* aClassName
,
1289 UINT aExtraStyle
, LPWSTR aIconID
) {
1291 if (::GetClassInfoW(nsToolkit::mDllInstance
, aClassName
, &wc
)) {
1292 // already registered
1296 wc
.style
= CS_DBLCLKS
| aExtraStyle
;
1297 wc
.lpfnWndProc
= WinUtils::NonClientDpiScalingDefWindowProcW
;
1300 wc
.hInstance
= nsToolkit::mDllInstance
;
1302 aIconID
? ::LoadIconW(::GetModuleHandleW(nullptr), aIconID
) : nullptr;
1303 wc
.hCursor
= nullptr;
1304 wc
.hbrBackground
= nullptr;
1305 wc
.lpszMenuName
= nullptr;
1306 wc
.lpszClassName
= aClassName
;
1308 if (!::RegisterClassW(&wc
)) {
1309 // For older versions of Win32 (i.e., not XP), the registration may
1310 // fail with aExtraStyle, so we have to re-register without it.
1311 wc
.style
= CS_DBLCLKS
;
1312 ::RegisterClassW(&wc
);
1317 static LPWSTR
const gStockApplicationIcon
= MAKEINTRESOURCEW(32512);
1320 const wchar_t* nsWindow::ChooseWindowClass(WindowType aWindowType
,
1321 bool aForMenupopupFrame
) {
1322 MOZ_ASSERT_IF(aForMenupopupFrame
, aWindowType
== WindowType::Popup
);
1323 switch (aWindowType
) {
1324 case WindowType::Invisible
:
1325 return RegisterWindowClass(kClassNameHidden
, 0, gStockApplicationIcon
);
1326 case WindowType::Dialog
:
1327 return RegisterWindowClass(kClassNameDialog
, 0, 0);
1328 case WindowType::Popup
:
1329 if (aForMenupopupFrame
) {
1330 return RegisterWindowClass(kClassNameDropShadow
, CS_DROPSHADOW
,
1331 gStockApplicationIcon
);
1335 return RegisterWindowClass(GetMainWindowClass(), 0,
1336 gStockApplicationIcon
);
1340 /**************************************************************
1342 * SECTION: Window styles utilities
1344 * Return the proper windows styles and extended styles.
1346 **************************************************************/
1348 // Return nsWindow styles
1349 DWORD
nsWindow::WindowStyle() {
1352 switch (mWindowType
) {
1353 case WindowType::Child
:
1354 style
= WS_OVERLAPPED
;
1357 case WindowType::Dialog
:
1358 style
= WS_OVERLAPPED
| WS_BORDER
| WS_DLGFRAME
| WS_SYSMENU
| DS_3DLOOK
|
1359 DS_MODALFRAME
| WS_CLIPCHILDREN
;
1360 if (mBorderStyle
!= BorderStyle::Default
)
1361 style
|= WS_THICKFRAME
| WS_MINIMIZEBOX
| WS_MAXIMIZEBOX
;
1364 case WindowType::Popup
:
1365 style
= WS_POPUP
| WS_OVERLAPPED
;
1369 NS_ERROR("unknown border style");
1372 case WindowType::TopLevel
:
1373 case WindowType::Invisible
:
1374 style
= WS_OVERLAPPED
| WS_BORDER
| WS_DLGFRAME
| WS_SYSMENU
|
1375 WS_THICKFRAME
| WS_MINIMIZEBOX
| WS_MAXIMIZEBOX
| WS_CLIPCHILDREN
;
1379 if (mBorderStyle
!= BorderStyle::Default
&&
1380 mBorderStyle
!= BorderStyle::All
) {
1381 if (mBorderStyle
== BorderStyle::None
||
1382 !(mBorderStyle
& BorderStyle::Border
))
1383 style
&= ~WS_BORDER
;
1385 if (mBorderStyle
== BorderStyle::None
||
1386 !(mBorderStyle
& BorderStyle::Title
)) {
1387 style
&= ~WS_DLGFRAME
;
1390 if (mBorderStyle
== BorderStyle::None
||
1391 !(mBorderStyle
& BorderStyle::Close
))
1393 // XXX The close box can only be removed by changing the window class,
1394 // as far as I know --- roc+moz@cs.cmu.edu
1396 if (mBorderStyle
== BorderStyle::None
||
1397 !(mBorderStyle
& (BorderStyle::Menu
| BorderStyle::Close
)))
1398 style
&= ~WS_SYSMENU
;
1399 // Looks like getting rid of the system menu also does away with the
1400 // close box. So, we only get rid of the system menu if you want neither it
1401 // nor the close box. How does the Windows "Dialog" window class get just
1402 // closebox and no sysmenu? Who knows.
1404 if (mBorderStyle
== BorderStyle::None
||
1405 !(mBorderStyle
& BorderStyle::ResizeH
))
1406 style
&= ~WS_THICKFRAME
;
1408 if (mBorderStyle
== BorderStyle::None
||
1409 !(mBorderStyle
& BorderStyle::Minimize
))
1410 style
&= ~WS_MINIMIZEBOX
;
1412 if (mBorderStyle
== BorderStyle::None
||
1413 !(mBorderStyle
& BorderStyle::Maximize
))
1414 style
&= ~WS_MAXIMIZEBOX
;
1416 if (IsPopupWithTitleBar()) {
1417 style
|= WS_CAPTION
;
1418 if (mBorderStyle
& BorderStyle::Close
) {
1419 style
|= WS_SYSMENU
;
1424 if (mIsChildWindow
) {
1425 style
|= WS_CLIPCHILDREN
;
1426 if (!(style
& WS_POPUP
)) {
1427 style
|= WS_CHILD
; // WS_POPUP and WS_CHILD are mutually exclusive.
1431 VERIFY_WINDOW_STYLE(style
);
1435 // Return nsWindow extended styles
1436 DWORD
nsWindow::WindowExStyle() {
1437 switch (mWindowType
) {
1438 case WindowType::Child
:
1441 case WindowType::Dialog
:
1442 return WS_EX_WINDOWEDGE
| WS_EX_DLGMODALFRAME
;
1444 case WindowType::Popup
: {
1445 DWORD extendedStyle
= WS_EX_TOOLWINDOW
;
1446 if (mPopupLevel
== PopupLevel::Top
) extendedStyle
|= WS_EX_TOPMOST
;
1447 return extendedStyle
;
1450 NS_ERROR("unknown border style");
1453 case WindowType::TopLevel
:
1454 case WindowType::Invisible
:
1455 return WS_EX_WINDOWEDGE
;
1459 /**************************************************************
1461 * SECTION: Native window association utilities
1463 * Used in Create and Destroy. A nsWindow can associate with its
1464 * underlying native window mWnd. Once a native window is
1465 * associated with a nsWindow, its native events will be handled
1466 * by the static member function nsWindow::WindowProc. Moreover,
1467 * the association will be registered in the WinUtils association
1468 * list, that is, calling WinUtils::GetNSWindowPtr on the native
1469 * window will return the associated nsWindow. This is used in
1470 * nsWindow::WindowProc to correctly dispatch native events to
1471 * the handler methods defined in nsWindow, even though it is a
1472 * static member function.
1474 * After dissociation, the native events of the native window will
1475 * no longer be handled by nsWindow::WindowProc, and will thus not
1476 * be dispatched to the nsWindow native event handler methods.
1477 * Moreover, the association will no longer be registered in the
1478 * WinUtils association list, so calling WinUtils::GetNSWindowPtr
1479 * on the native window will return nullptr.
1481 **************************************************************/
1483 bool nsWindow::AssociateWithNativeWindow() {
1484 if (!mWnd
|| !IsWindow(mWnd
)) {
1485 NS_ERROR("Invalid window handle");
1489 // Connect the this pointer to the native window handle.
1490 // This should be done before SetWindowLongPtrW, because nsWindow::WindowProc
1491 // uses WinUtils::GetNSWindowPtr internally.
1492 WinUtils::SetNSWindowPtr(mWnd
, this);
1494 ::SetLastError(ERROR_SUCCESS
);
1495 const auto prevWndProc
= reinterpret_cast<WNDPROC
>(::SetWindowLongPtrW(
1496 mWnd
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(nsWindow::WindowProc
)));
1497 if (!prevWndProc
&& GetLastError() != ERROR_SUCCESS
) {
1498 NS_ERROR("Failure in SetWindowLongPtrW");
1499 WinUtils::SetNSWindowPtr(mWnd
, nullptr);
1503 mPrevWndProc
.emplace(prevWndProc
);
1507 void nsWindow::DissociateFromNativeWindow() {
1508 if (!mWnd
|| !IsWindow(mWnd
) || mPrevWndProc
.isNothing()) {
1512 DebugOnly
<WNDPROC
> wndProcBeforeDissociate
=
1513 reinterpret_cast<WNDPROC
>(::SetWindowLongPtrW(
1514 mWnd
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(*mPrevWndProc
)));
1515 NS_ASSERTION(wndProcBeforeDissociate
== nsWindow::WindowProc
,
1516 "Unstacked an unexpected native window procedure");
1518 WinUtils::SetNSWindowPtr(mWnd
, nullptr);
1519 mPrevWndProc
.reset();
1522 /**************************************************************
1524 * SECTION: nsIWidget::SetParent, nsIWidget::GetParent
1526 * Set or clear the parent widgets using window properties, and
1527 * handles calculating native parent handles.
1529 **************************************************************/
1531 // Get and set parent widgets
1532 void nsWindow::SetParent(nsIWidget
* aNewParent
) {
1533 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
1534 nsIWidget
* parent
= GetParent();
1536 parent
->RemoveChild(this);
1539 mParent
= aNewParent
;
1542 ReparentNativeWidget(aNewParent
);
1543 aNewParent
->AddChild(this);
1547 // If we have no parent, SetParent should return the desktop.
1548 VERIFY(::SetParent(mWnd
, nullptr));
1549 RecreateDirectManipulationIfNeeded();
1553 void nsWindow::ReparentNativeWidget(nsIWidget
* aNewParent
) {
1554 MOZ_ASSERT(aNewParent
, "null widget");
1556 mParent
= aNewParent
;
1557 if (mWindowType
== WindowType::Popup
) {
1560 HWND newParent
= (HWND
)aNewParent
->GetNativeData(NS_NATIVE_WINDOW
);
1561 NS_ASSERTION(newParent
, "Parent widget has a null native window handle");
1562 if (newParent
&& mWnd
) {
1563 ::SetParent(mWnd
, newParent
);
1564 RecreateDirectManipulationIfNeeded();
1568 nsIWidget
* nsWindow::GetParent(void) {
1569 if (mIsTopWidgetWindow
) {
1572 if (mInDtor
|| mOnDestroyCalled
) {
1578 static int32_t RoundDown(double aDouble
) {
1579 return aDouble
> 0 ? static_cast<int32_t>(floor(aDouble
))
1580 : static_cast<int32_t>(ceil(aDouble
));
1583 float nsWindow::GetDPI() {
1585 nsCOMPtr
<nsIScreen
> screen
= GetWidgetScreen();
1587 screen
->GetDpi(&dpi
);
1592 double nsWindow::GetDefaultScaleInternal() {
1593 if (mDefaultScale
<= 0.0) {
1594 mDefaultScale
= WinUtils::LogToPhysFactor(mWnd
);
1596 return mDefaultScale
;
1599 int32_t nsWindow::LogToPhys(double aValue
) {
1600 return WinUtils::LogToPhys(
1601 ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTOPRIMARY
), aValue
);
1604 nsWindow
* nsWindow::GetParentWindow(bool aIncludeOwner
) {
1605 return static_cast<nsWindow
*>(GetParentWindowBase(aIncludeOwner
));
1608 nsWindow
* nsWindow::GetParentWindowBase(bool aIncludeOwner
) {
1609 if (mIsTopWidgetWindow
) {
1610 // Must use a flag instead of mWindowType to tell if the window is the
1611 // owned by the topmost widget, because a child window can be embedded
1612 // inside a HWND which is not associated with a nsIWidget.
1616 // If this widget has already been destroyed, pretend we have no parent.
1617 // This corresponds to code in Destroy which removes the destroyed
1618 // widget from its parent's child list.
1619 if (mInDtor
|| mOnDestroyCalled
) return nullptr;
1621 // aIncludeOwner set to true implies walking the parent chain to retrieve the
1622 // root owner. aIncludeOwner set to false implies the search will stop at the
1623 // true parent (default).
1624 nsWindow
* widget
= nullptr;
1626 HWND parent
= nullptr;
1628 parent
= ::GetParent(mWnd
);
1630 parent
= ::GetAncestor(mWnd
, GA_PARENT
);
1633 widget
= WinUtils::GetNSWindowPtr(parent
);
1635 // If the widget is in the process of being destroyed then
1637 if (widget
->mInDtor
) {
1647 /**************************************************************
1649 * SECTION: nsIWidget::Show
1651 * Hide or show this component.
1653 **************************************************************/
1655 void nsWindow::Show(bool bState
) {
1656 if (bState
&& mIsShowingPreXULSkeletonUI
) {
1657 // The first time we decide to actually show the window is when we decide
1658 // that we've taken over the window from the skeleton UI, and we should
1659 // no longer treat resizes / moves specially.
1660 mIsShowingPreXULSkeletonUI
= false;
1661 #if defined(ACCESSIBILITY)
1662 // If our HWND has focus and the a11y engine hasn't started yet, fire a
1663 // focus win event. Windows already did this when the skeleton UI appeared,
1664 // but a11y wouldn't have been able to start at that point even if a client
1665 // responded. Firing this now gives clients the chance to respond with
1666 // WM_GETOBJECT, which will trigger the a11y engine. We don't want to do
1667 // this if the a11y engine has already started because it has probably
1668 // already fired focus on a descendant.
1669 if (::GetFocus() == mWnd
&& !GetAccService()) {
1670 ::NotifyWinEvent(EVENT_OBJECT_FOCUS
, mWnd
, OBJID_CLIENT
, CHILDID_SELF
);
1672 #endif // defined(ACCESSIBILITY)
1675 if (mForMenupopupFrame
) {
1676 MOZ_ASSERT(ChooseWindowClass(mWindowType
, mForMenupopupFrame
) ==
1677 kClassNameDropShadow
);
1678 const bool shouldUseDropShadow
= [&] {
1679 if (mTransparencyMode
== TransparencyMode::Transparent
) {
1682 if (HasBogusPopupsDropShadowOnMultiMonitor() &&
1683 WinUtils::GetMonitorCount() > 1 &&
1684 !gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
1685 // See bug 603793. When we try to draw D3D9/10 windows with a drop
1686 // shadow without the DWM on a secondary monitor, windows fails to
1687 // composite our windows correctly. We therefor switch off the drop
1688 // shadow for pop-up windows when the DWM is disabled and two monitors
1695 static bool sShadowEnabled
= true;
1696 if (sShadowEnabled
!= shouldUseDropShadow
) {
1697 ::SetClassLongA(mWnd
, GCL_STYLE
, shouldUseDropShadow
? CS_DROPSHADOW
: 0);
1698 sShadowEnabled
= shouldUseDropShadow
;
1701 // WS_EX_COMPOSITED conflicts with the WS_EX_LAYERED style and causes
1702 // some popup menus to become invisible.
1703 LONG_PTR exStyle
= ::GetWindowLongPtrW(mWnd
, GWL_EXSTYLE
);
1704 if (exStyle
& WS_EX_LAYERED
) {
1705 ::SetWindowLongPtrW(mWnd
, GWL_EXSTYLE
, exStyle
& ~WS_EX_COMPOSITED
);
1709 bool syncInvalidate
= false;
1711 bool wasVisible
= mIsVisible
;
1712 // Set the status now so that anyone asking during ShowWindow or
1713 // SetWindowPos would get the correct answer.
1714 mIsVisible
= bState
;
1716 // We may have cached an out of date visible state. This can happen
1717 // when session restore sets the full screen mode.
1719 mOldStyle
|= WS_VISIBLE
;
1721 mOldStyle
&= ~WS_VISIBLE
;
1725 if (!wasVisible
&& mWindowType
== WindowType::TopLevel
) {
1726 // speed up the initial paint after show for
1727 // top level windows:
1728 syncInvalidate
= true;
1730 // Set the cursor before showing the window to avoid the default wait
1732 SetCursor(Cursor
{eCursor_standard
});
1734 switch (mFrameState
->GetSizeMode()) {
1735 case nsSizeMode_Fullscreen
:
1736 ::ShowWindow(mWnd
, SW_SHOW
);
1738 case nsSizeMode_Maximized
:
1739 ::ShowWindow(mWnd
, SW_SHOWMAXIMIZED
);
1741 case nsSizeMode_Minimized
:
1742 ::ShowWindow(mWnd
, SW_SHOWMINIMIZED
);
1745 if (CanTakeFocus() && !mAlwaysOnTop
) {
1746 ::ShowWindow(mWnd
, SW_SHOWNORMAL
);
1748 ::ShowWindow(mWnd
, SW_SHOWNOACTIVATE
);
1749 // Don't flicker the window if we're restoring session
1750 if (!sIsRestoringSession
) {
1751 Unused
<< GetAttention(2);
1757 DWORD flags
= SWP_NOSIZE
| SWP_NOMOVE
| SWP_SHOWWINDOW
;
1758 if (wasVisible
) flags
|= SWP_NOZORDER
;
1759 if (mAlwaysOnTop
) flags
|= SWP_NOACTIVATE
;
1761 if (mWindowType
== WindowType::Popup
) {
1762 // ensure popups are the topmost of the TOPMOST
1763 // layer. Remember not to set the SWP_NOZORDER
1764 // flag as that might allow the taskbar to overlap
1766 flags
|= SWP_NOACTIVATE
;
1767 HWND owner
= ::GetWindow(mWnd
, GW_OWNER
);
1769 // PopupLevel::Top popups should be above all else. All other
1770 // types should be placed in front of their owner, without
1771 // changing the owner's z-level relative to other windows.
1772 if (mPopupLevel
!= PopupLevel::Top
) {
1773 ::SetWindowPos(mWnd
, owner
, 0, 0, 0, 0, flags
);
1774 ::SetWindowPos(owner
, mWnd
, 0, 0, 0, 0,
1775 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
1777 ::SetWindowPos(mWnd
, HWND_TOP
, 0, 0, 0, 0, flags
);
1780 ::SetWindowPos(mWnd
, HWND_TOPMOST
, 0, 0, 0, 0, flags
);
1783 if (mWindowType
== WindowType::Dialog
&& !CanTakeFocus())
1784 flags
|= SWP_NOACTIVATE
;
1786 ::SetWindowPos(mWnd
, HWND_TOP
, 0, 0, 0, 0, flags
);
1790 // Clear contents to avoid ghosting of old content if we display
1791 // this window again.
1792 if (wasVisible
&& mTransparencyMode
== TransparencyMode::Transparent
) {
1793 if (mCompositorWidgetDelegate
) {
1794 mCompositorWidgetDelegate
->ClearTransparentWindow();
1797 if (mWindowType
!= WindowType::Dialog
) {
1798 ::ShowWindow(mWnd
, SW_HIDE
);
1800 ::SetWindowPos(mWnd
, 0, 0, 0, 0, 0,
1801 SWP_HIDEWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOZORDER
|
1807 if (!wasVisible
&& bState
) {
1809 if (syncInvalidate
&& !mInDtor
&& !mOnDestroyCalled
) {
1810 ::UpdateWindow(mWnd
);
1814 if (mOpeningAnimationSuppressed
) {
1815 SuppressAnimation(false);
1819 /**************************************************************
1821 * SECTION: nsIWidget::IsVisible
1823 * Returns the visibility state.
1825 **************************************************************/
1827 // Return true if the component is visible, false otherwise.
1829 // This does not take cloaking into account.
1830 bool nsWindow::IsVisible() const { return mIsVisible
; }
1832 /**************************************************************
1834 * SECTION: Window clipping utilities
1836 * Used in Size and Move operations for setting the proper
1837 * window clipping regions for window transparency.
1839 **************************************************************/
1841 // XP and Vista visual styles sometimes require window clipping regions to be
1842 // applied for proper transparency. These routines are called on size and move
1844 // XXX this is apparently still needed in Windows 7 and later
1845 void nsWindow::ClearThemeRegion() {
1846 if (mWindowType
== WindowType::Popup
&& !IsPopupWithTitleBar() &&
1847 (mPopupType
== PopupType::Tooltip
|| mPopupType
== PopupType::Panel
)) {
1848 SetWindowRgn(mWnd
, nullptr, false);
1852 /**************************************************************
1854 * SECTION: Touch and APZ-related functions
1856 **************************************************************/
1858 void nsWindow::RegisterTouchWindow() {
1859 mTouchWindow
= true;
1860 ::RegisterTouchWindow(mWnd
, TWF_WANTPALM
);
1861 ::EnumChildWindows(mWnd
, nsWindow::RegisterTouchForDescendants
, 0);
1864 BOOL CALLBACK
nsWindow::RegisterTouchForDescendants(HWND aWnd
, LPARAM aMsg
) {
1865 nsWindow
* win
= WinUtils::GetNSWindowPtr(aWnd
);
1867 ::RegisterTouchWindow(aWnd
, TWF_WANTPALM
);
1872 void nsWindow::LockAspectRatio(bool aShouldLock
) {
1874 mAspectRatio
= (float)mBounds
.Width() / (float)mBounds
.Height();
1880 /**************************************************************
1882 * SECTION: nsIWidget::SetInputRegion
1884 * Sets whether the window should ignore mouse events.
1886 **************************************************************/
1887 void nsWindow::SetInputRegion(const InputRegion
& aInputRegion
) {
1888 mInputRegion
= aInputRegion
;
1891 /**************************************************************
1893 * SECTION: nsIWidget::Move, nsIWidget::Resize, nsIWidget::Size
1895 * Repositioning and sizing a window.
1897 **************************************************************/
1899 void nsWindow::SetSizeConstraints(const SizeConstraints
& aConstraints
) {
1900 SizeConstraints c
= aConstraints
;
1902 if (mWindowType
!= WindowType::Popup
&& mResizable
) {
1904 std::max(int32_t(::GetSystemMetrics(SM_CXMINTRACK
)), c
.mMinSize
.width
);
1906 std::max(int32_t(::GetSystemMetrics(SM_CYMINTRACK
)), c
.mMinSize
.height
);
1909 if (mMaxTextureSize
> 0) {
1910 // We can't make ThebesLayers bigger than this anyway.. no point it letting
1911 // a window grow bigger as we won't be able to draw content there in
1913 c
.mMaxSize
.width
= std::min(c
.mMaxSize
.width
, mMaxTextureSize
);
1914 c
.mMaxSize
.height
= std::min(c
.mMaxSize
.height
, mMaxTextureSize
);
1917 mSizeConstraintsScale
= GetDefaultScale().scale
;
1919 nsBaseWidget::SetSizeConstraints(c
);
1922 const SizeConstraints
nsWindow::GetSizeConstraints() {
1923 double scale
= GetDefaultScale().scale
;
1924 if (mSizeConstraintsScale
== scale
|| mSizeConstraintsScale
== 0.0) {
1925 return mSizeConstraints
;
1927 scale
/= mSizeConstraintsScale
;
1928 SizeConstraints c
= mSizeConstraints
;
1929 if (c
.mMinSize
.width
!= NS_MAXSIZE
) {
1930 c
.mMinSize
.width
= NSToIntRound(c
.mMinSize
.width
* scale
);
1932 if (c
.mMinSize
.height
!= NS_MAXSIZE
) {
1933 c
.mMinSize
.height
= NSToIntRound(c
.mMinSize
.height
* scale
);
1935 if (c
.mMaxSize
.width
!= NS_MAXSIZE
) {
1936 c
.mMaxSize
.width
= NSToIntRound(c
.mMaxSize
.width
* scale
);
1938 if (c
.mMaxSize
.height
!= NS_MAXSIZE
) {
1939 c
.mMaxSize
.height
= NSToIntRound(c
.mMaxSize
.height
* scale
);
1944 // Move this component
1945 void nsWindow::Move(double aX
, double aY
) {
1946 if (mWindowType
== WindowType::TopLevel
||
1947 mWindowType
== WindowType::Dialog
) {
1948 SetSizeMode(nsSizeMode_Normal
);
1951 // for top-level windows only, convert coordinates from desktop pixels
1952 // (the "parent" coordinate space) to the window's device pixel space
1954 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale
: 1.0;
1955 int32_t x
= NSToIntRound(aX
* scale
);
1956 int32_t y
= NSToIntRound(aY
* scale
);
1958 // Check to see if window needs to be moved first
1959 // to avoid a costly call to SetWindowPos. This check
1960 // can not be moved to the calling code in nsView, because
1961 // some platforms do not position child windows correctly
1963 // Only perform this check for non-popup windows, since the positioning can
1964 // in fact change even when the x/y do not. We always need to perform the
1965 // check. See bug #97805 for details.
1966 if (mWindowType
!= WindowType::Popup
&& mBounds
.IsEqualXY(x
, y
)) {
1967 // Nothing to do, since it is already positioned correctly.
1971 mBounds
.MoveTo(x
, y
);
1975 // complain if a window is moved offscreen (legal, but potentially
1977 if (mIsTopWidgetWindow
) { // only a problem for top-level windows
1978 // Make sure this window is actually on the screen before we move it
1979 // XXX: Needs multiple monitor support
1980 HDC dc
= ::GetDC(mWnd
);
1982 if (::GetDeviceCaps(dc
, TECHNOLOGY
) == DT_RASDISPLAY
) {
1984 ::SystemParametersInfo(SPI_GETWORKAREA
, 0, &workArea
, 0);
1985 // no annoying assertions. just mention the issue.
1986 if (x
< 0 || x
>= workArea
.right
|| y
< 0 || y
>= workArea
.bottom
) {
1987 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
1988 ("window moved to offscreen position\n"));
1991 ::ReleaseDC(mWnd
, dc
);
1996 // Normally, when the skeleton UI is disabled, we resize+move the window
1997 // before showing it in order to ensure that it restores to the correct
1998 // position when the user un-maximizes it. However, when we are using the
1999 // skeleton UI, this results in the skeleton UI window being moved around
2000 // undesirably before being locked back into the maximized position. To
2001 // avoid this, we simply set the placement to restore to via
2002 // SetWindowPlacement. It's a little bit more of a dance, though, since we
2003 // need to convert the workspace coords that SetWindowPlacement uses to the
2004 // screen space coordinates we normally use with SetWindowPos.
2005 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
2006 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
2007 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
2009 HMONITOR monitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONULL
);
2010 if (NS_WARN_IF(!monitor
)) {
2013 MONITORINFO mi
= {sizeof(MONITORINFO
)};
2014 VERIFY(::GetMonitorInfo(monitor
, &mi
));
2017 x
+ mi
.rcWork
.left
- mi
.rcMonitor
.left
- pl
.rcNormalPosition
.left
;
2019 y
+ mi
.rcWork
.top
- mi
.rcMonitor
.top
- pl
.rcNormalPosition
.top
;
2020 pl
.rcNormalPosition
.left
+= deltaX
;
2021 pl
.rcNormalPosition
.right
+= deltaX
;
2022 pl
.rcNormalPosition
.top
+= deltaY
;
2023 pl
.rcNormalPosition
.bottom
+= deltaY
;
2024 VERIFY(::SetWindowPlacement(mWnd
, &pl
));
2028 UINT flags
= SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOSIZE
;
2029 double oldScale
= mDefaultScale
;
2030 mResizeState
= IN_SIZEMOVE
;
2031 VERIFY(::SetWindowPos(mWnd
, nullptr, x
, y
, 0, 0, flags
));
2032 mResizeState
= NOT_RESIZING
;
2033 if (WinUtils::LogToPhysFactor(mWnd
) != oldScale
) {
2038 ResizeDirectManipulationViewport();
2042 // Resize this component
2043 void nsWindow::Resize(double aWidth
, double aHeight
, bool aRepaint
) {
2044 // for top-level windows only, convert coordinates from desktop pixels
2045 // (the "parent" coordinate space) to the window's device pixel space
2047 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale
: 1.0;
2048 int32_t width
= NSToIntRound(aWidth
* scale
);
2049 int32_t height
= NSToIntRound(aHeight
* scale
);
2051 NS_ASSERTION((width
>= 0), "Negative width passed to nsWindow::Resize");
2052 NS_ASSERTION((height
>= 0), "Negative height passed to nsWindow::Resize");
2053 if (width
< 0 || height
< 0) {
2054 gfxCriticalNoteOnce
<< "Negative passed to Resize(" << width
<< ", "
2055 << height
<< ") repaint: " << aRepaint
;
2058 ConstrainSize(&width
, &height
);
2060 // Avoid unnecessary resizing calls
2061 if (mBounds
.IsEqualSize(width
, height
)) {
2068 // Set cached value for lightweight and printing
2069 bool wasLocking
= mAspectRatio
!= 0.0;
2070 mBounds
.SizeTo(width
, height
);
2072 LockAspectRatio(true); // This causes us to refresh the mAspectRatio value
2076 // Refer to the comment above a similar check in nsWindow::Move
2077 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
2078 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
2079 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
2080 pl
.rcNormalPosition
.right
= pl
.rcNormalPosition
.left
+ width
;
2081 pl
.rcNormalPosition
.bottom
= pl
.rcNormalPosition
.top
+ GetHeight(height
);
2082 mResizeState
= RESIZING
;
2083 VERIFY(::SetWindowPlacement(mWnd
, &pl
));
2084 mResizeState
= NOT_RESIZING
;
2086 UINT flags
= SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
;
2089 flags
|= SWP_NOREDRAW
;
2093 double oldScale
= mDefaultScale
;
2094 mResizeState
= RESIZING
;
2096 ::SetWindowPos(mWnd
, nullptr, 0, 0, width
, GetHeight(height
), flags
));
2098 mResizeState
= NOT_RESIZING
;
2099 if (WinUtils::LogToPhysFactor(mWnd
) != oldScale
) {
2104 ResizeDirectManipulationViewport();
2107 if (aRepaint
) Invalidate();
2110 // Resize this component
2111 void nsWindow::Resize(double aX
, double aY
, double aWidth
, double aHeight
,
2113 // for top-level windows only, convert coordinates from desktop pixels
2114 // (the "parent" coordinate space) to the window's device pixel space
2116 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale
: 1.0;
2117 int32_t x
= NSToIntRound(aX
* scale
);
2118 int32_t y
= NSToIntRound(aY
* scale
);
2119 int32_t width
= NSToIntRound(aWidth
* scale
);
2120 int32_t height
= NSToIntRound(aHeight
* scale
);
2122 NS_ASSERTION((width
>= 0), "Negative width passed to nsWindow::Resize");
2123 NS_ASSERTION((height
>= 0), "Negative height passed to nsWindow::Resize");
2124 if (width
< 0 || height
< 0) {
2125 gfxCriticalNoteOnce
<< "Negative passed to Resize(" << x
<< " ," << y
2126 << ", " << width
<< ", " << height
2127 << ") repaint: " << aRepaint
;
2130 ConstrainSize(&width
, &height
);
2132 // Avoid unnecessary resizing calls
2133 if (mBounds
.IsEqualRect(x
, y
, width
, height
)) {
2140 // Set cached value for lightweight and printing
2141 mBounds
.SetRect(x
, y
, width
, height
);
2144 // Refer to the comment above a similar check in nsWindow::Move
2145 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
2146 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
2147 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
2149 HMONITOR monitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONULL
);
2150 if (NS_WARN_IF(!monitor
)) {
2153 MONITORINFO mi
= {sizeof(MONITORINFO
)};
2154 VERIFY(::GetMonitorInfo(monitor
, &mi
));
2157 x
+ mi
.rcWork
.left
- mi
.rcMonitor
.left
- pl
.rcNormalPosition
.left
;
2159 y
+ mi
.rcWork
.top
- mi
.rcMonitor
.top
- pl
.rcNormalPosition
.top
;
2160 pl
.rcNormalPosition
.left
+= deltaX
;
2161 pl
.rcNormalPosition
.right
= pl
.rcNormalPosition
.left
+ width
;
2162 pl
.rcNormalPosition
.top
+= deltaY
;
2163 pl
.rcNormalPosition
.bottom
= pl
.rcNormalPosition
.top
+ GetHeight(height
);
2164 VERIFY(::SetWindowPlacement(mWnd
, &pl
));
2166 UINT flags
= SWP_NOZORDER
| SWP_NOACTIVATE
;
2168 flags
|= SWP_NOREDRAW
;
2173 double oldScale
= mDefaultScale
;
2174 mResizeState
= RESIZING
;
2176 ::SetWindowPos(mWnd
, nullptr, x
, y
, width
, GetHeight(height
), flags
));
2177 mResizeState
= NOT_RESIZING
;
2178 if (WinUtils::LogToPhysFactor(mWnd
) != oldScale
) {
2182 if (mTransitionWnd
) {
2183 // If we have a fullscreen transition window, we need to make
2184 // it topmost again, otherwise the taskbar may be raised by
2185 // the system unexpectedly when we leave fullscreen state.
2186 ::SetWindowPos(mTransitionWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
2187 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
2191 ResizeDirectManipulationViewport();
2194 if (aRepaint
) Invalidate();
2197 mozilla::Maybe
<bool> nsWindow::IsResizingNativeWidget() {
2198 if (mResizeState
== RESIZING
) {
2204 /**************************************************************
2206 * SECTION: Window Z-order and state.
2208 * nsIWidget::PlaceBehind, nsIWidget::SetSizeMode,
2209 * nsIWidget::ConstrainPosition
2211 * Z-order, positioning, restore, minimize, and maximize.
2213 **************************************************************/
2215 // Position the window behind the given window
2216 void nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement
,
2217 nsIWidget
* aWidget
, bool aActivate
) {
2218 HWND behind
= HWND_TOP
;
2219 if (aPlacement
== eZPlacementBottom
)
2220 behind
= HWND_BOTTOM
;
2221 else if (aPlacement
== eZPlacementBelow
&& aWidget
)
2222 behind
= (HWND
)aWidget
->GetNativeData(NS_NATIVE_WINDOW
);
2223 UINT flags
= SWP_NOMOVE
| SWP_NOREPOSITION
| SWP_NOSIZE
;
2224 if (!aActivate
) flags
|= SWP_NOACTIVATE
;
2226 if (!CanTakeFocus() && behind
== HWND_TOP
) {
2227 // Can't place the window to top so place it behind the foreground window
2228 // (as long as it is not topmost)
2229 HWND wndAfter
= ::GetForegroundWindow();
2231 behind
= HWND_BOTTOM
;
2232 else if (!(GetWindowLongPtrW(wndAfter
, GWL_EXSTYLE
) & WS_EX_TOPMOST
))
2234 flags
|= SWP_NOACTIVATE
;
2237 ::SetWindowPos(mWnd
, behind
, 0, 0, 0, 0, flags
);
2240 static UINT
GetCurrentShowCmd(HWND aWnd
) {
2242 pl
.length
= sizeof(pl
);
2243 ::GetWindowPlacement(aWnd
, &pl
);
2247 // Maximize, minimize or restore the window.
2248 void nsWindow::SetSizeMode(nsSizeMode aMode
) {
2249 // If we are still displaying a maximized pre-XUL skeleton UI, ignore the
2250 // noise of sizemode changes. Once we have "shown" the window for the first
2251 // time (called nsWindow::Show(true), even though the window is already
2252 // technically displayed), we will again accept sizemode changes.
2253 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
2257 mFrameState
->EnsureSizeMode(aMode
);
2260 nsSizeMode
nsWindow::SizeMode() { return mFrameState
->GetSizeMode(); }
2262 void DoGetWorkspaceID(HWND aWnd
, nsAString
* aWorkspaceID
) {
2263 RefPtr
<IVirtualDesktopManager
> desktopManager
= gVirtualDesktopManager
;
2264 if (!desktopManager
|| !aWnd
) {
2269 HRESULT hr
= desktopManager
->GetWindowDesktopId(aWnd
, &desktop
);
2274 RPC_WSTR workspaceIDStr
= nullptr;
2275 if (UuidToStringW(&desktop
, &workspaceIDStr
) == RPC_S_OK
) {
2276 aWorkspaceID
->Assign((wchar_t*)workspaceIDStr
);
2277 RpcStringFreeW(&workspaceIDStr
);
2281 void nsWindow::GetWorkspaceID(nsAString
& workspaceID
) {
2282 // If we have a value cached, use that, but also make sure it is
2283 // scheduled to be updated. If we don't yet have a value, get
2284 // one synchronously.
2285 auto desktop
= mDesktopId
.Lock();
2286 if (desktop
->mID
.IsEmpty()) {
2287 DoGetWorkspaceID(mWnd
, &desktop
->mID
);
2288 desktop
->mUpdateIsQueued
= false;
2290 AsyncUpdateWorkspaceID(*desktop
);
2293 workspaceID
= desktop
->mID
;
2296 void nsWindow::AsyncUpdateWorkspaceID(Desktop
& aDesktop
) {
2297 struct UpdateWorkspaceIdTask
: public Task
{
2298 explicit UpdateWorkspaceIdTask(nsWindow
* aSelf
)
2299 : Task(false /* mainThread */, EventQueuePriority::Normal
),
2302 bool Run() override
{
2303 auto desktop
= mSelf
->mDesktopId
.Lock();
2304 if (desktop
->mUpdateIsQueued
) {
2305 DoGetWorkspaceID(mSelf
->mWnd
, &desktop
->mID
);
2306 desktop
->mUpdateIsQueued
= false;
2311 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
2312 bool GetName(nsACString
& aName
) override
{
2313 aName
.AssignLiteral("UpdateWorkspaceIdTask");
2318 RefPtr
<nsWindow
> mSelf
;
2321 if (aDesktop
.mUpdateIsQueued
) {
2325 aDesktop
.mUpdateIsQueued
= true;
2326 TaskController::Get()->AddTask(MakeAndAddRef
<UpdateWorkspaceIdTask
>(this));
2329 void nsWindow::MoveToWorkspace(const nsAString
& workspaceID
) {
2330 RefPtr
<IVirtualDesktopManager
> desktopManager
= gVirtualDesktopManager
;
2331 if (!desktopManager
) {
2336 const nsString flat
= PromiseFlatString(workspaceID
);
2337 RPC_WSTR workspaceIDStr
= reinterpret_cast<RPC_WSTR
>((wchar_t*)flat
.get());
2338 if (UuidFromStringW(workspaceIDStr
, &desktop
) == RPC_S_OK
) {
2339 if (SUCCEEDED(desktopManager
->MoveWindowToDesktop(mWnd
, desktop
))) {
2340 auto desktop
= mDesktopId
.Lock();
2341 desktop
->mID
= workspaceID
;
2346 void nsWindow::SuppressAnimation(bool aSuppress
) {
2347 DWORD dwAttribute
= aSuppress
? TRUE
: FALSE
;
2348 DwmSetWindowAttribute(mWnd
, DWMWA_TRANSITIONS_FORCEDISABLED
, &dwAttribute
,
2349 sizeof dwAttribute
);
2352 // Constrain a potential move to fit onscreen
2353 // Position (aX, aY) is specified in Windows screen (logical) pixels,
2354 // except when using per-monitor DPI, in which case it's device pixels.
2355 void nsWindow::ConstrainPosition(DesktopIntPoint
& aPoint
) {
2356 if (!mIsTopWidgetWindow
) // only a problem for top-level windows
2359 double dpiScale
= GetDesktopToDeviceScale().scale
;
2361 // We need to use the window size in the kind of pixels used for window-
2362 // manipulation APIs.
2364 std::max
<int32_t>(NSToIntRound(mBounds
.Width() / dpiScale
), 1);
2366 std::max
<int32_t>(NSToIntRound(mBounds
.Height() / dpiScale
), 1);
2368 /* get our playing field. use the current screen, or failing that
2369 for any reason, use device caps for the default screen. */
2372 nsCOMPtr
<nsIScreenManager
> screenmgr
=
2373 do_GetService(sScreenManagerContractID
);
2377 nsCOMPtr
<nsIScreen
> screen
;
2378 int32_t left
, top
, width
, height
;
2380 screenmgr
->ScreenForRect(aPoint
.x
, aPoint
.y
, logWidth
, logHeight
,
2381 getter_AddRefs(screen
));
2382 if (mFrameState
->GetSizeMode() != nsSizeMode_Fullscreen
) {
2383 // For normalized windows, use the desktop work area.
2384 nsresult rv
= screen
->GetAvailRectDisplayPix(&left
, &top
, &width
, &height
);
2385 if (NS_FAILED(rv
)) {
2389 // For full screen windows, use the desktop.
2390 nsresult rv
= screen
->GetRectDisplayPix(&left
, &top
, &width
, &height
);
2391 if (NS_FAILED(rv
)) {
2395 screenRect
.left
= left
;
2396 screenRect
.right
= left
+ width
;
2397 screenRect
.top
= top
;
2398 screenRect
.bottom
= top
+ height
;
2400 if (aPoint
.x
< screenRect
.left
)
2401 aPoint
.x
= screenRect
.left
;
2402 else if (aPoint
.x
>= screenRect
.right
- logWidth
)
2403 aPoint
.x
= screenRect
.right
- logWidth
;
2405 if (aPoint
.y
< screenRect
.top
)
2406 aPoint
.y
= screenRect
.top
;
2407 else if (aPoint
.y
>= screenRect
.bottom
- logHeight
)
2408 aPoint
.y
= screenRect
.bottom
- logHeight
;
2411 /**************************************************************
2413 * SECTION: nsIWidget::Enable, nsIWidget::IsEnabled
2415 * Enabling and disabling the widget.
2417 **************************************************************/
2419 // Enable/disable this component
2420 void nsWindow::Enable(bool bState
) {
2422 ::EnableWindow(mWnd
, bState
);
2426 // Return the current enable state
2427 bool nsWindow::IsEnabled() const {
2428 return !mWnd
|| (::IsWindowEnabled(mWnd
) &&
2429 ::IsWindowEnabled(::GetAncestor(mWnd
, GA_ROOT
)));
2432 /**************************************************************
2434 * SECTION: nsIWidget::SetFocus
2436 * Give the focus to this widget.
2438 **************************************************************/
2440 void nsWindow::SetFocus(Raise aRaise
, mozilla::dom::CallerType aCallerType
) {
2442 #ifdef WINSTATE_DEBUG_OUTPUT
2443 if (mWnd
== WinUtils::GetTopLevelHWND(mWnd
)) {
2444 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
2445 ("*** SetFocus: [ top] raise=%d\n", aRaise
== Raise::Yes
));
2447 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
2448 ("*** SetFocus: [child] raise=%d\n", aRaise
== Raise::Yes
));
2451 // Uniconify, if necessary
2452 HWND toplevelWnd
= WinUtils::GetTopLevelHWND(mWnd
);
2453 if (aRaise
== Raise::Yes
&& ::IsIconic(toplevelWnd
)) {
2454 ::ShowWindow(toplevelWnd
, SW_RESTORE
);
2460 /**************************************************************
2464 * GetBounds, GetClientBounds, GetScreenBounds,
2465 * GetRestoredBounds, GetClientOffset
2466 * SetDrawsInTitlebar, SetNonClientMargins
2468 * Bound calculations.
2470 **************************************************************/
2472 // Return the window's full dimensions in screen coordinates.
2473 // If the window has a parent, converts the origin to an offset
2474 // of the parent's screen origin.
2475 LayoutDeviceIntRect
nsWindow::GetBounds() {
2481 VERIFY(::GetWindowRect(mWnd
, &r
));
2483 LayoutDeviceIntRect rect
;
2486 rect
.SizeTo(r
.right
- r
.left
, r
.bottom
- r
.top
);
2488 // popup window bounds' are in screen coordinates, not relative to parent
2490 if (mWindowType
== WindowType::Popup
) {
2491 rect
.MoveTo(r
.left
, r
.top
);
2495 // chrome on parent:
2496 // ___ 5,5 (chrome start)
2497 // | ____ 10,10 (client start)
2498 // | | ____ 20,20 (child start)
2500 // 20,20 - 5,5 = 15,15 (??)
2501 // minus GetClientOffset:
2502 // 15,15 - 5,5 = 10,10
2504 // no chrome on parent:
2505 // ______ 10,10 (win start)
2506 // | ____ 20,20 (child start)
2508 // 20,20 - 10,10 = 10,10
2510 // walking the chain:
2511 // ___ 5,5 (chrome start)
2512 // | ___ 10,10 (client start)
2513 // | | ___ 20,20 (child start)
2514 // | | | __ 30,30 (child start)
2516 // 30,30 - 20,20 = 10,10 (offset from second child to first)
2517 // 20,20 - 5,5 = 15,15 + 10,10 = 25,25 (??)
2518 // minus GetClientOffset:
2519 // 25,25 - 5,5 = 20,20 (offset from second child to parent client)
2521 // convert coordinates if parent exists
2522 HWND parent
= ::GetParent(mWnd
);
2525 VERIFY(::GetWindowRect(parent
, &pr
));
2528 // adjust for chrome
2529 nsWindow
* pWidget
= static_cast<nsWindow
*>(GetParent());
2530 if (pWidget
&& pWidget
->IsTopLevelWidget()) {
2531 LayoutDeviceIntPoint clientOffset
= pWidget
->GetClientOffset();
2532 r
.left
-= clientOffset
.x
;
2533 r
.top
-= clientOffset
.y
;
2536 rect
.MoveTo(r
.left
, r
.top
);
2537 if (mCompositorSession
&&
2538 !wr::WindowSizeSanityCheck(rect
.width
, rect
.height
)) {
2539 gfxCriticalNoteOnce
<< "Invalid size" << rect
<< " size mode "
2540 << mFrameState
->GetSizeMode();
2546 // Get this component dimension
2547 LayoutDeviceIntRect
nsWindow::GetClientBounds() {
2549 return LayoutDeviceIntRect(0, 0, 0, 0);
2553 if (!::GetClientRect(mWnd
, &r
)) {
2554 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
2555 gfxCriticalNoteOnce
<< "GetClientRect failed " << ::GetLastError();
2559 LayoutDeviceIntRect bounds
= GetBounds();
2560 LayoutDeviceIntRect rect
;
2561 rect
.MoveTo(bounds
.TopLeft() + GetClientOffset());
2562 rect
.SizeTo(r
.right
- r
.left
, r
.bottom
- r
.top
);
2566 // Like GetBounds, but don't offset by the parent
2567 LayoutDeviceIntRect
nsWindow::GetScreenBounds() {
2573 VERIFY(::GetWindowRect(mWnd
, &r
));
2575 return LayoutDeviceIntRect(r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
);
2578 nsresult
nsWindow::GetRestoredBounds(LayoutDeviceIntRect
& aRect
) {
2579 if (SizeMode() == nsSizeMode_Normal
) {
2580 aRect
= GetScreenBounds();
2584 return NS_ERROR_FAILURE
;
2587 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
2588 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
2589 const RECT
& r
= pl
.rcNormalPosition
;
2591 HMONITOR monitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONULL
);
2593 return NS_ERROR_FAILURE
;
2595 MONITORINFO mi
= {sizeof(MONITORINFO
)};
2596 VERIFY(::GetMonitorInfo(monitor
, &mi
));
2598 aRect
.SetRect(r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
);
2599 aRect
.MoveBy(mi
.rcWork
.left
- mi
.rcMonitor
.left
,
2600 mi
.rcWork
.top
- mi
.rcMonitor
.top
);
2604 // Return the x,y offset of the client area from the origin of the window. If
2605 // the window is borderless returns (0,0).
2606 LayoutDeviceIntPoint
nsWindow::GetClientOffset() {
2608 return LayoutDeviceIntPoint(0, 0);
2612 GetWindowRect(mWnd
, &r1
);
2613 LayoutDeviceIntPoint pt
= WidgetToScreenOffset();
2614 return LayoutDeviceIntPoint(pt
.x
- LayoutDeviceIntCoord(r1
.left
),
2615 pt
.y
- LayoutDeviceIntCoord(r1
.top
));
2618 void nsWindow::SetDrawsInTitlebar(bool aState
) {
2619 nsWindow
* window
= GetTopLevelWindow(true);
2620 if (window
&& window
!= this) {
2621 return window
->SetDrawsInTitlebar(aState
);
2624 // top, right, bottom, left
2625 SetNonClientMargins(aState
? LayoutDeviceIntMargin(0, -1, -1, -1)
2626 : LayoutDeviceIntMargin(-1, -1, -1, -1));
2629 void nsWindow::ResetLayout() {
2630 // This will trigger a frame changed event, triggering
2631 // nc calc size and a sizemode gecko event.
2632 SetWindowPos(mWnd
, 0, 0, 0, 0, 0,
2633 SWP_FRAMECHANGED
| SWP_NOACTIVATE
| SWP_NOMOVE
|
2634 SWP_NOOWNERZORDER
| SWP_NOSIZE
| SWP_NOZORDER
);
2636 // If hidden, just send the frame changed event for now.
2641 // Send a gecko size event to trigger reflow.
2642 RECT clientRc
= {0};
2643 GetClientRect(mWnd
, &clientRc
);
2644 OnResize(WinUtils::ToIntRect(clientRc
).Size());
2646 // Invalidate and update
2650 // Internally track the caption status via a window property. Required
2651 // due to our internal handling of WM_NCACTIVATE when custom client
2653 static const wchar_t kManageWindowInfoProperty
[] = L
"ManageWindowInfoProperty";
2654 typedef BOOL(WINAPI
* GetWindowInfoPtr
)(HWND hwnd
, PWINDOWINFO pwi
);
2655 static WindowsDllInterceptor::FuncHookType
<GetWindowInfoPtr
>
2656 sGetWindowInfoPtrStub
;
2658 BOOL WINAPI
GetWindowInfoHook(HWND hWnd
, PWINDOWINFO pwi
) {
2659 if (!sGetWindowInfoPtrStub
) {
2660 NS_ASSERTION(FALSE
, "Something is horribly wrong in GetWindowInfoHook!");
2664 reinterpret_cast<LONG_PTR
>(GetPropW(hWnd
, kManageWindowInfoProperty
));
2665 // No property set, return the default data.
2666 if (!windowStatus
) return sGetWindowInfoPtrStub(hWnd
, pwi
);
2667 // Call GetWindowInfo and update dwWindowStatus with our
2668 // internally tracked value.
2669 BOOL result
= sGetWindowInfoPtrStub(hWnd
, pwi
);
2671 pwi
->dwWindowStatus
= (windowStatus
== 1 ? 0 : WS_ACTIVECAPTION
);
2675 void nsWindow::UpdateGetWindowInfoCaptionStatus(bool aActiveCaption
) {
2678 sUser32Intercept
.Init("user32.dll");
2679 sGetWindowInfoPtrStub
.Set(sUser32Intercept
, "GetWindowInfo",
2680 &GetWindowInfoHook
);
2681 if (!sGetWindowInfoPtrStub
) {
2685 // Update our internally tracked caption status
2686 SetPropW(mWnd
, kManageWindowInfoProperty
,
2687 reinterpret_cast<HANDLE
>(static_cast<INT_PTR
>(aActiveCaption
) + 1));
2690 #define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19
2691 #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
2693 void nsWindow::UpdateDarkModeToolbar() {
2694 if (!IsWin10OrLater()) {
2697 LookAndFeel::EnsureColorSchemesInitialized();
2698 BOOL dark
= LookAndFeel::ColorSchemeForChrome() == ColorScheme::Dark
;
2699 DwmSetWindowAttribute(mWnd
, DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1
, &dark
,
2701 DwmSetWindowAttribute(mWnd
, DWMWA_USE_IMMERSIVE_DARK_MODE
, &dark
,
2705 LayoutDeviceIntMargin
nsWindow::NormalWindowNonClientOffset() const {
2706 bool glass
= gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled();
2708 LayoutDeviceIntMargin nonClientOffset
;
2710 // We're dealing with a "normal" window (not maximized, minimized, or
2711 // fullscreen), so process `mNonClientMargins` and set `mNonClientOffset`
2714 // Setting `mNonClientOffset` to 0 has the effect of leaving the default
2715 // frame intact. Setting it to a value greater than 0 reduces the frame
2716 // size by that amount.
2718 if (mNonClientMargins
.top
> 0 && glass
) {
2719 nonClientOffset
.top
= std::min(mCaptionHeight
, mNonClientMargins
.top
);
2720 } else if (mNonClientMargins
.top
== 0) {
2721 nonClientOffset
.top
= mCaptionHeight
;
2723 nonClientOffset
.top
= 0;
2726 if (mNonClientMargins
.bottom
> 0 && glass
) {
2727 nonClientOffset
.bottom
=
2728 std::min(mVertResizeMargin
, mNonClientMargins
.bottom
);
2729 } else if (mNonClientMargins
.bottom
== 0) {
2730 nonClientOffset
.bottom
= mVertResizeMargin
;
2732 nonClientOffset
.bottom
= 0;
2735 if (mNonClientMargins
.left
> 0 && glass
) {
2736 nonClientOffset
.left
= std::min(mHorResizeMargin
, mNonClientMargins
.left
);
2737 } else if (mNonClientMargins
.left
== 0) {
2738 nonClientOffset
.left
= mHorResizeMargin
;
2740 nonClientOffset
.left
= 0;
2743 if (mNonClientMargins
.right
> 0 && glass
) {
2744 nonClientOffset
.right
= std::min(mHorResizeMargin
, mNonClientMargins
.right
);
2745 } else if (mNonClientMargins
.right
== 0) {
2746 nonClientOffset
.right
= mHorResizeMargin
;
2748 nonClientOffset
.right
= 0;
2750 return nonClientOffset
;
2754 * Called when the window layout changes: full screen mode transitions,
2755 * theme changes, and composition changes. Calculates the new non-client
2756 * margins and fires off a frame changed event, which triggers an nc calc
2757 * size windows event, kicking the changes in.
2759 * The offsets calculated here are based on the value of `mNonClientMargins`
2760 * which is specified in the "chromemargins" attribute of the window. For
2761 * each margin, the value specified has the following meaning:
2762 * -1 - leave the default frame in place
2763 * 0 - remove the frame
2764 * >0 - frame size equals min(0, (default frame size - margin value))
2766 * This function calculates and populates `mNonClientOffset`.
2767 * In our processing of `WM_NCCALCSIZE`, the frame size will be calculated
2768 * as (default frame size - offset). For example, if the left frame should
2769 * be 1 pixel narrower than the default frame size, `mNonClientOffset.left`
2772 * For maximized, fullscreen, and minimized windows, the values stored in
2773 * `mNonClientMargins` are ignored, and special processing takes place.
2775 * For non-glass windows, we only allow frames to be their default size
2776 * or removed entirely.
2778 bool nsWindow::UpdateNonClientMargins(bool aReflowWindow
) {
2779 if (!mCustomNonClient
) {
2783 const nsSizeMode sizeMode
= mFrameState
->GetSizeMode();
2786 bool(mBorderStyle
& (BorderStyle::All
| BorderStyle::Title
|
2787 BorderStyle::Menu
| BorderStyle::Default
));
2789 float dpi
= GetDPI();
2791 // mCaptionHeight is the default size of the NC area at
2792 // the top of the window. If the window has a caption,
2793 // the size is calculated as the sum of:
2794 // SM_CYFRAME - The thickness of the sizing border
2795 // around a resizable window
2796 // SM_CXPADDEDBORDER - The amount of border padding
2797 // for captioned windows
2798 // SM_CYCAPTION - The height of the caption area
2800 // If the window does not have a caption, mCaptionHeight will be equal to
2801 // `WinUtils::GetSystemMetricsForDpi(SM_CYFRAME, dpi)`
2803 WinUtils::GetSystemMetricsForDpi(SM_CYFRAME
, dpi
) +
2804 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CYCAPTION
, dpi
) +
2805 WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2807 if (!mUseResizeMarginOverrides
) {
2808 // mHorResizeMargin is the size of the default NC areas on the
2809 // left and right sides of our window. It is calculated as
2811 // SM_CXFRAME - The thickness of the sizing border
2812 // SM_CXPADDEDBORDER - The amount of border padding
2813 // for captioned windows
2815 // If the window does not have a caption, mHorResizeMargin will be equal to
2816 // `WinUtils::GetSystemMetricsForDpi(SM_CXFRAME, dpi)`
2818 WinUtils::GetSystemMetricsForDpi(SM_CXFRAME
, dpi
) +
2819 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2822 // mVertResizeMargin is the size of the default NC area at the
2823 // bottom of the window. It is calculated as the sum of:
2824 // SM_CYFRAME - The thickness of the sizing border
2825 // SM_CXPADDEDBORDER - The amount of border padding
2826 // for captioned windows.
2828 // If the window does not have a caption, mVertResizeMargin will be equal to
2829 // `WinUtils::GetSystemMetricsForDpi(SM_CYFRAME, dpi)`
2831 WinUtils::GetSystemMetricsForDpi(SM_CYFRAME
, dpi
) +
2832 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2836 if (sizeMode
== nsSizeMode_Minimized
) {
2837 // Use default frame size for minimized windows
2838 mNonClientOffset
.top
= 0;
2839 mNonClientOffset
.left
= 0;
2840 mNonClientOffset
.right
= 0;
2841 mNonClientOffset
.bottom
= 0;
2842 } else if (sizeMode
== nsSizeMode_Fullscreen
) {
2843 // Remove the default frame from the top of our fullscreen window. This
2844 // makes the whole caption part of our client area, allowing us to draw
2845 // in the whole caption area. Additionally remove the default frame from
2846 // the left, right, and bottom.
2847 mNonClientOffset
.top
= mCaptionHeight
;
2848 mNonClientOffset
.bottom
= mVertResizeMargin
;
2849 mNonClientOffset
.left
= mHorResizeMargin
;
2850 mNonClientOffset
.right
= mHorResizeMargin
;
2851 } else if (sizeMode
== nsSizeMode_Maximized
) {
2852 // We make the entire frame part of the client area. We leave the default
2853 // frame sizes for left, right and bottom since Windows will automagically
2854 // position the edges "offscreen" for maximized windows.
2855 int verticalResize
=
2856 WinUtils::GetSystemMetricsForDpi(SM_CYFRAME
, dpi
) +
2857 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2860 mNonClientOffset
.top
= mCaptionHeight
- verticalResize
;
2861 mNonClientOffset
.bottom
= 0;
2862 mNonClientOffset
.left
= 0;
2863 mNonClientOffset
.right
= 0;
2865 mozilla::Maybe
<UINT
> maybeEdge
= GetHiddenTaskbarEdge();
2867 auto edge
= maybeEdge
.value();
2868 if (ABE_LEFT
== edge
) {
2869 mNonClientOffset
.left
-= kHiddenTaskbarSize
;
2870 } else if (ABE_RIGHT
== edge
) {
2871 mNonClientOffset
.right
-= kHiddenTaskbarSize
;
2872 } else if (ABE_BOTTOM
== edge
|| ABE_TOP
== edge
) {
2873 mNonClientOffset
.bottom
-= kHiddenTaskbarSize
;
2876 // On Windows 10+, when we are drawing the non-client region, we need
2877 // to clear the portion of the NC region that is exposed by the
2878 // hidden taskbar. As above, we clear the bottom of the NC region
2879 // when the taskbar is at the top of the screen.
2880 if (IsWin10OrLater()) {
2881 UINT clearEdge
= (edge
== ABE_TOP
) ? ABE_BOTTOM
: edge
;
2882 mClearNCEdge
= Some(clearEdge
);
2886 mNonClientOffset
= NormalWindowNonClientOffset();
2889 if (aReflowWindow
) {
2890 // Force a reflow of content based on the new client
2898 nsresult
nsWindow::SetNonClientMargins(const LayoutDeviceIntMargin
& margins
) {
2899 if (!mIsTopWidgetWindow
|| mBorderStyle
== BorderStyle::None
)
2900 return NS_ERROR_INVALID_ARG
;
2903 mFutureMarginsOnceChromeShows
= margins
;
2904 mFutureMarginsToUse
= true;
2907 mFutureMarginsToUse
= false;
2909 // Request for a reset
2910 if (margins
.top
== -1 && margins
.left
== -1 && margins
.right
== -1 &&
2911 margins
.bottom
== -1) {
2912 mCustomNonClient
= false;
2913 mNonClientMargins
= margins
;
2914 // Force a reflow of content based on the new client
2919 reinterpret_cast<LONG_PTR
>(GetPropW(mWnd
, kManageWindowInfoProperty
));
2921 ::SendMessageW(mWnd
, WM_NCACTIVATE
, 1 != windowStatus
, 0);
2927 if (margins
.top
< -1 || margins
.bottom
< -1 || margins
.left
< -1 ||
2929 return NS_ERROR_INVALID_ARG
;
2931 mNonClientMargins
= margins
;
2932 mCustomNonClient
= true;
2933 if (!UpdateNonClientMargins()) {
2934 NS_WARNING("UpdateNonClientMargins failed!");
2941 void nsWindow::SetResizeMargin(mozilla::LayoutDeviceIntCoord aResizeMargin
) {
2942 mUseResizeMarginOverrides
= true;
2943 mHorResizeMargin
= aResizeMargin
;
2944 mVertResizeMargin
= aResizeMargin
;
2945 UpdateNonClientMargins();
2948 void nsWindow::InvalidateNonClientRegion() {
2949 // +-+-----------------------+-+
2950 // | | app non-client chrome | |
2951 // | +-----------------------+ |
2952 // | | app client chrome | | }
2953 // | +-----------------------+ | }
2954 // | | app content | | } area we don't want to invalidate
2955 // | +-----------------------+ | }
2956 // | | app client chrome | | }
2957 // | +-----------------------+ |
2958 // +---------------------------+ <
2959 // ^ ^ windows non-client chrome
2960 // client area = app *
2962 GetWindowRect(mWnd
, &rect
);
2963 MapWindowPoints(nullptr, mWnd
, (LPPOINT
)&rect
, 2);
2964 HRGN winRgn
= CreateRectRgnIndirect(&rect
);
2966 // Subtract app client chrome and app content leaving
2967 // windows non-client chrome and app non-client chrome
2969 GetWindowRect(mWnd
, &rect
);
2970 rect
.top
+= mCaptionHeight
;
2971 rect
.right
-= mHorResizeMargin
;
2972 rect
.bottom
-= mVertResizeMargin
;
2973 rect
.left
+= mHorResizeMargin
;
2974 MapWindowPoints(nullptr, mWnd
, (LPPOINT
)&rect
, 2);
2975 HRGN clientRgn
= CreateRectRgnIndirect(&rect
);
2976 CombineRgn(winRgn
, winRgn
, clientRgn
, RGN_DIFF
);
2977 DeleteObject(clientRgn
);
2979 // triggers ncpaint and paint events for the two areas
2980 RedrawWindow(mWnd
, nullptr, winRgn
, RDW_FRAME
| RDW_INVALIDATE
);
2981 DeleteObject(winRgn
);
2984 HRGN
nsWindow::ExcludeNonClientFromPaintRegion(HRGN aRegion
) {
2987 if (aRegion
== (HRGN
)1) { // undocumented value indicating a full refresh
2988 GetWindowRect(mWnd
, &rect
);
2989 rgn
= CreateRectRgnIndirect(&rect
);
2993 GetClientRect(mWnd
, &rect
);
2994 MapWindowPoints(mWnd
, nullptr, (LPPOINT
)&rect
, 2);
2995 HRGN nonClientRgn
= CreateRectRgnIndirect(&rect
);
2996 CombineRgn(rgn
, rgn
, nonClientRgn
, RGN_DIFF
);
2997 DeleteObject(nonClientRgn
);
3001 /**************************************************************
3003 * SECTION: nsIWidget::SetBackgroundColor
3005 * Sets the window background paint color.
3007 **************************************************************/
3009 void nsWindow::SetBackgroundColor(const nscolor
& aColor
) {
3010 if (mBrush
) ::DeleteObject(mBrush
);
3012 mBrush
= ::CreateSolidBrush(NSRGB_2_COLOREF(aColor
));
3013 if (mWnd
!= nullptr) {
3014 ::SetClassLongPtrW(mWnd
, GCLP_HBRBACKGROUND
, (LONG_PTR
)mBrush
);
3018 /**************************************************************
3020 * SECTION: nsIWidget::SetCursor
3022 * SetCursor and related utilities for manging cursor state.
3024 **************************************************************/
3026 // Set this component cursor
3027 static HCURSOR
CursorFor(nsCursor aCursor
) {
3029 case eCursor_select
:
3030 return ::LoadCursor(nullptr, IDC_IBEAM
);
3032 return ::LoadCursor(nullptr, IDC_WAIT
);
3033 case eCursor_hyperlink
:
3034 return ::LoadCursor(nullptr, IDC_HAND
);
3035 case eCursor_standard
:
3036 case eCursor_context_menu
: // XXX See bug 258960.
3037 return ::LoadCursor(nullptr, IDC_ARROW
);
3039 case eCursor_n_resize
:
3040 case eCursor_s_resize
:
3041 return ::LoadCursor(nullptr, IDC_SIZENS
);
3043 case eCursor_w_resize
:
3044 case eCursor_e_resize
:
3045 return ::LoadCursor(nullptr, IDC_SIZEWE
);
3047 case eCursor_nw_resize
:
3048 case eCursor_se_resize
:
3049 return ::LoadCursor(nullptr, IDC_SIZENWSE
);
3051 case eCursor_ne_resize
:
3052 case eCursor_sw_resize
:
3053 return ::LoadCursor(nullptr, IDC_SIZENESW
);
3055 case eCursor_crosshair
:
3056 return ::LoadCursor(nullptr, IDC_CROSS
);
3059 return ::LoadCursor(nullptr, IDC_SIZEALL
);
3062 return ::LoadCursor(nullptr, IDC_HELP
);
3064 case eCursor_copy
: // CSS3
3065 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_COPY
));
3068 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_ALIAS
));
3071 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_CELL
));
3073 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_GRAB
));
3075 case eCursor_grabbing
:
3076 return ::LoadCursor(nsToolkit::mDllInstance
,
3077 MAKEINTRESOURCE(IDC_GRABBING
));
3079 case eCursor_spinning
:
3080 return ::LoadCursor(nullptr, IDC_APPSTARTING
);
3082 case eCursor_zoom_in
:
3083 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_ZOOMIN
));
3085 case eCursor_zoom_out
:
3086 return ::LoadCursor(nsToolkit::mDllInstance
,
3087 MAKEINTRESOURCE(IDC_ZOOMOUT
));
3089 case eCursor_not_allowed
:
3090 case eCursor_no_drop
:
3091 return ::LoadCursor(nullptr, IDC_NO
);
3093 case eCursor_col_resize
:
3094 return ::LoadCursor(nsToolkit::mDllInstance
,
3095 MAKEINTRESOURCE(IDC_COLRESIZE
));
3097 case eCursor_row_resize
:
3098 return ::LoadCursor(nsToolkit::mDllInstance
,
3099 MAKEINTRESOURCE(IDC_ROWRESIZE
));
3101 case eCursor_vertical_text
:
3102 return ::LoadCursor(nsToolkit::mDllInstance
,
3103 MAKEINTRESOURCE(IDC_VERTICALTEXT
));
3105 case eCursor_all_scroll
:
3106 // XXX not 100% appropriate perhaps
3107 return ::LoadCursor(nullptr, IDC_SIZEALL
);
3109 case eCursor_nesw_resize
:
3110 return ::LoadCursor(nullptr, IDC_SIZENESW
);
3112 case eCursor_nwse_resize
:
3113 return ::LoadCursor(nullptr, IDC_SIZENWSE
);
3115 case eCursor_ns_resize
:
3116 return ::LoadCursor(nullptr, IDC_SIZENS
);
3118 case eCursor_ew_resize
:
3119 return ::LoadCursor(nullptr, IDC_SIZEWE
);
3122 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_NONE
));
3125 NS_ERROR("Invalid cursor type");
3130 static HCURSOR
CursorForImage(const nsIWidget::Cursor
& aCursor
,
3131 CSSToLayoutDeviceScale aScale
) {
3132 if (!aCursor
.IsCustom()) {
3136 nsIntSize size
= nsIWidget::CustomCursorSize(aCursor
);
3138 // Reject cursors greater than 128 pixels in either direction, to prevent
3140 // XXX ideally we should rescale. Also, we could modify the API to
3141 // allow trusted content to set larger cursors.
3142 if (size
.width
> 128 || size
.height
> 128) {
3146 LayoutDeviceIntSize layoutSize
=
3147 RoundedToInt(CSSIntSize(size
.width
, size
.height
) * aScale
);
3148 LayoutDeviceIntPoint hotspot
=
3149 RoundedToInt(CSSIntPoint(aCursor
.mHotspotX
, aCursor
.mHotspotY
) * aScale
);
3151 nsresult rv
= nsWindowGfx::CreateIcon(aCursor
.mContainer
, true, hotspot
,
3152 layoutSize
, &cursor
);
3153 if (NS_FAILED(rv
)) {
3160 void nsWindow::SetCursor(const Cursor
& aCursor
) {
3161 static HCURSOR sCurrentHCursor
= nullptr;
3162 static bool sCurrentHCursorIsCustom
= false;
3166 if (sCurrentCursor
== aCursor
&& sCurrentHCursor
&& !mUpdateCursor
) {
3167 // Cursors in windows are global, so even if our mUpdateCursor flag is
3168 // false we always need to make sure the Windows cursor is up-to-date,
3169 // since stuff like native drag and drop / resizers code can mutate it
3170 // outside of this method.
3171 ::SetCursor(sCurrentHCursor
);
3175 mUpdateCursor
= false;
3177 if (sCurrentHCursorIsCustom
) {
3178 ::DestroyIcon(sCurrentHCursor
);
3180 sCurrentHCursor
= nullptr;
3181 sCurrentHCursorIsCustom
= false;
3182 sCurrentCursor
= aCursor
;
3184 HCURSOR cursor
= CursorForImage(aCursor
, GetDefaultScale());
3185 bool custom
= false;
3189 cursor
= CursorFor(aCursor
.mDefaultCursor
);
3196 sCurrentHCursor
= cursor
;
3197 sCurrentHCursorIsCustom
= custom
;
3198 ::SetCursor(cursor
);
3201 /**************************************************************
3203 * SECTION: nsIWidget::Get/SetTransparencyMode
3205 * Manage the transparency mode of the window containing this
3206 * widget. Only works for popup and dialog windows when the
3207 * Desktop Window Manager compositor is not enabled.
3209 **************************************************************/
3211 TransparencyMode
nsWindow::GetTransparencyMode() {
3212 return GetTopLevelWindow(true)->GetWindowTranslucencyInner();
3215 void nsWindow::SetTransparencyMode(TransparencyMode aMode
) {
3216 nsWindow
* window
= GetTopLevelWindow(true);
3219 if (!window
|| window
->DestroyCalled()) {
3223 if (WindowType::TopLevel
== window
->mWindowType
&&
3224 mTransparencyMode
!= aMode
&&
3225 !gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
3226 NS_WARNING("Cannot set transparency mode on top-level windows.");
3230 window
->SetWindowTranslucencyInner(aMode
);
3233 /**************************************************************
3235 * SECTION: nsIWidget::UpdateWindowDraggingRegion
3237 * For setting the draggable titlebar region from CSS
3238 * with -moz-window-dragging: drag.
3240 **************************************************************/
3242 void nsWindow::UpdateWindowDraggingRegion(
3243 const LayoutDeviceIntRegion
& aRegion
) {
3244 if (mDraggableRegion
!= aRegion
) {
3245 mDraggableRegion
= aRegion
;
3249 /**************************************************************
3251 * SECTION: nsIWidget::HideWindowChrome
3253 * Show or hide window chrome.
3255 **************************************************************/
3257 void nsWindow::HideWindowChrome(bool aShouldHide
) {
3258 HWND hwnd
= WinUtils::GetTopLevelHWND(mWnd
, true);
3259 if (!WinUtils::GetNSWindowPtr(hwnd
)) {
3260 NS_WARNING("Trying to hide window decorations in an embedded context");
3264 if (mHideChrome
== aShouldHide
) return;
3266 DWORD_PTR style
, exStyle
;
3267 mHideChrome
= aShouldHide
;
3269 DWORD_PTR tempStyle
= ::GetWindowLongPtrW(hwnd
, GWL_STYLE
);
3270 DWORD_PTR tempExStyle
= ::GetWindowLongPtrW(hwnd
, GWL_EXSTYLE
);
3272 style
= tempStyle
& ~(WS_CAPTION
| WS_THICKFRAME
);
3273 exStyle
= tempExStyle
& ~(WS_EX_DLGMODALFRAME
| WS_EX_WINDOWEDGE
|
3274 WS_EX_CLIENTEDGE
| WS_EX_STATICEDGE
);
3276 mOldStyle
= tempStyle
;
3277 mOldExStyle
= tempExStyle
;
3279 if (!mOldStyle
|| !mOldExStyle
) {
3280 mOldStyle
= ::GetWindowLongPtrW(hwnd
, GWL_STYLE
);
3281 mOldExStyle
= ::GetWindowLongPtrW(hwnd
, GWL_EXSTYLE
);
3285 exStyle
= mOldExStyle
;
3286 if (mFutureMarginsToUse
) {
3287 SetNonClientMargins(mFutureMarginsOnceChromeShows
);
3291 VERIFY_WINDOW_STYLE(style
);
3292 ::SetWindowLongPtrW(hwnd
, GWL_STYLE
, style
);
3293 ::SetWindowLongPtrW(hwnd
, GWL_EXSTYLE
, exStyle
);
3296 /**************************************************************
3298 * SECTION: nsWindow::Invalidate
3300 * Invalidate an area of the client for painting.
3302 **************************************************************/
3304 // Invalidate this component visible area
3305 void nsWindow::Invalidate(bool aEraseBackground
, bool aUpdateNCArea
,
3306 bool aIncludeChildren
) {
3311 #ifdef WIDGET_DEBUG_OUTPUT
3312 debug_DumpInvalidate(stdout
, this, nullptr, "noname", (int32_t)mWnd
);
3313 #endif // WIDGET_DEBUG_OUTPUT
3315 DWORD flags
= RDW_INVALIDATE
;
3316 if (aEraseBackground
) {
3319 if (aUpdateNCArea
) {
3322 if (aIncludeChildren
) {
3323 flags
|= RDW_ALLCHILDREN
;
3326 VERIFY(::RedrawWindow(mWnd
, nullptr, nullptr, flags
));
3329 // Invalidate this component visible area
3330 void nsWindow::Invalidate(const LayoutDeviceIntRect
& aRect
) {
3332 #ifdef WIDGET_DEBUG_OUTPUT
3333 debug_DumpInvalidate(stdout
, this, &aRect
, "noname", (int32_t)mWnd
);
3334 #endif // WIDGET_DEBUG_OUTPUT
3338 rect
.left
= aRect
.X();
3339 rect
.top
= aRect
.Y();
3340 rect
.right
= aRect
.XMost();
3341 rect
.bottom
= aRect
.YMost();
3343 VERIFY(::InvalidateRect(mWnd
, &rect
, FALSE
));
3347 static LRESULT CALLBACK
FullscreenTransitionWindowProc(HWND hWnd
, UINT uMsg
,
3351 case WM_FULLSCREEN_TRANSITION_BEFORE
:
3352 case WM_FULLSCREEN_TRANSITION_AFTER
: {
3353 DWORD duration
= (DWORD
)lParam
;
3354 DWORD flags
= AW_BLEND
;
3355 if (uMsg
== WM_FULLSCREEN_TRANSITION_AFTER
) {
3358 ::AnimateWindow(hWnd
, duration
, flags
);
3359 // The message sender should have added ref for us.
3360 NS_DispatchToMainThread(
3361 already_AddRefed
<nsIRunnable
>((nsIRunnable
*)wParam
));
3365 ::PostQuitMessage(0);
3368 return ::DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3373 struct FullscreenTransitionInitData
{
3374 LayoutDeviceIntRect mBounds
;
3379 FullscreenTransitionInitData()
3380 : mSemaphore(nullptr), mThread(nullptr), mWnd(nullptr) {}
3382 ~FullscreenTransitionInitData() {
3384 ::CloseHandle(mSemaphore
);
3387 ::CloseHandle(mThread
);
3392 static DWORD WINAPI
FullscreenTransitionThreadProc(LPVOID lpParam
) {
3393 // Initialize window class
3394 static bool sInitialized
= false;
3395 if (!sInitialized
) {
3397 wc
.lpfnWndProc
= ::FullscreenTransitionWindowProc
;
3398 wc
.hInstance
= nsToolkit::mDllInstance
;
3399 wc
.hbrBackground
= ::CreateSolidBrush(RGB(0, 0, 0));
3400 wc
.lpszClassName
= kClassNameTransition
;
3401 ::RegisterClassW(&wc
);
3402 sInitialized
= true;
3405 auto data
= static_cast<FullscreenTransitionInitData
*>(lpParam
);
3406 HWND wnd
= ::CreateWindowW(kClassNameTransition
, L
"", 0, 0, 0, 0, 0, nullptr,
3407 nullptr, nsToolkit::mDllInstance
, nullptr);
3409 ::ReleaseSemaphore(data
->mSemaphore
, 1, nullptr);
3413 // Since AnimateWindow blocks the thread of the transition window,
3414 // we need to hide the cursor for that window, otherwise the system
3415 // would show the busy pointer to the user.
3416 ::ShowCursor(false);
3417 ::SetWindowLongW(wnd
, GWL_STYLE
, 0);
3420 WS_EX_LAYERED
| WS_EX_TRANSPARENT
| WS_EX_TOOLWINDOW
| WS_EX_NOACTIVATE
);
3421 ::SetWindowPos(wnd
, HWND_TOPMOST
, data
->mBounds
.X(), data
->mBounds
.Y(),
3422 data
->mBounds
.Width(), data
->mBounds
.Height(), 0);
3424 ::ReleaseSemaphore(data
->mSemaphore
, 1, nullptr);
3425 // The initialization data may no longer be valid
3426 // after we release the semaphore.
3430 while (::GetMessageW(&msg
, nullptr, 0, 0)) {
3431 ::TranslateMessage(&msg
);
3432 ::DispatchMessage(&msg
);
3435 ::DestroyWindow(wnd
);
3439 class FullscreenTransitionData final
: public nsISupports
{
3443 explicit FullscreenTransitionData(HWND aWnd
) : mWnd(aWnd
) {
3444 MOZ_ASSERT(NS_IsMainThread(),
3445 "FullscreenTransitionData "
3446 "should be constructed in the main thread");
3452 ~FullscreenTransitionData() {
3453 MOZ_ASSERT(NS_IsMainThread(),
3454 "FullscreenTransitionData "
3455 "should be deconstructed in the main thread");
3456 ::PostMessageW(mWnd
, WM_DESTROY
, 0, 0);
3460 NS_IMPL_ISUPPORTS0(FullscreenTransitionData
)
3463 bool nsWindow::PrepareForFullscreenTransition(nsISupports
** aData
) {
3464 // We don't support fullscreen transition when composition is not
3465 // enabled, which could make the transition broken and annoying.
3467 if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
3471 FullscreenTransitionInitData initData
;
3472 nsCOMPtr
<nsIScreen
> screen
= GetWidgetScreen();
3473 const DesktopIntRect rect
= screen
->GetRectDisplayPix();
3474 MOZ_ASSERT(BoundsUseDesktopPixels(),
3475 "Should only be called on top-level window");
3477 LayoutDeviceIntRect::Round(rect
* GetDesktopToDeviceScale());
3479 // Create a semaphore for synchronizing the window handle which will
3480 // be created by the transition thread and used by the main thread for
3481 // posting the transition messages.
3482 initData
.mSemaphore
= ::CreateSemaphore(nullptr, 0, 1, nullptr);
3483 if (initData
.mSemaphore
) {
3484 initData
.mThread
= ::CreateThread(
3485 nullptr, 0, FullscreenTransitionThreadProc
, &initData
, 0, nullptr);
3486 if (initData
.mThread
) {
3487 ::WaitForSingleObject(initData
.mSemaphore
, INFINITE
);
3490 if (!initData
.mWnd
) {
3494 mTransitionWnd
= initData
.mWnd
;
3496 auto data
= new FullscreenTransitionData(initData
.mWnd
);
3503 void nsWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage
,
3506 nsIRunnable
* aCallback
) {
3507 auto data
= static_cast<FullscreenTransitionData
*>(aData
);
3508 nsCOMPtr
<nsIRunnable
> callback
= aCallback
;
3509 UINT msg
= aStage
== eBeforeFullscreenToggle
? WM_FULLSCREEN_TRANSITION_BEFORE
3510 : WM_FULLSCREEN_TRANSITION_AFTER
;
3511 WPARAM wparam
= (WPARAM
)callback
.forget().take();
3512 ::PostMessage(data
->mWnd
, msg
, wparam
, (LPARAM
)aDuration
);
3516 void nsWindow::CleanupFullscreenTransition() {
3517 MOZ_ASSERT(NS_IsMainThread(),
3518 "CleanupFullscreenTransition "
3519 "should only run on the main thread");
3521 mTransitionWnd
= nullptr;
3524 void nsWindow::TryDwmResizeHack() {
3525 // The "DWM resize hack", aka the "fullscreen resize hack", is a workaround
3526 // for DWM's occasional and not-entirely-predictable failure to update its
3527 // internal state when the client area of a window changes without changing
3528 // the window size. The effect of this is that DWM will clip the content of
3529 // the window to its former client area.
3531 // It is not known under what circumstances the bug will trigger. Windows 11
3532 // is known to be required, but many Windows 11 machines do not exhibit the
3533 // issue. Even machines that _do_ exhibit it will sometimes not do so when
3534 // apparently-irrelevant changes are made to the configuration. (See bug
3537 // The bug is triggered by Firefox when a maximized window (which has window
3538 // decorations) becomes fullscreen (which doesn't). To work around this, if we
3539 // think it may occur, we "flicker-resize" the relevant window -- that is, we
3540 // reduce its height by 1px, then restore it. This causes DWM to acquire the
3541 // new client-area metrics.
3543 // Note that, in particular, this bug will not occur when using a separate
3544 // compositor window, as our compositor windows never have any nonclient area.
3546 // This is admittedly a sledgehammer where a screwdriver should suffice.
3548 // ---------------------------------------------------------------------------
3550 // Regardless of preferences or heuristics, only apply the hack if this is the
3551 // first time we've entered fullscreen across the entire Firefox session.
3552 // (Subsequent transitions to fullscreen, even with different windows, don't
3553 // appear to induce the bug.)
3555 // (main thread only; `atomic` not needed)
3556 static bool sIsFirstFullscreenEntry
= true;
3557 bool isFirstFullscreenEntry
= sIsFirstFullscreenEntry
;
3558 sIsFirstFullscreenEntry
= false;
3559 if (MOZ_LIKELY(!isFirstFullscreenEntry
)) {
3562 MOZ_LOG(gWindowsLog
, LogLevel::Verbose
,
3563 ("%s: first fullscreen entry", __PRETTY_FUNCTION__
));
3566 // Check whether to try to apply the DWM resize hack, based on the override
3567 // pref and/or some internal heuristics.
3569 const auto hackApplicationHeuristics
= [&]() -> bool {
3570 // The bug has only been seen under Windows 11. (At time of writing, this
3571 // is the latest version of Windows.)
3572 if (!IsWin11OrLater()) {
3576 KnowsCompositor
const* const kc
= mWindowRenderer
->AsKnowsCompositor();
3577 // This should never happen...
3579 // ... so if it does, we are in uncharted territory: don't apply the hack.
3584 // The bug doesn't occur when we're using a separate compositor window
3585 // (since the compositor window always comprises exactly its client area,
3586 // with no non-client border).
3587 if (kc
->GetUseCompositorWnd()) {
3591 // Otherwise, apply the hack.
3595 // Figure out whether or not we should perform the hack, and -- arguably
3596 // more importantly -- log that decision.
3597 bool const shouldApplyHack
= [&]() {
3598 enum Reason
: bool { Pref
, Heuristics
};
3599 auto const msg
= [&](bool decision
, Reason reason
) -> bool {
3600 MOZ_LOG(gWindowsLog
, LogLevel::Verbose
,
3601 ("%s %s per %s", decision
? "applying" : "skipping",
3602 "DWM resize hack", reason
== Pref
? "pref" : "heuristics"));
3605 switch (StaticPrefs::widget_windows_apply_dwm_resize_hack()) {
3607 return msg(false, Pref
);
3609 return msg(true, Pref
);
3610 default: // treat all other values as `auto`
3611 return msg(hackApplicationHeuristics(), Heuristics
);
3615 if (!shouldApplyHack
) {
3620 // The DWM bug is believed to involve a race condition: some users have
3621 // reported that setting a custom theme or adding unused command-line
3622 // parameters sometimes causes the bug to vanish.
3624 // Out of an abundance of caution, we therefore apply the hack in a later
3625 // event, rather than inline.
3626 NS_DispatchToMainThread(NS_NewRunnableFunction(
3627 "nsWindow::TryFullscreenResizeHack", [self
= RefPtr(this)]() {
3628 HWND
const hwnd
= self
->GetWindowHandle();
3630 if (self
->mFrameState
->GetSizeMode() != nsSizeMode_Fullscreen
) {
3631 MOZ_LOG(gWindowsLog
, mozilla::LogLevel::Info
,
3632 ("DWM resize hack: window no longer fullscreen; aborting"));
3637 if (!::GetWindowRect(hwnd
, &origRect
)) {
3638 MOZ_LOG(gWindowsLog
, mozilla::LogLevel::Error
,
3639 ("DWM resize hack: could not get window size?!"));
3642 LONG
const x
= origRect
.left
;
3643 LONG
const y
= origRect
.top
;
3644 LONG
const width
= origRect
.right
- origRect
.left
;
3645 LONG
const height
= origRect
.bottom
- origRect
.top
;
3647 MOZ_DIAGNOSTIC_ASSERT(!self
->mIsPerformingDwmFlushHack
);
3649 MakeScopeExit([&, oldVal
= self
->mIsPerformingDwmFlushHack
]() {
3650 self
->mIsPerformingDwmFlushHack
= oldVal
;
3652 self
->mIsPerformingDwmFlushHack
= true;
3654 MOZ_LOG(gWindowsLog
, LogLevel::Debug
,
3655 ("beginning DWM resize hack for HWND %08" PRIXPTR
,
3657 ::MoveWindow(hwnd
, x
, y
, width
, height
- 1, FALSE
);
3658 ::MoveWindow(hwnd
, x
, y
, width
, height
, TRUE
);
3659 MOZ_LOG(gWindowsLog
, LogLevel::Debug
,
3660 ("concluded DWM resize hack for HWND %08" PRIXPTR
,
3665 void nsWindow::OnFullscreenChanged(nsSizeMode aOldSizeMode
, bool aFullScreen
) {
3666 MOZ_ASSERT((aOldSizeMode
!= nsSizeMode_Fullscreen
) == aFullScreen
);
3668 // HACK: Potentially flicker-resize the window, to force DWM to get the right
3669 // client-area information.
3674 // Hide chrome and reposition window. Note this will also cache dimensions for
3675 // restoration, so it should only be called once per fullscreen request.
3677 // Don't do this when minimized, since our bounds make no sense then, nor when
3678 // coming back from that state.
3679 const bool toOrFromMinimized
=
3680 mFrameState
->GetSizeMode() == nsSizeMode_Minimized
||
3681 aOldSizeMode
== nsSizeMode_Minimized
;
3682 if (!toOrFromMinimized
) {
3683 InfallibleMakeFullScreen(aFullScreen
);
3686 // Possibly notify the taskbar that we have changed our fullscreen mode.
3687 TaskbarConcealer::OnFullscreenChanged(this, aFullScreen
);
3690 nsresult
nsWindow::MakeFullScreen(bool aFullScreen
) {
3691 mFrameState
->EnsureFullscreenMode(aFullScreen
);
3695 /**************************************************************
3697 * SECTION: Native data storage
3699 * nsIWidget::GetNativeData
3700 * nsIWidget::FreeNativeData
3702 * Set or clear native data based on a constant.
3704 **************************************************************/
3706 // Return some native data according to aDataType
3707 void* nsWindow::GetNativeData(uint32_t aDataType
) {
3708 switch (aDataType
) {
3709 case NS_NATIVE_WIDGET
:
3710 case NS_NATIVE_WINDOW
:
3711 case NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID
:
3713 case NS_NATIVE_GRAPHIC
:
3714 MOZ_ASSERT_UNREACHABLE("Not supported on Windows:");
3716 case NS_RAW_NATIVE_IME_CONTEXT
: {
3717 void* pseudoIMEContext
= GetPseudoIMEContext();
3718 if (pseudoIMEContext
) {
3719 return pseudoIMEContext
;
3723 case NS_NATIVE_TSF_THREAD_MGR
:
3724 case NS_NATIVE_TSF_CATEGORY_MGR
:
3725 case NS_NATIVE_TSF_DISPLAY_ATTR_MGR
:
3726 return IMEHandler::GetNativeData(this, aDataType
);
3735 // Free some native data according to aDataType
3736 void nsWindow::FreeNativeData(void* data
, uint32_t aDataType
) {
3737 switch (aDataType
) {
3738 case NS_NATIVE_GRAPHIC
:
3739 case NS_NATIVE_WIDGET
:
3740 case NS_NATIVE_WINDOW
:
3747 /**************************************************************
3749 * SECTION: nsIWidget::SetTitle
3751 * Set the main windows title text.
3753 **************************************************************/
3755 nsresult
nsWindow::SetTitle(const nsAString
& aTitle
) {
3756 const nsString
& strTitle
= PromiseFlatString(aTitle
);
3757 AutoRestore
<bool> sendingText(mSendingSetText
);
3758 mSendingSetText
= true;
3759 ::SendMessageW(mWnd
, WM_SETTEXT
, (WPARAM
)0, (LPARAM
)(LPCWSTR
)strTitle
.get());
3763 /**************************************************************
3765 * SECTION: nsIWidget::SetIcon
3767 * Set the main windows icon.
3769 **************************************************************/
3771 void nsWindow::SetBigIcon(HICON aIcon
) {
3773 (HICON
)::SendMessageW(mWnd
, WM_SETICON
, (WPARAM
)ICON_BIG
, (LPARAM
)aIcon
);
3775 ::DestroyIcon(icon
);
3781 void nsWindow::SetSmallIcon(HICON aIcon
) {
3782 HICON icon
= (HICON
)::SendMessageW(mWnd
, WM_SETICON
, (WPARAM
)ICON_SMALL
,
3785 ::DestroyIcon(icon
);
3791 void nsWindow::SetIcon(const nsAString
& aIconSpec
) {
3792 // Assume the given string is a local identifier for an icon file.
3794 nsCOMPtr
<nsIFile
> iconFile
;
3795 ResolveIconName(aIconSpec
, u
".ico"_ns
, getter_AddRefs(iconFile
));
3796 if (!iconFile
) return;
3798 nsAutoString iconPath
;
3799 iconFile
->GetPath(iconPath
);
3801 // XXX this should use MZLU (see bug 239279)
3806 (HICON
)::LoadImageW(nullptr, (LPCWSTR
)iconPath
.get(), IMAGE_ICON
,
3807 ::GetSystemMetrics(SM_CXICON
),
3808 ::GetSystemMetrics(SM_CYICON
), LR_LOADFROMFILE
);
3810 (HICON
)::LoadImageW(nullptr, (LPCWSTR
)iconPath
.get(), IMAGE_ICON
,
3811 ::GetSystemMetrics(SM_CXSMICON
),
3812 ::GetSystemMetrics(SM_CYSMICON
), LR_LOADFROMFILE
);
3815 SetBigIcon(bigIcon
);
3817 #ifdef DEBUG_SetIcon
3819 NS_LossyConvertUTF16toASCII
cPath(iconPath
);
3820 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
3821 ("\nIcon load error; icon=%s, rc=0x%08X\n\n", cPath
.get(),
3826 SetSmallIcon(smallIcon
);
3828 #ifdef DEBUG_SetIcon
3830 NS_LossyConvertUTF16toASCII
cPath(iconPath
);
3831 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
3832 ("\nSmall icon load error; icon=%s, rc=0x%08X\n\n", cPath
.get(),
3838 void nsWindow::SetBigIconNoData() {
3840 ::LoadIconW(::GetModuleHandleW(nullptr), gStockApplicationIcon
);
3841 SetBigIcon(bigIcon
);
3844 void nsWindow::SetSmallIconNoData() {
3846 ::LoadIconW(::GetModuleHandleW(nullptr), gStockApplicationIcon
);
3847 SetSmallIcon(smallIcon
);
3850 /**************************************************************
3852 * SECTION: nsIWidget::WidgetToScreenOffset
3854 * Return this widget's origin in screen coordinates.
3856 **************************************************************/
3858 LayoutDeviceIntPoint
nsWindow::WidgetToScreenOffset() {
3862 ::ClientToScreen(mWnd
, &point
);
3863 return LayoutDeviceIntPoint(point
.x
, point
.y
);
3866 LayoutDeviceIntMargin
nsWindow::ClientToWindowMargin() {
3867 if (mWindowType
== WindowType::Popup
&& !IsPopupWithTitleBar()) {
3871 if (mCustomNonClient
) {
3872 return NonClientSizeMargin(NormalWindowNonClientOffset());
3875 // Just use a dummy 200x200 at (200, 200) client rect as the rect.
3877 clientRect
.left
= 200;
3878 clientRect
.top
= 200;
3879 clientRect
.right
= 400;
3880 clientRect
.bottom
= 400;
3882 auto ToRect
= [](const RECT
& aRect
) -> LayoutDeviceIntRect
{
3883 return {aRect
.left
, aRect
.top
, aRect
.right
- aRect
.left
,
3884 aRect
.bottom
- aRect
.top
};
3887 RECT windowRect
= clientRect
;
3888 ::AdjustWindowRectEx(&windowRect
, WindowStyle(), false, WindowExStyle());
3890 return ToRect(windowRect
) - ToRect(clientRect
);
3893 /**************************************************************
3895 * SECTION: nsIWidget::EnableDragDrop
3897 * Enables/Disables drag and drop of files on this widget.
3899 **************************************************************/
3901 void nsWindow::EnableDragDrop(bool aEnable
) {
3903 // Return early if the window already closed
3908 if (!mNativeDragTarget
) {
3909 mNativeDragTarget
= new nsNativeDragTarget(this);
3910 mNativeDragTarget
->AddRef();
3911 if (SUCCEEDED(::CoLockObjectExternal((LPUNKNOWN
)mNativeDragTarget
, TRUE
,
3913 ::RegisterDragDrop(mWnd
, (LPDROPTARGET
)mNativeDragTarget
);
3917 if (mWnd
&& mNativeDragTarget
) {
3918 ::RevokeDragDrop(mWnd
);
3919 ::CoLockObjectExternal((LPUNKNOWN
)mNativeDragTarget
, FALSE
, TRUE
);
3920 mNativeDragTarget
->DragCancel();
3921 NS_RELEASE(mNativeDragTarget
);
3926 /**************************************************************
3928 * SECTION: nsIWidget::CaptureMouse
3930 * Enables/Disables system mouse capture.
3932 **************************************************************/
3934 void nsWindow::CaptureMouse(bool aCapture
) {
3935 TRACKMOUSEEVENT mTrack
;
3936 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3937 mTrack
.dwHoverTime
= 0;
3938 mTrack
.hwndTrack
= mWnd
;
3940 mTrack
.dwFlags
= TME_CANCEL
| TME_LEAVE
;
3943 mTrack
.dwFlags
= TME_LEAVE
;
3946 sIsInMouseCapture
= aCapture
;
3947 TrackMouseEvent(&mTrack
);
3950 /**************************************************************
3952 * SECTION: nsIWidget::CaptureRollupEvents
3954 * Dealing with event rollup on destroy for popups. Enables &
3955 * Disables system capture of any and all events that would
3956 * cause a dropdown to be rolled up.
3958 **************************************************************/
3960 void nsWindow::CaptureRollupEvents(bool aDoCapture
) {
3962 if (!sMsgFilterHook
&& !sCallProcHook
&& !sCallMouseHook
) {
3963 RegisterSpecialDropdownHooks();
3965 sProcessHook
= true;
3967 sProcessHook
= false;
3968 UnregisterSpecialDropdownHooks();
3972 /**************************************************************
3974 * SECTION: nsIWidget::GetAttention
3976 * Bring this window to the user's attention.
3978 **************************************************************/
3980 // Draw user's attention to this window until it comes to foreground.
3981 nsresult
nsWindow::GetAttention(int32_t aCycleCount
) {
3983 if (!mWnd
) return NS_ERROR_NOT_INITIALIZED
;
3985 HWND flashWnd
= WinUtils::GetTopLevelHWND(mWnd
, false, false);
3986 HWND fgWnd
= ::GetForegroundWindow();
3987 // Don't flash if the flash count is 0 or if the foreground window is our
3988 // window handle or that of our owned-most window.
3989 if (aCycleCount
== 0 || flashWnd
== fgWnd
||
3990 flashWnd
== WinUtils::GetTopLevelHWND(fgWnd
, false, false)) {
3994 DWORD defaultCycleCount
= 0;
3995 ::SystemParametersInfo(SPI_GETFOREGROUNDFLASHCOUNT
, 0, &defaultCycleCount
, 0);
3997 FLASHWINFO flashInfo
= {sizeof(FLASHWINFO
), flashWnd
, FLASHW_ALL
,
3998 aCycleCount
> 0 ? aCycleCount
: defaultCycleCount
, 0};
3999 ::FlashWindowEx(&flashInfo
);
4004 void nsWindow::StopFlashing() {
4005 HWND flashWnd
= mWnd
;
4006 while (HWND ownerWnd
= ::GetWindow(flashWnd
, GW_OWNER
)) {
4007 flashWnd
= ownerWnd
;
4010 FLASHWINFO flashInfo
= {sizeof(FLASHWINFO
), flashWnd
, FLASHW_STOP
, 0, 0};
4011 ::FlashWindowEx(&flashInfo
);
4014 /**************************************************************
4016 * SECTION: nsIWidget::HasPendingInputEvent
4018 * Ask whether there user input events pending. All input events are
4019 * included, including those not targeted at this nsIwidget instance.
4021 **************************************************************/
4023 bool nsWindow::HasPendingInputEvent() {
4024 // If there is pending input or the user is currently
4025 // moving the window then return true.
4026 // Note: When the user is moving the window WIN32 spins
4027 // a separate event loop and input events are not
4028 // reported to the application.
4029 if (HIWORD(GetQueueStatus(QS_INPUT
))) return true;
4030 GUITHREADINFO guiInfo
;
4031 guiInfo
.cbSize
= sizeof(GUITHREADINFO
);
4032 if (!GetGUIThreadInfo(GetCurrentThreadId(), &guiInfo
)) return false;
4033 return GUI_INMOVESIZE
== (guiInfo
.flags
& GUI_INMOVESIZE
);
4036 /**************************************************************
4038 * SECTION: nsIWidget::GetWindowRenderer
4040 * Get the window renderer associated with this widget.
4042 **************************************************************/
4044 WindowRenderer
* nsWindow::GetWindowRenderer() {
4045 if (mWindowRenderer
) {
4046 return mWindowRenderer
;
4049 if (!mLocalesChangedObserver
) {
4050 mLocalesChangedObserver
= new LocalesChangedObserver(this);
4054 if (!mWindowRenderer
&& ShouldUseOffMainThreadCompositing()) {
4055 gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
4059 if (!mWindowRenderer
) {
4060 MOZ_ASSERT(!mCompositorSession
&& !mCompositorBridgeChild
);
4061 MOZ_ASSERT(!mCompositorWidgetDelegate
);
4063 // Ensure we have a widget proxy even if we're not using the compositor,
4064 // since all our transparent window handling lives there.
4065 WinCompositorWidgetInitData
initData(
4066 reinterpret_cast<uintptr_t>(mWnd
),
4067 reinterpret_cast<uintptr_t>(static_cast<nsIWidget
*>(this)),
4068 mTransparencyMode
, mFrameState
->GetSizeMode());
4069 // If we're not using the compositor, the options don't actually matter.
4070 CompositorOptions
options(false, false);
4071 mBasicLayersSurface
=
4072 new InProcessWinCompositorWidget(initData
, options
, this);
4073 mCompositorWidgetDelegate
= mBasicLayersSurface
;
4074 mWindowRenderer
= CreateFallbackRenderer();
4077 NS_ASSERTION(mWindowRenderer
, "Couldn't provide a valid window renderer.");
4079 if (mWindowRenderer
) {
4080 // Update the size constraints now that the layer manager has been
4082 KnowsCompositor
* knowsCompositor
= mWindowRenderer
->AsKnowsCompositor();
4083 if (knowsCompositor
) {
4084 SizeConstraints c
= mSizeConstraints
;
4085 mMaxTextureSize
= knowsCompositor
->GetMaxTextureSize();
4086 c
.mMaxSize
.width
= std::min(c
.mMaxSize
.width
, mMaxTextureSize
);
4087 c
.mMaxSize
.height
= std::min(c
.mMaxSize
.height
, mMaxTextureSize
);
4088 nsBaseWidget::SetSizeConstraints(c
);
4092 return mWindowRenderer
;
4095 /**************************************************************
4097 * SECTION: nsBaseWidget::SetCompositorWidgetDelegate
4099 * Called to connect the nsWindow to the delegate providing
4100 * platform compositing API access.
4102 **************************************************************/
4104 void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate
* delegate
) {
4106 mCompositorWidgetDelegate
= delegate
->AsPlatformSpecificDelegate();
4107 MOZ_ASSERT(mCompositorWidgetDelegate
,
4108 "nsWindow::SetCompositorWidgetDelegate called with a "
4109 "non-PlatformCompositorWidgetDelegate");
4111 mCompositorWidgetDelegate
= nullptr;
4115 /**************************************************************
4117 * SECTION: nsIWidget::OnDefaultButtonLoaded
4119 * Called after the dialog is loaded and it has a default button.
4121 **************************************************************/
4123 nsresult
nsWindow::OnDefaultButtonLoaded(
4124 const LayoutDeviceIntRect
& aButtonRect
) {
4125 if (aButtonRect
.IsEmpty()) return NS_OK
;
4127 // Don't snap when we are not active.
4128 HWND activeWnd
= ::GetActiveWindow();
4129 if (activeWnd
!= ::GetForegroundWindow() ||
4130 WinUtils::GetTopLevelHWND(mWnd
, true) !=
4131 WinUtils::GetTopLevelHWND(activeWnd
, true)) {
4135 bool isAlwaysSnapCursor
=
4136 Preferences::GetBool("ui.cursor_snapping.always_enabled", false);
4138 if (!isAlwaysSnapCursor
) {
4139 BOOL snapDefaultButton
;
4140 if (!::SystemParametersInfo(SPI_GETSNAPTODEFBUTTON
, 0, &snapDefaultButton
,
4146 LayoutDeviceIntRect widgetRect
= GetScreenBounds();
4147 LayoutDeviceIntRect
buttonRect(aButtonRect
+ widgetRect
.TopLeft());
4149 LayoutDeviceIntPoint
centerOfButton(buttonRect
.X() + buttonRect
.Width() / 2,
4150 buttonRect
.Y() + buttonRect
.Height() / 2);
4151 // The center of the button can be outside of the widget.
4152 // E.g., it could be hidden by scrolling.
4153 if (!widgetRect
.Contains(centerOfButton
)) {
4157 if (!::SetCursorPos(centerOfButton
.x
, centerOfButton
.y
)) {
4158 NS_ERROR("SetCursorPos failed");
4159 return NS_ERROR_FAILURE
;
4164 uint32_t nsWindow::GetMaxTouchPoints() const {
4165 return WinUtils::GetMaxTouchPoints();
4168 void nsWindow::SetWindowClass(const nsAString
& xulWinType
,
4169 const nsAString
& xulWinClass
,
4170 const nsAString
& xulWinName
) {
4171 mIsEarlyBlankWindow
= xulWinType
.EqualsLiteral("navigator:blank");
4174 /**************************************************************
4175 **************************************************************
4177 ** BLOCK: Moz Events
4179 ** Moz GUI event management.
4181 **************************************************************
4182 **************************************************************/
4184 /**************************************************************
4186 * SECTION: Mozilla event initialization
4188 * Helpers for initializing moz events.
4190 **************************************************************/
4192 // Event initialization
4193 void nsWindow::InitEvent(WidgetGUIEvent
& event
, LayoutDeviceIntPoint
* aPoint
) {
4194 if (nullptr == aPoint
) { // use the point from the event
4195 // get the message position in client coordinates
4196 if (mWnd
!= nullptr) {
4197 DWORD pos
= ::GetMessagePos();
4200 cpos
.x
= GET_X_LPARAM(pos
);
4201 cpos
.y
= GET_Y_LPARAM(pos
);
4203 ::ScreenToClient(mWnd
, &cpos
);
4204 event
.mRefPoint
= LayoutDeviceIntPoint(cpos
.x
, cpos
.y
);
4206 event
.mRefPoint
= LayoutDeviceIntPoint(0, 0);
4209 // use the point override if provided
4210 event
.mRefPoint
= *aPoint
;
4213 event
.AssignEventTime(CurrentMessageWidgetEventTime());
4216 WidgetEventTime
nsWindow::CurrentMessageWidgetEventTime() const {
4217 LONG messageTime
= ::GetMessageTime();
4218 return WidgetEventTime(GetMessageTimeStamp(messageTime
));
4221 /**************************************************************
4223 * SECTION: Moz event dispatch helpers
4225 * Helpers for dispatching different types of moz events.
4227 **************************************************************/
4229 // Main event dispatch. Invokes callback and ProcessEvent method on
4230 // Event Listener object. Part of nsIWidget.
4231 nsresult
nsWindow::DispatchEvent(WidgetGUIEvent
* event
,
4232 nsEventStatus
& aStatus
) {
4233 #ifdef WIDGET_DEBUG_OUTPUT
4234 debug_DumpEvent(stdout
, event
->mWidget
, event
, "something", (int32_t)mWnd
);
4235 #endif // WIDGET_DEBUG_OUTPUT
4237 aStatus
= nsEventStatus_eIgnore
;
4239 // Top level windows can have a view attached which requires events be sent
4240 // to the underlying base window and the view. Added when we combined the
4241 // base chrome window with the main content child for nc client area (title
4243 if (mAttachedWidgetListener
) {
4244 aStatus
= mAttachedWidgetListener
->HandleEvent(event
, mUseAttachedEvents
);
4245 } else if (mWidgetListener
) {
4246 aStatus
= mWidgetListener
->HandleEvent(event
, mUseAttachedEvents
);
4249 // the window can be destroyed during processing of seemingly innocuous events
4250 // like, say, mousedowns due to the magic of scripting. mousedowns will return
4251 // nsEventStatus_eIgnore, which causes problems with the deleted window.
4253 if (mOnDestroyCalled
) aStatus
= nsEventStatus_eConsumeNoDefault
;
4257 bool nsWindow::DispatchStandardEvent(EventMessage aMsg
) {
4258 WidgetGUIEvent
event(true, aMsg
, this);
4261 bool result
= DispatchWindowEvent(event
);
4265 bool nsWindow::DispatchKeyboardEvent(WidgetKeyboardEvent
* event
) {
4266 nsEventStatus status
= DispatchInputEvent(event
).mContentStatus
;
4267 return ConvertStatus(status
);
4270 bool nsWindow::DispatchContentCommandEvent(WidgetContentCommandEvent
* aEvent
) {
4271 nsEventStatus status
;
4272 DispatchEvent(aEvent
, status
);
4273 return ConvertStatus(status
);
4276 bool nsWindow::DispatchWheelEvent(WidgetWheelEvent
* aEvent
) {
4277 nsEventStatus status
=
4278 DispatchInputEvent(aEvent
->AsInputEvent()).mContentStatus
;
4279 return ConvertStatus(status
);
4282 // Recursively dispatch synchronous paints for nsIWidget
4283 // descendants with invalidated rectangles.
4284 BOOL CALLBACK
nsWindow::DispatchStarvedPaints(HWND aWnd
, LPARAM aMsg
) {
4285 LONG_PTR proc
= ::GetWindowLongPtrW(aWnd
, GWLP_WNDPROC
);
4286 if (proc
== (LONG_PTR
)&nsWindow::WindowProc
) {
4287 // its one of our windows so check to see if it has a
4288 // invalidated rect. If it does. Dispatch a synchronous
4290 if (GetUpdateRect(aWnd
, nullptr, FALSE
)) VERIFY(::UpdateWindow(aWnd
));
4295 // Check for pending paints and dispatch any pending paint
4296 // messages for any nsIWidget which is a descendant of the
4297 // top-level window that *this* window is embedded within.
4299 // Note: We do not dispatch pending paint messages for non
4300 // nsIWidget managed windows.
4301 void nsWindow::DispatchPendingEvents() {
4302 // We need to ensure that reflow events do not get starved.
4303 // At the same time, we don't want to recurse through here
4304 // as that would prevent us from dispatching starved paints.
4305 static int recursionBlocker
= 0;
4306 if (recursionBlocker
++ == 0) {
4307 NS_ProcessPendingEvents(nullptr, PR_MillisecondsToInterval(100));
4311 // Quickly check to see if there are any paint events pending,
4312 // but only dispatch them if it has been long enough since the
4313 // last paint completed.
4314 if (::GetQueueStatus(QS_PAINT
) &&
4315 ((TimeStamp::Now() - mLastPaintEndTime
).ToMilliseconds() >= 50)) {
4316 // Find the top level window.
4317 HWND topWnd
= WinUtils::GetTopLevelHWND(mWnd
);
4319 // Dispatch pending paints for topWnd and all its descendant windows.
4320 // Note: EnumChildWindows enumerates all descendant windows not just
4321 // the children (but not the window itself).
4322 nsWindow::DispatchStarvedPaints(topWnd
, 0);
4323 ::EnumChildWindows(topWnd
, nsWindow::DispatchStarvedPaints
, 0);
4327 void nsWindow::DispatchCustomEvent(const nsString
& eventName
) {
4328 if (Document
* doc
= GetDocument()) {
4329 if (nsPIDOMWindowOuter
* win
= doc
->GetWindow()) {
4330 win
->DispatchCustomEvent(eventName
, ChromeOnlyDispatch::eYes
);
4335 bool nsWindow::TouchEventShouldStartDrag(EventMessage aEventMessage
,
4336 LayoutDeviceIntPoint aEventPoint
) {
4337 // Allow users to start dragging by double-tapping.
4338 if (aEventMessage
== eMouseDoubleClick
) {
4342 // In chrome UI, allow touchdownstartsdrag attributes
4343 // to cause any touchdown event to trigger a drag.
4344 if (aEventMessage
== eMouseDown
) {
4345 WidgetMouseEvent
hittest(true, eMouseHitTest
, this,
4346 WidgetMouseEvent::eReal
);
4347 hittest
.mRefPoint
= aEventPoint
;
4348 hittest
.mIgnoreRootScrollFrame
= true;
4349 hittest
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
4350 DispatchInputEvent(&hittest
);
4352 if (EventTarget
* target
= hittest
.GetDOMEventTarget()) {
4353 if (nsIContent
* content
= nsIContent::FromEventTarget(target
)) {
4354 // Check if the element or any parent element has the
4355 // attribute we're looking for.
4356 for (Element
* element
= content
->GetAsElementOrParentElement(); element
;
4357 element
= element
->GetParentElement()) {
4358 nsAutoString startDrag
;
4359 element
->GetAttribute(u
"touchdownstartsdrag"_ns
, startDrag
);
4360 if (!startDrag
.IsEmpty()) {
4371 // Deal with all sort of mouse event
4372 bool nsWindow::DispatchMouseEvent(EventMessage aEventMessage
, WPARAM wParam
,
4373 LPARAM lParam
, bool aIsContextMenuKey
,
4374 int16_t aButton
, uint16_t aInputSource
,
4375 WinPointerInfo
* aPointerInfo
,
4377 ContextMenuPreventer
contextMenuPreventer(this);
4378 bool result
= false;
4382 if (!mWidgetListener
) {
4386 LayoutDeviceIntPoint
eventPoint(GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
4387 LayoutDeviceIntPoint mpScreen
= eventPoint
+ WidgetToScreenOffset();
4389 // Suppress mouse moves caused by widget creation. Make sure to do this early
4390 // so that we update sLastMouseMovePoint even for touch-induced mousemove
4392 if (aEventMessage
== eMouseMove
) {
4393 if ((sLastMouseMovePoint
.x
== mpScreen
.x
.value
) &&
4394 (sLastMouseMovePoint
.y
== mpScreen
.y
.value
)) {
4397 sLastMouseMovePoint
.x
= mpScreen
.x
;
4398 sLastMouseMovePoint
.y
= mpScreen
.y
;
4401 if (!aIgnoreAPZ
&& WinUtils::GetIsMouseFromTouch(aEventMessage
)) {
4403 // If mTouchWindow is true, then we must have APZ enabled and be
4404 // feeding it raw touch events. In that case we only want to
4405 // send touch-generated mouse events to content if they should
4406 // start a touch-based drag-and-drop gesture, such as on
4407 // double-tapping or when tapping elements marked with the
4408 // touchdownstartsdrag attribute in chrome UI.
4410 if (TouchEventShouldStartDrag(aEventMessage
, eventPoint
)) {
4411 aEventMessage
= eMouseTouchDrag
;
4418 uint32_t pointerId
=
4419 aPointerInfo
? aPointerInfo
->pointerId
: MOUSE_POINTERID();
4421 // Since it is unclear whether a user will use the digitizer,
4422 // Postpone initialization until first PEN message will be found.
4423 if (MouseEvent_Binding::MOZ_SOURCE_PEN
== aInputSource
4424 // Messages should be only at topLevel window.
4425 && WindowType::TopLevel
== mWindowType
4426 // Currently this scheme is used only when pointer events is enabled.
4427 && InkCollector::sInkCollector
) {
4428 InkCollector::sInkCollector
->SetTarget(mWnd
);
4429 InkCollector::sInkCollector
->SetPointerId(pointerId
);
4432 switch (aEventMessage
) {
4437 // eMouseMove and eMouseExitFromWidget are here because we need to make
4438 // sure capture flag isn't left on after a drag where we wouldn't see a
4439 // button up message (see bug 324131).
4442 case eMouseExitFromWidget
:
4443 if (!(wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) &&
4445 CaptureMouse(false);
4453 WidgetMouseEvent
event(true, aEventMessage
, this, WidgetMouseEvent::eReal
,
4454 aIsContextMenuKey
? WidgetMouseEvent::eContextMenuKey
4455 : WidgetMouseEvent::eNormal
);
4456 if (aEventMessage
== eContextMenu
&& aIsContextMenuKey
) {
4457 LayoutDeviceIntPoint
zero(0, 0);
4458 InitEvent(event
, &zero
);
4460 InitEvent(event
, &eventPoint
);
4463 ModifierKeyState modifierKeyState
;
4464 modifierKeyState
.InitInputEvent(event
);
4466 // eContextMenu with Shift state is special. It won't fire "contextmenu"
4467 // event in the web content for blocking web content to prevent its default.
4468 // However, Shift+F10 is a standard shortcut key on Windows. Therefore,
4469 // this should not block web page to prevent its default. I.e., it should
4470 // behave same as ContextMenu key without Shift key.
4471 // XXX Should we allow to block web page to prevent its default with
4472 // Ctrl+Shift+F10 or Alt+Shift+F10 instead?
4473 if (aEventMessage
== eContextMenu
&& aIsContextMenuKey
&& event
.IsShift() &&
4474 NativeKey::LastKeyOrCharMSG().message
== WM_SYSKEYDOWN
&&
4475 NativeKey::LastKeyOrCharMSG().wParam
== VK_F10
) {
4476 event
.mModifiers
&= ~MODIFIER_SHIFT
;
4479 event
.mButton
= aButton
;
4480 event
.mInputSource
= aInputSource
;
4482 // Mouse events from Windows WM_POINTER*. Fill more information in
4483 // WidgetMouseEvent.
4484 event
.AssignPointerHelperData(*aPointerInfo
);
4485 event
.mPressure
= aPointerInfo
->mPressure
;
4486 event
.mButtons
= aPointerInfo
->mButtons
;
4488 // If we get here the mouse events must be from non-touch sources, so
4489 // convert it to pointer events as well
4490 event
.convertToPointer
= true;
4491 event
.pointerId
= pointerId
;
4494 // Static variables used to distinguish simple-, double- and triple-clicks.
4495 static POINT sLastMousePoint
= {0};
4496 static LONG sLastMouseDownTime
= 0L;
4497 static LONG sLastClickCount
= 0L;
4498 static BYTE sLastMouseButton
= 0;
4500 bool insideMovementThreshold
=
4501 (DeprecatedAbs(sLastMousePoint
.x
- eventPoint
.x
.value
) <
4502 (short)::GetSystemMetrics(SM_CXDOUBLECLK
)) &&
4503 (DeprecatedAbs(sLastMousePoint
.y
- eventPoint
.y
.value
) <
4504 (short)::GetSystemMetrics(SM_CYDOUBLECLK
));
4508 case MouseButton::ePrimary
:
4509 eventButton
= VK_LBUTTON
;
4511 case MouseButton::eMiddle
:
4512 eventButton
= VK_MBUTTON
;
4514 case MouseButton::eSecondary
:
4515 eventButton
= VK_RBUTTON
;
4522 // Doubleclicks are used to set the click count, then changed to mousedowns
4523 // We're going to time double-clicks from mouse *up* to next mouse *down*
4524 LONG curMsgTime
= ::GetMessageTime();
4526 switch (aEventMessage
) {
4527 case eMouseDoubleClick
:
4528 event
.mMessage
= eMouseDown
;
4529 event
.mButton
= aButton
;
4530 sLastClickCount
= 2;
4531 sLastMouseDownTime
= curMsgTime
;
4534 // remember when this happened for the next mouse down
4535 sLastMousePoint
.x
= eventPoint
.x
;
4536 sLastMousePoint
.y
= eventPoint
.y
;
4537 sLastMouseButton
= eventButton
;
4540 // now look to see if we want to convert this to a double- or triple-click
4541 if (((curMsgTime
- sLastMouseDownTime
) < (LONG
)::GetDoubleClickTime()) &&
4542 insideMovementThreshold
&& eventButton
== sLastMouseButton
) {
4545 // reset the click count, to count *this* click
4546 sLastClickCount
= 1;
4548 // Set last Click time on MouseDown only
4549 sLastMouseDownTime
= curMsgTime
;
4552 if (!insideMovementThreshold
) {
4553 sLastClickCount
= 0;
4556 case eMouseExitFromWidget
:
4558 Some(IsTopLevelMouseExit(mWnd
) ? WidgetMouseEvent::ePlatformTopLevel
4559 : WidgetMouseEvent::ePlatformChild
);
4564 event
.mClickCount
= sLastClickCount
;
4567 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
4568 ("Msg Time: %d Click Count: %d\n", curMsgTime
, event
.mClickCount
));
4571 // call the event callback
4572 if (mWidgetListener
) {
4573 if (aEventMessage
== eMouseMove
) {
4574 LayoutDeviceIntRect rect
= GetBounds();
4577 if (rect
.Contains(event
.mRefPoint
)) {
4578 if (sCurrentWindow
== nullptr || sCurrentWindow
!= this) {
4579 if ((nullptr != sCurrentWindow
) && (!sCurrentWindow
->mInDtor
)) {
4580 LPARAM pos
= sCurrentWindow
->lParamToClient(lParamToScreen(lParam
));
4581 sCurrentWindow
->DispatchMouseEvent(
4582 eMouseExitFromWidget
, wParam
, pos
, false, MouseButton::ePrimary
,
4583 aInputSource
, aPointerInfo
);
4585 sCurrentWindow
= this;
4587 LPARAM pos
= sCurrentWindow
->lParamToClient(lParamToScreen(lParam
));
4588 sCurrentWindow
->DispatchMouseEvent(
4589 eMouseEnterIntoWidget
, wParam
, pos
, false,
4590 MouseButton::ePrimary
, aInputSource
, aPointerInfo
);
4594 } else if (aEventMessage
== eMouseExitFromWidget
) {
4595 if (sCurrentWindow
== this) {
4596 sCurrentWindow
= nullptr;
4600 nsIWidget::ContentAndAPZEventStatus eventStatus
=
4601 DispatchInputEvent(&event
);
4602 contextMenuPreventer
.Update(event
, eventStatus
);
4603 return ConvertStatus(eventStatus
.mContentStatus
);
4609 HWND
nsWindow::GetTopLevelForFocus(HWND aCurWnd
) {
4610 // retrieve the toplevel window or dialogue
4611 HWND toplevelWnd
= nullptr;
4613 toplevelWnd
= aCurWnd
;
4614 nsWindow
* win
= WinUtils::GetNSWindowPtr(aCurWnd
);
4616 if (win
->mWindowType
== WindowType::TopLevel
||
4617 win
->mWindowType
== WindowType::Dialog
) {
4622 aCurWnd
= ::GetParent(aCurWnd
); // Parent or owner (if has no parent)
4627 void nsWindow::DispatchFocusToTopLevelWindow(bool aIsActivate
) {
4629 sJustGotActivate
= false;
4631 sJustGotDeactivate
= false;
4632 mLastKillFocusWindow
= nullptr;
4634 HWND toplevelWnd
= GetTopLevelForFocus(mWnd
);
4637 nsWindow
* win
= WinUtils::GetNSWindowPtr(toplevelWnd
);
4638 if (win
&& win
->mWidgetListener
) {
4640 win
->mWidgetListener
->WindowActivated();
4642 win
->mWidgetListener
->WindowDeactivated();
4648 HWND
nsWindow::WindowAtMouse() {
4649 DWORD pos
= ::GetMessagePos();
4651 mp
.x
= GET_X_LPARAM(pos
);
4652 mp
.y
= GET_Y_LPARAM(pos
);
4653 return ::WindowFromPoint(mp
);
4656 bool nsWindow::IsTopLevelMouseExit(HWND aWnd
) {
4657 HWND mouseWnd
= WindowAtMouse();
4659 // WinUtils::GetTopLevelHWND() will return a HWND for the window frame
4660 // (which includes the non-client area). If the mouse has moved into
4661 // the non-client area, we should treat it as a top-level exit.
4662 HWND mouseTopLevel
= WinUtils::GetTopLevelHWND(mouseWnd
);
4663 if (mouseWnd
== mouseTopLevel
) return true;
4665 return WinUtils::GetTopLevelHWND(aWnd
) != mouseTopLevel
;
4668 /**************************************************************
4672 * IPC related helpers.
4674 **************************************************************/
4677 bool nsWindow::IsAsyncResponseEvent(UINT aMsg
, LRESULT
& aResult
) {
4682 case WM_WINDOWPOSCHANGING
:
4683 case WM_WINDOWPOSCHANGED
:
4684 case WM_PARENTNOTIFY
:
4685 case WM_ACTIVATEAPP
:
4688 case WM_CHILDACTIVATE
:
4689 case WM_IME_SETCONTEXT
:
4693 case WM_MOUSEACTIVATE
:
4694 case WM_CONTEXTMENU
:
4698 case WM_SETTINGCHANGE
:
4706 "An unhandled ISMEX_SEND message was received during spin loop! (%X)",
4714 void nsWindow::IPCWindowProcHandler(UINT
& msg
, WPARAM
& wParam
, LPARAM
& lParam
) {
4716 msg
!= WM_GETOBJECT
,
4717 !mozilla::ipc::MessageChannel::IsPumpingMessages() ||
4718 mozilla::ipc::SuppressedNeuteringRegion::IsNeuteringSuppressed());
4720 // Modal UI being displayed in windowless plugins.
4721 if (mozilla::ipc::MessageChannel::IsSpinLoopActive() &&
4722 (InSendMessageEx(nullptr) & (ISMEX_REPLIED
| ISMEX_SEND
)) == ISMEX_SEND
) {
4724 if (IsAsyncResponseEvent(msg
, res
)) {
4730 // Handle certain sync plugin events sent to the parent which
4731 // trigger ipc calls that result in deadlocks.
4734 bool handled
= false;
4737 // Windowless flash sending WM_ACTIVATE events to the main window
4738 // via calls to ShowWindow.
4740 if (lParam
!= 0 && LOWORD(wParam
) == WA_ACTIVE
&&
4741 IsWindow((HWND
)lParam
)) {
4742 // Check for Adobe Reader X sync activate message from their
4743 // helper window and ignore. Fixes an annoying focus problem.
4744 if ((InSendMessageEx(nullptr) & (ISMEX_REPLIED
| ISMEX_SEND
)) ==
4746 wchar_t szClass
[10];
4747 HWND focusWnd
= (HWND
)lParam
;
4748 if (IsWindowVisible(focusWnd
) &&
4749 GetClassNameW(focusWnd
, szClass
,
4750 sizeof(szClass
) / sizeof(char16_t
)) &&
4751 !wcscmp(szClass
, L
"Edit") &&
4752 !WinUtils::IsOurProcessWindow(focusWnd
)) {
4759 // Plugins taking or losing focus triggering focus app messages.
4762 // Windowed plugins that pass sys key events to defwndproc generate
4763 // WM_SYSCOMMAND events to the main window.
4765 // Windowed plugins that fire context menu selection events to parent
4767 case WM_CONTEXTMENU
:
4768 // IME events fired as a result of synchronous focus changes
4769 case WM_IME_SETCONTEXT
:
4775 (InSendMessageEx(nullptr) & (ISMEX_REPLIED
| ISMEX_SEND
)) == ISMEX_SEND
) {
4776 ReplyMessage(dwResult
);
4780 /**************************************************************
4781 **************************************************************
4783 ** BLOCK: Native events
4785 ** Main Windows message handlers and OnXXX handlers for
4786 ** Windows event handling.
4788 **************************************************************
4789 **************************************************************/
4791 /**************************************************************
4793 * SECTION: Wind proc.
4795 * The main Windows event procedures and associated
4796 * message processing methods.
4798 **************************************************************/
4800 static bool DisplaySystemMenu(HWND hWnd
, nsSizeMode sizeMode
, bool isRtl
,
4801 int32_t x
, int32_t y
) {
4802 HMENU hMenu
= GetSystemMenu(hWnd
, FALSE
);
4805 mii
.cbSize
= sizeof(MENUITEMINFO
);
4806 mii
.fMask
= MIIM_STATE
;
4809 // update the options
4810 mii
.fState
= MF_ENABLED
;
4811 SetMenuItemInfo(hMenu
, SC_RESTORE
, FALSE
, &mii
);
4812 SetMenuItemInfo(hMenu
, SC_SIZE
, FALSE
, &mii
);
4813 SetMenuItemInfo(hMenu
, SC_MOVE
, FALSE
, &mii
);
4814 SetMenuItemInfo(hMenu
, SC_MAXIMIZE
, FALSE
, &mii
);
4815 SetMenuItemInfo(hMenu
, SC_MINIMIZE
, FALSE
, &mii
);
4817 mii
.fState
= MF_GRAYED
;
4819 case nsSizeMode_Fullscreen
:
4820 // intentional fall through
4821 case nsSizeMode_Maximized
:
4822 SetMenuItemInfo(hMenu
, SC_SIZE
, FALSE
, &mii
);
4823 SetMenuItemInfo(hMenu
, SC_MOVE
, FALSE
, &mii
);
4824 SetMenuItemInfo(hMenu
, SC_MAXIMIZE
, FALSE
, &mii
);
4826 case nsSizeMode_Minimized
:
4827 SetMenuItemInfo(hMenu
, SC_MINIMIZE
, FALSE
, &mii
);
4829 case nsSizeMode_Normal
:
4830 SetMenuItemInfo(hMenu
, SC_RESTORE
, FALSE
, &mii
);
4832 case nsSizeMode_Invalid
:
4833 NS_ASSERTION(false, "Did the argument come from invalid IPC?");
4836 MOZ_ASSERT_UNREACHABLE("Unhnalded nsSizeMode value detected");
4839 LPARAM cmd
= TrackPopupMenu(
4841 (TPM_LEFTBUTTON
| TPM_RIGHTBUTTON
| TPM_RETURNCMD
| TPM_TOPALIGN
|
4842 (isRtl
? TPM_RIGHTALIGN
: TPM_LEFTALIGN
)),
4843 x
, y
, 0, hWnd
, nullptr);
4845 PostMessage(hWnd
, WM_SYSCOMMAND
, cmd
, 0);
4852 // The WndProc procedure for all nsWindows in this toolkit. This merely catches
4853 // SEH exceptions and passes the real work to WindowProcInternal. See bug 587406
4854 // and http://msdn.microsoft.com/en-us/library/ms633573%28VS.85%29.aspx
4855 LRESULT CALLBACK
nsWindow::WindowProc(HWND hWnd
, UINT msg
, WPARAM wParam
,
4857 mozilla::ipc::CancelCPOWs();
4859 BackgroundHangMonitor().NotifyActivity();
4861 return mozilla::CallWindowProcCrashProtected(WindowProcInternal
, hWnd
, msg
,
4865 LRESULT CALLBACK
nsWindow::WindowProcInternal(HWND hWnd
, UINT msg
,
4866 WPARAM wParam
, LPARAM lParam
) {
4867 if (::GetWindowLongPtrW(hWnd
, GWLP_ID
) == eFakeTrackPointScrollableID
) {
4868 // This message was sent to the FAKETRACKPOINTSCROLLABLE.
4869 if (msg
== WM_HSCROLL
) {
4870 // Route WM_HSCROLL messages to the main window.
4871 hWnd
= ::GetParent(::GetParent(hWnd
));
4873 // Handle all other messages with its original window procedure.
4874 WNDPROC prevWindowProc
= (WNDPROC
)::GetWindowLongPtr(hWnd
, GWLP_USERDATA
);
4875 return ::CallWindowProcW(prevWindowProc
, hWnd
, msg
, wParam
, lParam
);
4879 if (msg
== MOZ_WM_TRACE
) {
4880 // This is a tracer event for measuring event loop latency.
4881 // See WidgetTraceEvent.cpp for more details.
4882 mozilla::SignalTracerThread();
4886 // Get the window which caused the event and ask it to process the message
4887 nsWindow
* targetWindow
= WinUtils::GetNSWindowPtr(hWnd
);
4888 NS_ASSERTION(targetWindow
, "nsWindow* is null!");
4889 if (!targetWindow
) return ::DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
4891 // Hold the window for the life of this method, in case it gets
4892 // destroyed during processing, unless we're in the dtor already.
4893 nsCOMPtr
<nsIWidget
> kungFuDeathGrip
;
4894 if (!targetWindow
->mInDtor
) kungFuDeathGrip
= targetWindow
;
4896 targetWindow
->IPCWindowProcHandler(msg
, wParam
, lParam
);
4898 // Create this here so that we store the last rolled up popup until after
4899 // the event has been processed.
4900 nsAutoRollup autoRollup
;
4902 LRESULT popupHandlingResult
;
4903 if (DealWithPopups(hWnd
, msg
, wParam
, lParam
, &popupHandlingResult
))
4904 return popupHandlingResult
;
4906 // Call ProcessMessage
4908 if (targetWindow
->ProcessMessage(msg
, wParam
, lParam
, &retValue
)) {
4912 LRESULT res
= ::CallWindowProcW(targetWindow
->GetPrevWindowProc(), hWnd
, msg
,
4918 const char16_t
* GetQuitType() {
4919 if (Preferences::GetBool(PREF_WIN_REGISTER_APPLICATION_RESTART
, false)) {
4920 DWORD cchCmdLine
= 0;
4921 HRESULT rc
= ::GetApplicationRestartSettings(::GetCurrentProcess(), nullptr,
4922 &cchCmdLine
, nullptr);
4924 return u
"os-restart";
4930 bool nsWindow::ExternalHandlerProcessMessage(UINT aMessage
, WPARAM
& aWParam
,
4932 MSGResult
& aResult
) {
4933 if (mWindowHook
.Notify(mWnd
, aMessage
, aWParam
, aLParam
, aResult
)) {
4937 if (IMEHandler::ProcessMessage(this, aMessage
, aWParam
, aLParam
, aResult
)) {
4941 if (MouseScrollHandler::ProcessMessage(this, aMessage
, aWParam
, aLParam
,
4949 // The main windows message processing method. Wraps ProcessMessageInternal so
4950 // we can log aRetValue.
4951 bool nsWindow::ProcessMessage(UINT msg
, WPARAM
& wParam
, LPARAM
& lParam
,
4952 LRESULT
* aRetValue
) {
4953 // For some events we might change the parameter values, so log
4954 // before and after we process them.
4955 NativeEventLogger
eventLogger("nsWindow", mWnd
, msg
, wParam
, lParam
);
4956 bool result
= ProcessMessageInternal(msg
, wParam
, lParam
, aRetValue
);
4957 eventLogger
.SetResult(*aRetValue
, result
);
4962 // The main windows message processing method. Called by ProcessMessage.
4963 bool nsWindow::ProcessMessageInternal(UINT msg
, WPARAM
& wParam
, LPARAM
& lParam
,
4964 LRESULT
* aRetValue
) {
4965 MSGResult
msgResult(aRetValue
);
4966 if (ExternalHandlerProcessMessage(msg
, wParam
, lParam
, msgResult
)) {
4967 return (msgResult
.mConsumed
|| !mWnd
);
4970 bool result
= false; // call the default nsWindow proc
4973 // The DWM resize hack (see bug 1763981) causes us to process a number of
4974 // messages, notably including some WM_WINDOWPOSCHANG{ING,ED} messages which
4975 // would ordinarily result in a whole lot of internal state being updated.
4977 // Since we're supposed to end in the same state we started in (and since the
4978 // content shouldn't know about any of this nonsense), just discard any
4979 // messages synchronously dispatched from within the hack.
4980 if (MOZ_UNLIKELY(mIsPerformingDwmFlushHack
)) {
4984 // Glass hit testing w/custom transparent margins.
4986 // FIXME(emilio): is this needed? We deal with titlebar buttons non-natively
4988 LRESULT dwmHitResult
;
4989 if (mCustomNonClient
&&
4990 DwmDefWindowProc(mWnd
, msg
, wParam
, lParam
, &dwmHitResult
)) {
4991 *aRetValue
= dwmHitResult
;
4995 // The preference whether to use a different keyboard layout for each
4996 // window is cached, and updating it will not take effect until the
4997 // next restart. We read the preference here and not upon WM_ACTIVATE to make
4998 // sure that this behavior is consistent. Otherwise, if the user changed the
4999 // preference before having ever lowered the window, the preference would take
5000 // effect immediately.
5001 static const bool sSwitchKeyboardLayout
=
5002 Preferences::GetBool("intl.keyboard.per_window_layout", false);
5003 AppShutdownReason shutdownReason
= AppShutdownReason::Unknown
;
5005 // (Large blocks of code should be broken out into OnEvent handlers.)
5007 // WM_QUERYENDSESSION must be handled by all windows.
5008 // Otherwise Windows thinks the window can just be killed at will.
5009 case WM_QUERYENDSESSION
: {
5010 // Ask around if it's ok to quit.
5011 nsCOMPtr
<nsIObserverService
> obsServ
=
5012 mozilla::services::GetObserverService();
5013 nsCOMPtr
<nsISupportsPRBool
> cancelQuitWrapper
=
5014 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID
);
5015 cancelQuitWrapper
->SetData(false);
5017 const char16_t
* quitType
= GetQuitType();
5018 obsServ
->NotifyObservers(cancelQuitWrapper
, "quit-application-requested",
5021 bool shouldCancelQuit
;
5022 cancelQuitWrapper
->GetData(&shouldCancelQuit
);
5023 *aRetValue
= !shouldCancelQuit
;
5027 case MOZ_WM_STARTA11Y
:
5028 #if defined(ACCESSIBILITY)
5029 Unused
<< GetAccessible();
5036 case WM_ENDSESSION
: {
5037 // For WM_ENDSESSION, wParam indicates whether we need to shutdown
5038 // (TRUE) or not (FALSE).
5043 // According to WM_ENDSESSION lParam documentation:
5044 // 0 -> OS shutdown or restart (no way to distinguish)
5045 // ENDSESSION_LOGOFF -> User is logging off
5046 // ENDSESSION_CLOSEAPP -> Application must shutdown
5047 // ENDSESSION_CRITICAL -> Application is forced to shutdown
5048 // The difference of the last two is not very clear.
5050 shutdownReason
= AppShutdownReason::OSShutdown
;
5051 } else if (lParam
& ENDSESSION_LOGOFF
) {
5052 shutdownReason
= AppShutdownReason::OSSessionEnd
;
5053 } else if (lParam
& (ENDSESSION_CLOSEAPP
| ENDSESSION_CRITICAL
)) {
5054 shutdownReason
= AppShutdownReason::OSForceClose
;
5056 MOZ_DIAGNOSTIC_ASSERT(false,
5057 "Received WM_ENDSESSION with unknown flags.");
5058 shutdownReason
= AppShutdownReason::OSForceClose
;
5062 case MOZ_WM_APP_QUIT
: {
5063 if (shutdownReason
== AppShutdownReason::Unknown
) {
5064 // TODO: We do not expect that these days anybody sends us
5065 // MOZ_WM_APP_QUIT, see bug 1827807.
5066 shutdownReason
= AppShutdownReason::WinUnexpectedMozQuit
;
5068 // Let's fake a shutdown sequence without actually closing windows etc.
5069 // to avoid Windows killing us in the middle. A proper shutdown would
5070 // require having a chance to pump some messages. Unfortunately
5071 // Windows won't let us do that. Bug 212316.
5072 nsCOMPtr
<nsIObserverService
> obsServ
=
5073 mozilla::services::GetObserverService();
5074 const char16_t
* syncShutdown
= u
"syncShutdown";
5075 const char16_t
* quitType
= GetQuitType();
5077 AppShutdown::Init(AppShutdownMode::Normal
, 0, shutdownReason
);
5079 obsServ
->NotifyObservers(nullptr, "quit-application-granted",
5081 obsServ
->NotifyObservers(nullptr, "quit-application-forced", nullptr);
5083 AppShutdown::OnShutdownConfirmed();
5085 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownConfirmed
,
5087 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownNetTeardown
,
5089 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownTeardown
,
5091 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdown
, nullptr);
5092 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownQM
, nullptr);
5093 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownTelemetry
,
5096 AppShutdown::DoImmediateExit();
5097 MOZ_ASSERT_UNREACHABLE("Our process was supposed to exit.");
5100 case WM_SYSCOLORCHANGE
:
5101 // No need to invalidate layout for system color changes, but we need to
5102 // invalidate style.
5103 NotifyThemeChanged(widget::ThemeChangeKind::Style
);
5106 case WM_THEMECHANGED
: {
5107 // Update non-client margin offsets
5108 UpdateNonClientMargins();
5109 nsUXThemeData::UpdateNativeThemeInfo();
5111 // We assume pretty much everything could've changed here.
5112 NotifyThemeChanged(widget::ThemeChangeKind::StyleAndLayout
);
5114 UpdateDarkModeToolbar();
5116 // Invalidate the window so that the repaint will
5117 // pick up the new theme.
5118 Invalidate(true, true, true);
5121 case WM_WTSSESSION_CHANGE
: {
5123 case WTS_CONSOLE_CONNECT
:
5124 case WTS_REMOTE_CONNECT
:
5125 case WTS_SESSION_UNLOCK
:
5126 // When a session becomes visible, we should invalidate.
5127 Invalidate(true, true, true);
5134 case WM_FONTCHANGE
: {
5135 // We only handle this message for the hidden window,
5136 // as we only need to update the (global) font list once
5137 // for any given change, not once per window!
5138 if (mWindowType
!= WindowType::Invisible
) {
5142 // update the global font list
5143 gfxPlatform::GetPlatform()->UpdateFontList();
5146 case WM_SETTINGCHANGE
: {
5147 if (wParam
== SPI_SETCLIENTAREAANIMATION
||
5148 wParam
== SPI_SETKEYBOARDDELAY
) {
5149 // These need to update LookAndFeel cached values.
5150 // They affect reduced motion settings / caret blink count / and
5151 // keyboard cues, so no need to invalidate style / layout.
5152 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly
);
5155 if (wParam
== SPI_SETFONTSMOOTHING
||
5156 wParam
== SPI_SETFONTSMOOTHINGTYPE
) {
5157 gfxDWriteFont::UpdateSystemTextVars();
5160 if (wParam
== SPI_SETWORKAREA
) {
5161 // NB: We also refresh screens on WM_DISPLAYCHANGE but the rcWork
5162 // values are sometimes wrong at that point. This message then
5163 // arrives soon afterward, when we can get the right rcWork values.
5164 ScreenHelperWin::RefreshScreens();
5167 if (auto lParamString
= reinterpret_cast<const wchar_t*>(lParam
)) {
5168 if (!wcscmp(lParamString
, L
"ImmersiveColorSet")) {
5169 // This affects system colors (-moz-win-accentcolor), so gotta pass
5171 NotifyThemeChanged(widget::ThemeChangeKind::Style
);
5175 // UserInteractionMode, ConvertibleSlateMode, SystemDockMode may cause
5176 // @media(pointer) queries to change, which layout needs to know about
5178 // (WM_SETTINGCHANGE will be sent to all top-level windows, so we
5179 // only respond to the hidden top-level window to avoid hammering
5180 // layout with a bunch of NotifyThemeChanged() calls)
5182 if (mWindowType
== WindowType::Invisible
) {
5183 if (!wcscmp(lParamString
, L
"UserInteractionMode") ||
5184 !wcscmp(lParamString
, L
"ConvertibleSlateMode") ||
5185 !wcscmp(lParamString
, L
"SystemDockMode")) {
5186 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly
);
5187 WindowsUIUtils::UpdateInTabletMode();
5192 // SPI_GETMOUSEVANISH sends WM_SETTINGCHANGE when changed but does
5193 // not include identifiers in the parameters. WM_SETTINGCHANGE docs
5194 // recommend updating all cached settings when this message is received
5196 GetMouseVanishSystemPref(true);
5199 case WM_DEVICECHANGE
: {
5200 if (wParam
== DBT_DEVICEARRIVAL
|| wParam
== DBT_DEVICEREMOVECOMPLETE
) {
5201 DEV_BROADCAST_HDR
* hdr
= reinterpret_cast<DEV_BROADCAST_HDR
*>(lParam
);
5202 // Check dbch_devicetype explicitly since we will get other device types
5203 // (e.g. DBT_DEVTYP_VOLUME) for some reasons even if we specify
5204 // DBT_DEVTYP_DEVICEINTERFACE in the filter for
5205 // RegisterDeviceNotification.
5206 if (hdr
->dbch_devicetype
== DBT_DEVTYP_DEVICEINTERFACE
) {
5207 // This can only change media queries (any-hover/any-pointer).
5208 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly
);
5213 case WM_NCCALCSIZE
: {
5214 // NOTE: the following block is mirrored in PreXULSkeletonUI.cpp, and
5215 // will need to be kept in sync.
5216 if (mCustomNonClient
) {
5217 // If `wParam` is `FALSE`, `lParam` points to a `RECT` that contains
5218 // the proposed window rectangle for our window. During our
5219 // processing of the `WM_NCCALCSIZE` message, we are expected to
5220 // modify the `RECT` that `lParam` points to, so that its value upon
5221 // our return is the new client area. We must return 0 if `wParam`
5224 // If `wParam` is `TRUE`, `lParam` points to a `NCCALCSIZE_PARAMS`
5225 // struct. This struct contains an array of 3 `RECT`s, the first of
5226 // which has the exact same meaning as the `RECT` that is pointed to
5227 // by `lParam` when `wParam` is `FALSE`. The remaining `RECT`s, in
5228 // conjunction with our return value, can
5229 // be used to specify portions of the source and destination window
5230 // rectangles that are valid and should be preserved. We opt not to
5231 // implement an elaborate client-area preservation technique, and
5232 // simply return 0, which means "preserve the entire old client area
5233 // and align it with the upper-left corner of our new client area".
5235 wParam
? &(reinterpret_cast<NCCALCSIZE_PARAMS
*>(lParam
))->rgrc
[0]
5236 : (reinterpret_cast<RECT
*>(lParam
));
5237 auto margin
= NonClientSizeMargin();
5238 clientRect
->top
+= margin
.top
;
5239 clientRect
->left
+= margin
.left
;
5240 clientRect
->right
-= margin
.right
;
5241 clientRect
->bottom
-= margin
.bottom
;
5242 // Make client rect's width and height more than 0 to
5243 // avoid problems of webrender and angle.
5244 clientRect
->right
= std::max(clientRect
->right
, clientRect
->left
+ 1);
5245 clientRect
->bottom
= std::max(clientRect
->bottom
, clientRect
->top
+ 1);
5253 case WM_NCHITTEST
: {
5254 if (mInputRegion
.mFullyTransparent
) {
5255 // Treat this window as transparent.
5256 *aRetValue
= HTTRANSPARENT
;
5261 if (mInputRegion
.mMargin
) {
5262 const LayoutDeviceIntPoint
screenPoint(GET_X_LPARAM(lParam
),
5263 GET_Y_LPARAM(lParam
));
5264 LayoutDeviceIntRect screenRect
= GetScreenBounds();
5265 screenRect
.Deflate(mInputRegion
.mMargin
);
5266 if (!screenRect
.Contains(screenPoint
)) {
5267 *aRetValue
= HTTRANSPARENT
;
5274 * If an nc client area margin has been moved, we are responsible
5275 * for calculating where the resize margins are and returning the
5276 * appropriate set of hit test constants. DwmDefWindowProc (above)
5277 * will handle hit testing on it's command buttons if we are on a
5278 * composited desktop.
5281 if (!mCustomNonClient
) {
5286 ClientMarginHitTestPoint(GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
5293 * WM_SETTEXT paints the titlebar area. Avoid this if we have a
5294 * custom titlebar we paint ourselves, or if we're the ones
5295 * sending the message with an updated title
5298 if ((mSendingSetText
&&
5299 gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) ||
5300 !mCustomNonClient
|| mNonClientMargins
.top
== -1)
5304 // From msdn, the way around this is to disable the visible state
5305 // temporarily. We need the text to be set but we don't want the
5306 // redraw to occur. However, we need to make sure that we don't
5307 // do this at the same time that a Present is happening.
5309 // To do this we take mPresentLock in nsWindow::PreRender and
5310 // if that lock is taken we wait before doing WM_SETTEXT
5311 if (mCompositorWidgetDelegate
) {
5312 mCompositorWidgetDelegate
->EnterPresentLock();
5314 DWORD style
= GetWindowLong(mWnd
, GWL_STYLE
);
5315 SetWindowLong(mWnd
, GWL_STYLE
, style
& ~WS_VISIBLE
);
5317 CallWindowProcW(GetPrevWindowProc(), mWnd
, msg
, wParam
, lParam
);
5318 SetWindowLong(mWnd
, GWL_STYLE
, style
);
5319 if (mCompositorWidgetDelegate
) {
5320 mCompositorWidgetDelegate
->LeavePresentLock();
5326 case WM_NCACTIVATE
: {
5328 * WM_NCACTIVATE paints nc areas. Avoid this and re-route painting
5329 * through WM_NCPAINT via InvalidateNonClientRegion.
5331 UpdateGetWindowInfoCaptionStatus(FALSE
!= wParam
);
5333 if (!mCustomNonClient
) {
5337 // There is a case that rendered result is not kept. Bug 1237617
5338 if (wParam
== TRUE
&& !gfxEnv::MOZ_DISABLE_FORCE_PRESENT() &&
5339 gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
5340 NS_DispatchToMainThread(NewRunnableMethod(
5341 "nsWindow::ForcePresent", this, &nsWindow::ForcePresent
));
5344 // let the dwm handle nc painting on glass
5345 // Never allow native painting if we are on fullscreen
5346 if (mFrameState
->GetSizeMode() != nsSizeMode_Fullscreen
&&
5347 gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled())
5350 if (wParam
== TRUE
) {
5352 *aRetValue
= FALSE
; // ignored
5354 // invalidate to trigger a paint
5355 InvalidateNonClientRegion();
5359 *aRetValue
= TRUE
; // go ahead and deactive
5361 // invalidate to trigger a paint
5362 InvalidateNonClientRegion();
5369 * ClearType changes often don't send a WM_SETTINGCHANGE message. But they
5370 * do seem to always send a WM_NCPAINT message, so let's update on that.
5372 gfxDWriteFont::UpdateSystemTextVars();
5375 * Reset the non-client paint region so that it excludes the
5376 * non-client areas we paint manually. Then call defwndproc
5377 * to do the actual painting.
5380 if (!mCustomNonClient
) break;
5382 // let the dwm handle nc painting on glass
5383 if (gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) break;
5385 HRGN paintRgn
= ExcludeNonClientFromPaintRegion((HRGN
)wParam
);
5386 LRESULT res
= CallWindowProcW(GetPrevWindowProc(), mWnd
, msg
,
5387 (WPARAM
)paintRgn
, lParam
);
5388 if (paintRgn
!= (HRGN
)wParam
) DeleteObject(paintRgn
);
5393 case WM_POWERBROADCAST
:
5395 case PBT_APMSUSPEND
:
5396 PostSleepWakeNotification(true);
5398 case PBT_APMRESUMEAUTOMATIC
:
5399 case PBT_APMRESUMECRITICAL
:
5400 case PBT_APMRESUMESUSPEND
:
5401 PostSleepWakeNotification(false);
5406 case WM_CLOSE
: // close request
5407 if (mWidgetListener
) mWidgetListener
->RequestWindowClose(this);
5408 result
= true; // abort window closure
5413 DestroyLayerManager();
5419 *aRetValue
= (int)OnPaint(nullptr, 0);
5423 case WM_PRINTCLIENT
:
5424 result
= OnPaint((HDC
)wParam
, 0);
5428 result
= OnHotKey(wParam
, lParam
);
5433 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5434 result
= ProcessCharMessage(nativeMsg
, nullptr);
5435 DispatchPendingEvents();
5440 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5441 nativeMsg
.time
= ::GetMessageTime();
5442 result
= ProcessKeyUpMessage(nativeMsg
, nullptr);
5443 DispatchPendingEvents();
5448 if (IsMouseVanishKey(wParam
)) {
5449 MaybeHideCursor(true);
5452 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5453 result
= ProcessKeyDownMessage(nativeMsg
, nullptr);
5454 DispatchPendingEvents();
5457 // Say we've dealt with erasing the background. (This is actually handled in
5458 // WM_PAINT, where necessary.)
5459 case WM_ERASEBKGND
: {
5464 case WM_MOUSEMOVE
: {
5465 MaybeHideCursor(false);
5467 LPARAM lParamScreen
= lParamToScreen(lParam
);
5468 mSimulatedClientArea
= IsSimulatedClientArea(GET_X_LPARAM(lParamScreen
),
5469 GET_Y_LPARAM(lParamScreen
));
5471 if (!mMousePresent
&& !sIsInMouseCapture
) {
5472 // First MOUSEMOVE over the client area. Ask for MOUSELEAVE
5473 TRACKMOUSEEVENT mTrack
;
5474 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
5475 mTrack
.dwFlags
= TME_LEAVE
;
5476 mTrack
.dwHoverTime
= 0;
5477 mTrack
.hwndTrack
= mWnd
;
5478 TrackMouseEvent(&mTrack
);
5480 mMousePresent
= true;
5482 // Suppress dispatch of pending events
5483 // when mouse moves are generated by widget
5484 // creation instead of user input.
5486 mp
.x
= GET_X_LPARAM(lParamScreen
);
5487 mp
.y
= GET_Y_LPARAM(lParamScreen
);
5488 bool userMovedMouse
= false;
5489 if ((sLastMouseMovePoint
.x
!= mp
.x
) || (sLastMouseMovePoint
.y
!= mp
.y
)) {
5490 userMovedMouse
= true;
5493 if (userMovedMouse
) {
5494 result
= DispatchMouseEvent(
5495 eMouseMove
, wParam
, lParam
, false, MouseButton::ePrimary
,
5496 MOUSE_INPUT_SOURCE(),
5497 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5498 DispatchPendingEvents();
5502 case WM_NCMOUSEMOVE
: {
5503 MaybeHideCursor(false);
5505 LPARAM lParamClient
= lParamToClient(lParam
);
5506 if (IsSimulatedClientArea(GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
))) {
5507 if (!sIsInMouseCapture
) {
5508 TRACKMOUSEEVENT mTrack
;
5509 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
5510 mTrack
.dwFlags
= TME_LEAVE
| TME_NONCLIENT
;
5511 mTrack
.dwHoverTime
= 0;
5512 mTrack
.hwndTrack
= mWnd
;
5513 TrackMouseEvent(&mTrack
);
5515 // If we noticed the mouse moving in our draggable region, forward the
5516 // message as a normal WM_MOUSEMOVE.
5517 SendMessage(mWnd
, WM_MOUSEMOVE
, 0, lParamClient
);
5519 // We've transitioned from a draggable area to somewhere else within
5520 // the non-client area - perhaps one of the edges of the window for
5522 mSimulatedClientArea
= false;
5525 if (mMousePresent
&& !sIsInMouseCapture
&& !mSimulatedClientArea
) {
5526 SendMessage(mWnd
, WM_MOUSELEAVE
, 0, 0);
5530 case WM_LBUTTONDOWN
: {
5531 MaybeHideCursor(false);
5534 DispatchMouseEvent(eMouseDown
, wParam
, lParam
, false,
5535 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE(),
5536 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5537 DispatchPendingEvents();
5540 case WM_LBUTTONUP
: {
5542 DispatchMouseEvent(eMouseUp
, wParam
, lParam
, false,
5543 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE(),
5544 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5545 DispatchPendingEvents();
5548 case WM_NCMOUSELEAVE
: {
5549 mSimulatedClientArea
= false;
5551 if (EventIsInsideWindow(this)) {
5552 // If we're handling WM_NCMOUSELEAVE and the mouse is still over the
5553 // window, then by process of elimination, the mouse has moved from the
5554 // non-client to client area, so no need to fall-through to the
5555 // WM_MOUSELEAVE handler. We also need to re-register for the
5556 // WM_MOUSELEAVE message, since according to the documentation at [1],
5557 // all tracking requested via TrackMouseEvent is cleared once
5558 // WM_NCMOUSELEAVE or WM_MOUSELEAVE fires.
5560 // https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-trackmouseevent
5561 TRACKMOUSEEVENT mTrack
;
5562 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
5563 mTrack
.dwFlags
= TME_LEAVE
;
5564 mTrack
.dwHoverTime
= 0;
5565 mTrack
.hwndTrack
= mWnd
;
5566 TrackMouseEvent(&mTrack
);
5569 // We've transitioned from non-client to outside of the window, so
5570 // fall-through to the WM_MOUSELEAVE handler.
5573 case WM_MOUSELEAVE
: {
5574 if (!mMousePresent
) break;
5575 if (mSimulatedClientArea
) break;
5576 mMousePresent
= false;
5578 // Check if the mouse is over the fullscreen transition window, if so
5579 // clear sLastMouseMovePoint. This way the WM_MOUSEMOVE we get after the
5580 // transition window disappears will not be ignored, even if the mouse
5582 if (mTransitionWnd
&& WindowAtMouse() == mTransitionWnd
) {
5583 sLastMouseMovePoint
= {0};
5586 // We need to check mouse button states and put them in for
5588 WPARAM mouseState
= (GetKeyState(VK_LBUTTON
) ? MK_LBUTTON
: 0) |
5589 (GetKeyState(VK_MBUTTON
) ? MK_MBUTTON
: 0) |
5590 (GetKeyState(VK_RBUTTON
) ? MK_RBUTTON
: 0);
5591 // Synthesize an event position because we don't get one from
5593 LPARAM pos
= lParamToClient(::GetMessagePos());
5594 DispatchMouseEvent(eMouseExitFromWidget
, mouseState
, pos
, false,
5595 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5598 case MOZ_WM_PEN_LEAVES_HOVER_OF_DIGITIZER
: {
5599 LPARAM pos
= lParamToClient(::GetMessagePos());
5600 MOZ_ASSERT(InkCollector::sInkCollector
);
5601 uint16_t pointerId
= InkCollector::sInkCollector
->GetPointerId();
5602 if (pointerId
!= 0) {
5603 WinPointerInfo pointerInfo
;
5604 pointerInfo
.pointerId
= pointerId
;
5605 DispatchMouseEvent(eMouseExitFromWidget
, wParam
, pos
, false,
5606 MouseButton::ePrimary
,
5607 MouseEvent_Binding::MOZ_SOURCE_PEN
, &pointerInfo
);
5608 InkCollector::sInkCollector
->ClearTarget();
5609 InkCollector::sInkCollector
->ClearPointerId();
5613 case WM_CONTEXTMENU
: {
5614 // If the context menu is brought up by a touch long-press, then
5615 // the APZ code is responsible for dealing with this, so we don't
5616 // need to do anything.
5618 MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
5619 MOZ_ASSERT(mAPZC
); // since mTouchWindow is true, APZ must be enabled
5624 // If this WM_CONTEXTMENU is triggered by a mouse's secondary button up
5625 // event in overscroll gutter, we shouldn't open context menu.
5626 if (MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_MOUSE
&&
5627 mNeedsToPreventContextMenu
) {
5632 // if the context menu is brought up from the keyboard, |lParam|
5635 bool contextMenukey
= false;
5637 contextMenukey
= true;
5638 pos
= lParamToClient(GetMessagePos());
5640 pos
= lParamToClient(lParam
);
5643 result
= DispatchMouseEvent(
5644 eContextMenu
, wParam
, pos
, contextMenukey
,
5645 contextMenukey
? MouseButton::ePrimary
: MouseButton::eSecondary
,
5646 MOUSE_INPUT_SOURCE());
5647 if (lParam
!= -1 && !result
&& mCustomNonClient
&&
5648 mDraggableRegion
.Contains(GET_X_LPARAM(pos
), GET_Y_LPARAM(pos
))) {
5649 // Blank area hit, throw up the system menu.
5650 DisplaySystemMenu(mWnd
, mFrameState
->GetSizeMode(), mIsRTL
,
5651 GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
5656 case WM_POINTERLEAVE
:
5657 case WM_POINTERDOWN
:
5659 case WM_POINTERUPDATE
:
5660 MaybeHideCursor(false);
5661 result
= OnPointerEvents(msg
, wParam
, lParam
);
5663 DispatchPendingEvents();
5667 case DM_POINTERHITTEST
:
5669 UINT contactId
= GET_POINTERID_WPARAM(wParam
);
5670 POINTER_INPUT_TYPE pointerType
;
5671 if (mPointerEvents
.GetPointerType(contactId
, &pointerType
) &&
5672 pointerType
== PT_TOUCHPAD
) {
5673 mDmOwner
->SetContact(contactId
);
5678 case WM_LBUTTONDBLCLK
:
5679 MaybeHideCursor(false);
5680 result
= DispatchMouseEvent(eMouseDoubleClick
, wParam
, lParam
, false,
5681 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5682 DispatchPendingEvents();
5685 case WM_MBUTTONDOWN
:
5686 MaybeHideCursor(false);
5687 result
= DispatchMouseEvent(eMouseDown
, wParam
, lParam
, false,
5688 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5689 DispatchPendingEvents();
5693 result
= DispatchMouseEvent(eMouseUp
, wParam
, lParam
, false,
5694 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5695 DispatchPendingEvents();
5698 case WM_MBUTTONDBLCLK
:
5699 MaybeHideCursor(false);
5700 result
= DispatchMouseEvent(eMouseDoubleClick
, wParam
, lParam
, false,
5701 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5702 DispatchPendingEvents();
5705 case WM_NCMBUTTONDOWN
:
5706 MaybeHideCursor(false);
5707 result
= DispatchMouseEvent(eMouseDown
, 0, lParamToClient(lParam
), false,
5708 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5709 DispatchPendingEvents();
5712 case WM_NCMBUTTONUP
:
5713 result
= DispatchMouseEvent(eMouseUp
, 0, lParamToClient(lParam
), false,
5714 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5715 DispatchPendingEvents();
5718 case WM_NCMBUTTONDBLCLK
:
5719 MaybeHideCursor(false);
5721 DispatchMouseEvent(eMouseDoubleClick
, 0, lParamToClient(lParam
),
5722 false, MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5723 DispatchPendingEvents();
5726 case WM_RBUTTONDOWN
:
5727 MaybeHideCursor(false);
5729 DispatchMouseEvent(eMouseDown
, wParam
, lParam
, false,
5730 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE(),
5731 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5732 DispatchPendingEvents();
5737 DispatchMouseEvent(eMouseUp
, wParam
, lParam
, false,
5738 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE(),
5739 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5740 DispatchPendingEvents();
5743 case WM_RBUTTONDBLCLK
:
5744 MaybeHideCursor(false);
5746 DispatchMouseEvent(eMouseDoubleClick
, wParam
, lParam
, false,
5747 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE());
5748 DispatchPendingEvents();
5751 case WM_NCRBUTTONDOWN
:
5752 MaybeHideCursor(false);
5754 DispatchMouseEvent(eMouseDown
, 0, lParamToClient(lParam
), false,
5755 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE());
5756 DispatchPendingEvents();
5759 case WM_NCRBUTTONUP
:
5761 DispatchMouseEvent(eMouseUp
, 0, lParamToClient(lParam
), false,
5762 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE());
5763 DispatchPendingEvents();
5766 case WM_NCRBUTTONDBLCLK
:
5767 MaybeHideCursor(false);
5768 result
= DispatchMouseEvent(eMouseDoubleClick
, 0, lParamToClient(lParam
),
5769 false, MouseButton::eSecondary
,
5770 MOUSE_INPUT_SOURCE());
5771 DispatchPendingEvents();
5774 // Windows doesn't provide to customize the behavior of 4th nor 5th button
5775 // of mouse. If 5-button mouse works with standard mouse deriver of
5776 // Windows, users cannot disable 4th button (browser back) nor 5th button
5777 // (browser forward). We should allow to do it with our prefs since we can
5778 // prevent Windows to generate WM_APPCOMMAND message if WM_XBUTTONUP
5779 // messages are not sent to DefWindowProc.
5780 case WM_XBUTTONDOWN
:
5782 case WM_NCXBUTTONDOWN
:
5783 case WM_NCXBUTTONUP
:
5784 MaybeHideCursor(false);
5787 switch (GET_XBUTTON_WPARAM(wParam
)) {
5789 result
= !Preferences::GetBool("mousebutton.4th.enabled", true);
5792 result
= !Preferences::GetBool("mousebutton.5th.enabled", true);
5800 if (mAspectRatio
> 0) {
5801 LPRECT rect
= (LPRECT
)lParam
;
5802 int32_t newWidth
, newHeight
;
5804 // The following conditions and switch statement borrow heavily from the
5805 // Chromium source code from
5806 // https://chromium.googlesource.com/chromium/src/+/456d6e533cfb4531995e0ef52c279d4b5aa8a352/ui/views/window/window_resize_utils.cc#45
5807 if (wParam
== WMSZ_LEFT
|| wParam
== WMSZ_RIGHT
||
5808 wParam
== WMSZ_TOPLEFT
|| wParam
== WMSZ_BOTTOMLEFT
) {
5809 newWidth
= rect
->right
- rect
->left
;
5810 newHeight
= newWidth
/ mAspectRatio
;
5811 if (newHeight
< mSizeConstraints
.mMinSize
.height
) {
5812 newHeight
= mSizeConstraints
.mMinSize
.height
;
5813 newWidth
= newHeight
* mAspectRatio
;
5814 } else if (newHeight
> mSizeConstraints
.mMaxSize
.height
) {
5815 newHeight
= mSizeConstraints
.mMaxSize
.height
;
5816 newWidth
= newHeight
* mAspectRatio
;
5819 newHeight
= rect
->bottom
- rect
->top
;
5820 newWidth
= newHeight
* mAspectRatio
;
5821 if (newWidth
< mSizeConstraints
.mMinSize
.width
) {
5822 newWidth
= mSizeConstraints
.mMinSize
.width
;
5823 newHeight
= newWidth
/ mAspectRatio
;
5824 } else if (newWidth
> mSizeConstraints
.mMaxSize
.width
) {
5825 newWidth
= mSizeConstraints
.mMaxSize
.width
;
5826 newHeight
= newWidth
/ mAspectRatio
;
5833 rect
->right
= newWidth
+ rect
->left
;
5834 rect
->bottom
= rect
->top
+ newHeight
;
5837 rect
->right
= newWidth
+ rect
->left
;
5838 rect
->top
= rect
->bottom
- newHeight
;
5842 rect
->left
= rect
->right
- newWidth
;
5843 rect
->top
= rect
->bottom
- newHeight
;
5846 rect
->right
= rect
->left
+ newWidth
;
5847 rect
->top
= rect
->bottom
- newHeight
;
5849 case WMSZ_BOTTOMLEFT
:
5850 rect
->left
= rect
->right
- newWidth
;
5851 rect
->bottom
= rect
->top
+ newHeight
;
5853 case WMSZ_BOTTOMRIGHT
:
5854 rect
->right
= rect
->left
+ newWidth
;
5855 rect
->bottom
= rect
->top
+ newHeight
;
5860 // When we get WM_ENTERSIZEMOVE we don't know yet if we're in a live
5861 // resize or move event. Instead we wait for first VM_SIZING message
5862 // within a ENTERSIZEMOVE to consider this a live resize event.
5863 if (mResizeState
== IN_SIZEMOVE
) {
5864 mResizeState
= RESIZING
;
5865 NotifyLiveResizeStarted();
5871 FinishLiveResizing(MOVING
);
5872 if (WinUtils::IsPerMonitorDPIAware()) {
5873 // Sometimes, we appear to miss a WM_DPICHANGED message while moving
5874 // a window around. Therefore, call ChangedDPI and ResetLayout here
5875 // if it appears that the window's scaling is not what we expect.
5876 // This causes the prescontext and appshell window management code to
5877 // check the appUnitsPerDevPixel value and current widget size, and
5878 // refresh them if necessary. If nothing has changed, these calls will
5879 // return without actually triggering any extra reflow or painting.
5880 if (WinUtils::LogToPhysFactor(mWnd
) != mDefaultScale
) {
5883 if (mWidgetListener
) {
5884 mWidgetListener
->UIResolutionChanged();
5890 case WM_ENTERSIZEMOVE
: {
5891 if (mResizeState
== NOT_RESIZING
) {
5892 mResizeState
= IN_SIZEMOVE
;
5897 case WM_EXITSIZEMOVE
: {
5898 FinishLiveResizing(NOT_RESIZING
);
5900 if (!sIsInMouseCapture
) {
5901 NotifySizeMoveDone();
5907 case WM_DISPLAYCHANGE
: {
5908 ScreenHelperWin::RefreshScreens();
5909 if (mWidgetListener
) {
5910 mWidgetListener
->UIResolutionChanged();
5915 case WM_NCLBUTTONDBLCLK
:
5916 DispatchMouseEvent(eMouseDoubleClick
, 0, lParamToClient(lParam
), false,
5917 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5918 result
= DispatchMouseEvent(eMouseUp
, 0, lParamToClient(lParam
), false,
5919 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5920 DispatchPendingEvents();
5923 case WM_NCLBUTTONDOWN
: {
5924 // Dispatch a custom event when this happens in the draggable region, so
5925 // that non-popup-based panels can react to it. This doesn't send an
5926 // actual mousedown event because that would break dragging or interfere
5927 // with other mousedown handling in the caption area.
5928 if (ClientMarginHitTestPoint(GET_X_LPARAM(lParam
),
5929 GET_Y_LPARAM(lParam
)) == HTCAPTION
) {
5930 DispatchCustomEvent(u
"draggableregionleftmousedown"_ns
);
5933 if (IsWindowButton(wParam
) && mCustomNonClient
) {
5934 DispatchMouseEvent(eMouseDown
, wParamFromGlobalMouseState(),
5935 lParamToClient(lParam
), false, MouseButton::ePrimary
,
5936 MOUSE_INPUT_SOURCE(), nullptr, true);
5937 DispatchPendingEvents();
5943 case WM_APPCOMMAND
: {
5944 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5945 result
= HandleAppCommandMsg(nativeMsg
, aRetValue
);
5949 // The WM_ACTIVATE event is fired when a window is raised or lowered,
5950 // and the loword of wParam specifies which. But we don't want to tell
5951 // the focus system about this until the WM_SETFOCUS or WM_KILLFOCUS
5952 // events are fired. Instead, set either the sJustGotActivate or
5953 // gJustGotDeactivate flags and activate/deactivate once the focus
5956 int32_t fActive
= LOWORD(wParam
);
5958 MaybeHideCursor(false);
5961 if (mWidgetListener
) {
5962 if (WA_INACTIVE
== fActive
) {
5963 // when minimizing a window, the deactivation and focus events will
5964 // be fired in the reverse order. Instead, just deactivate right away.
5965 // This can also happen when a modal system dialog is opened, so check
5966 // if the last window to receive the WM_KILLFOCUS message was this one
5967 // or a child of this one.
5968 if (HIWORD(wParam
) ||
5969 (mLastKillFocusWindow
&&
5970 (GetTopLevelForFocus(mLastKillFocusWindow
) == mWnd
))) {
5971 DispatchFocusToTopLevelWindow(false);
5973 sJustGotDeactivate
= true;
5975 if (mIsTopWidgetWindow
) {
5976 mLastKeyboardLayout
= KeyboardLayout::GetInstance()->GetLayout();
5981 sJustGotActivate
= true;
5982 WidgetMouseEvent
event(true, eMouseActivate
, this,
5983 WidgetMouseEvent::eReal
);
5985 ModifierKeyState modifierKeyState
;
5986 modifierKeyState
.InitInputEvent(event
);
5987 DispatchInputEvent(&event
);
5988 if (sSwitchKeyboardLayout
&& mLastKeyboardLayout
)
5989 ActivateKeyboardLayout(mLastKeyboardLayout
, 0);
5991 #ifdef ACCESSIBILITY
5992 a11y::LazyInstantiator::ResetUiaDetectionCache();
5998 case WM_ACTIVATEAPP
: {
5999 GPUProcessManager::Get()->SetAppInForeground(wParam
);
6002 case WM_MOUSEACTIVATE
:
6003 // A popup with a parent owner should not be activated when clicked but
6004 // should still allow the mouse event to be fired, so the return value
6005 // is set to MA_NOACTIVATE. But if the owner isn't the frontmost window,
6006 // just use default processing so that the window is activated.
6007 if (IsPopup() && IsOwnerForegroundWindow()) {
6008 *aRetValue
= MA_NOACTIVATE
;
6013 case WM_WINDOWPOSCHANGING
: {
6014 LPWINDOWPOS info
= (LPWINDOWPOS
)lParam
;
6015 OnWindowPosChanging(info
);
6019 case WM_GETMINMAXINFO
: {
6020 MINMAXINFO
* mmi
= (MINMAXINFO
*)lParam
;
6021 // Set the constraints. The minimum size should also be constrained to the
6022 // default window maximum size so that it fits on screen.
6023 mmi
->ptMinTrackSize
.x
=
6024 std::min((int32_t)mmi
->ptMaxTrackSize
.x
,
6025 std::max((int32_t)mmi
->ptMinTrackSize
.x
,
6026 mSizeConstraints
.mMinSize
.width
));
6027 mmi
->ptMinTrackSize
.y
=
6028 std::min((int32_t)mmi
->ptMaxTrackSize
.y
,
6029 std::max((int32_t)mmi
->ptMinTrackSize
.y
,
6030 mSizeConstraints
.mMinSize
.height
));
6031 mmi
->ptMaxTrackSize
.x
= std::min((int32_t)mmi
->ptMaxTrackSize
.x
,
6032 mSizeConstraints
.mMaxSize
.width
);
6033 mmi
->ptMaxTrackSize
.y
= std::min((int32_t)mmi
->ptMaxTrackSize
.y
,
6034 mSizeConstraints
.mMaxSize
.height
);
6038 // If previous focused window isn't ours, it must have received the
6039 // redirected message. So, we should forget it.
6040 if (!WinUtils::IsOurProcessWindow(HWND(wParam
))) {
6041 RedirectedKeyDownMessageManager::Forget();
6043 if (sJustGotActivate
) {
6044 DispatchFocusToTopLevelWindow(true);
6046 TaskbarConcealer::OnFocusAcquired(this);
6050 if (sJustGotDeactivate
) {
6051 DispatchFocusToTopLevelWindow(false);
6053 mLastKillFocusWindow
= mWnd
;
6057 case WM_WINDOWPOSCHANGED
: {
6058 WINDOWPOS
* wp
= (LPWINDOWPOS
)lParam
;
6059 OnWindowPosChanged(wp
);
6060 TaskbarConcealer::OnWindowPosChanged(this);
6064 case WM_INPUTLANGCHANGEREQUEST
:
6069 case WM_INPUTLANGCHANGE
:
6070 KeyboardLayout::GetInstance()->OnLayoutChange(
6071 reinterpret_cast<HKL
>(lParam
));
6072 nsBidiKeyboard::OnLayoutChange();
6073 result
= false; // always pass to child window
6076 case WM_DESTROYCLIPBOARD
: {
6077 nsIClipboard
* clipboard
;
6078 nsresult rv
= CallGetService(kCClipboardCID
, &clipboard
);
6079 if (NS_SUCCEEDED(rv
)) {
6080 clipboard
->EmptyClipboard(nsIClipboard::kGlobalClipboard
);
6081 NS_RELEASE(clipboard
);
6085 #ifdef ACCESSIBILITY
6086 case WM_GETOBJECT
: {
6088 // Do explicit casting to make it working on 64bit systems (see bug 649236
6090 int32_t objId
= static_cast<DWORD
>(lParam
);
6091 if (objId
== OBJID_CLIENT
) { // oleacc.dll will be loaded dynamically
6092 RefPtr
<IAccessible
> root(
6093 a11y::LazyInstantiator::GetRootAccessible(mWnd
));
6095 *aRetValue
= LresultFromObject(IID_IAccessible
, wParam
, root
);
6096 a11y::LazyInstantiator::EnableBlindAggregation(mWnd
);
6103 case WM_SYSCOMMAND
: {
6104 WPARAM filteredWParam
= (wParam
& 0xFFF0);
6105 if (mFrameState
->GetSizeMode() == nsSizeMode_Fullscreen
&&
6106 filteredWParam
== SC_RESTORE
&&
6107 GetCurrentShowCmd(mWnd
) != SW_SHOWMINIMIZED
) {
6108 mFrameState
->EnsureFullscreenMode(false);
6112 // Handle the system menu manually when we're in full screen mode
6113 // so we can set the appropriate options.
6114 if (filteredWParam
== SC_KEYMENU
&& lParam
== VK_SPACE
&&
6115 mFrameState
->GetSizeMode() == nsSizeMode_Fullscreen
) {
6116 DisplaySystemMenu(mWnd
, mFrameState
->GetSizeMode(), mIsRTL
,
6117 MOZ_SYSCONTEXT_X_POS
, MOZ_SYSCONTEXT_Y_POS
);
6122 case WM_DWMCOMPOSITIONCHANGED
:
6123 // Every window will get this message, but gfxVars only broadcasts
6124 // updates when the value actually changes
6125 if (XRE_IsParentProcess()) {
6126 BOOL dwmEnabled
= FALSE
;
6127 if (FAILED(::DwmIsCompositionEnabled(&dwmEnabled
)) || !dwmEnabled
) {
6128 gfxVars::SetDwmCompositionEnabled(false);
6130 gfxVars::SetDwmCompositionEnabled(true);
6134 UpdateNonClientMargins();
6135 BroadcastMsg(mWnd
, WM_DWMCOMPOSITIONCHANGED
);
6136 // TODO: Why is NotifyThemeChanged needed, what does it affect? And can we
6137 // make it more granular by tweaking the ChangeKind we pass?
6138 NotifyThemeChanged(widget::ThemeChangeKind::StyleAndLayout
);
6139 Invalidate(true, true, true);
6142 case WM_DPICHANGED
: {
6143 LPRECT rect
= (LPRECT
)lParam
;
6144 OnDPIChanged(rect
->left
, rect
->top
, rect
->right
- rect
->left
,
6145 rect
->bottom
- rect
->top
);
6149 /* Gesture support events */
6150 case WM_TABLET_QUERYSYSTEMGESTURESTATUS
:
6151 // According to MS samples, this must be handled to enable
6152 // rotational support in multi-touch drivers.
6154 *aRetValue
= TABLET_ROTATE_GESTURE_ENABLE
;
6158 result
= OnTouch(wParam
, lParam
);
6165 result
= OnGesture(wParam
, lParam
);
6168 case WM_GESTURENOTIFY
: {
6169 if (mWindowType
!= WindowType::Invisible
) {
6170 // A GestureNotify event is dispatched to decide which single-finger
6171 // panning direction should be active (including none) and if pan
6172 // feedback should be displayed. Java and plugin windows can make their
6175 GESTURENOTIFYSTRUCT
* gestureinfo
= (GESTURENOTIFYSTRUCT
*)lParam
;
6176 nsPointWin touchPoint
;
6177 touchPoint
= gestureinfo
->ptsLocation
;
6178 touchPoint
.ScreenToClient(mWnd
);
6179 WidgetGestureNotifyEvent
gestureNotifyEvent(true, eGestureNotify
, this);
6180 gestureNotifyEvent
.mRefPoint
=
6181 LayoutDeviceIntPoint::FromUnknownPoint(touchPoint
);
6182 nsEventStatus status
;
6183 DispatchEvent(&gestureNotifyEvent
, status
);
6184 mDisplayPanFeedback
= gestureNotifyEvent
.mDisplayPanFeedback
;
6186 mGesture
.SetWinGestureSupport(mWnd
, gestureNotifyEvent
.mPanDirection
);
6188 result
= false; // should always bubble to DefWindowProc
6192 WidgetContentCommandEvent
command(true, eContentCommandDelete
, this);
6193 DispatchWindowEvent(command
);
6198 WidgetContentCommandEvent
command(true, eContentCommandCut
, this);
6199 DispatchWindowEvent(command
);
6204 WidgetContentCommandEvent
command(true, eContentCommandCopy
, this);
6205 DispatchWindowEvent(command
);
6210 WidgetContentCommandEvent
command(true, eContentCommandPaste
, this);
6211 DispatchWindowEvent(command
);
6216 WidgetContentCommandEvent
command(true, eContentCommandUndo
, this);
6217 DispatchWindowEvent(command
);
6218 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6223 WidgetContentCommandEvent
command(true, eContentCommandRedo
, this);
6224 DispatchWindowEvent(command
);
6225 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6230 // Support EM_CANPASTE message only when wParam isn't specified or
6231 // is plain text format.
6232 if (wParam
== 0 || wParam
== CF_TEXT
|| wParam
== CF_UNICODETEXT
) {
6233 WidgetContentCommandEvent
command(true, eContentCommandPaste
, this,
6235 DispatchWindowEvent(command
);
6236 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6242 WidgetContentCommandEvent
command(true, eContentCommandUndo
, this, true);
6243 DispatchWindowEvent(command
);
6244 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6249 WidgetContentCommandEvent
command(true, eContentCommandRedo
, this, true);
6250 DispatchWindowEvent(command
);
6251 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6255 case MOZ_WM_SKEWFIX
: {
6256 TimeStamp skewStamp
;
6257 if (CurrentWindowsTimeGetter::GetAndClearBackwardsSkewStamp(wParam
,
6259 TimeConverter().CompensateForBackwardsSkew(::GetMessageTime(),
6265 if (msg
== nsAppShell::GetTaskbarButtonCreatedMessage()) {
6266 SetHasTaskbarIconBeenCreated();
6271 //*aRetValue = result;
6275 // Events which caused mWnd destruction and aren't consumed
6276 // will crash during the Windows default processing.
6281 void nsWindow::FinishLiveResizing(ResizeState aNewState
) {
6282 if (mResizeState
== RESIZING
) {
6283 NotifyLiveResizeStopped();
6285 mResizeState
= aNewState
;
6289 /**************************************************************
6291 * SECTION: Broadcast messaging
6293 * Broadcast messages to all windows.
6295 **************************************************************/
6297 // Enumerate all child windows sending aMsg to each of them
6298 BOOL CALLBACK
nsWindow::BroadcastMsgToChildren(HWND aWnd
, LPARAM aMsg
) {
6299 WNDPROC winProc
= (WNDPROC
)::GetWindowLongPtrW(aWnd
, GWLP_WNDPROC
);
6300 if (winProc
== &nsWindow::WindowProc
) {
6301 // it's one of our windows so go ahead and send a message to it
6302 ::CallWindowProcW(winProc
, aWnd
, aMsg
, 0, 0);
6307 // Enumerate all top level windows specifying that the children of each
6308 // top level window should be enumerated. Do *not* send the message to
6309 // each top level window since it is assumed that the toolkit will send
6310 // aMsg to them directly.
6311 BOOL CALLBACK
nsWindow::BroadcastMsg(HWND aTopWindow
, LPARAM aMsg
) {
6312 // Iterate each of aTopWindows child windows sending the aMsg
6314 ::EnumChildWindows(aTopWindow
, nsWindow::BroadcastMsgToChildren
, aMsg
);
6318 /**************************************************************
6320 * SECTION: Event processing helpers
6322 * Special processing for certain event types and
6323 * synthesized events.
6325 **************************************************************/
6327 LayoutDeviceIntMargin
nsWindow::NonClientSizeMargin(
6328 const LayoutDeviceIntMargin
& aNonClientOffset
) const {
6329 return LayoutDeviceIntMargin(mCaptionHeight
- aNonClientOffset
.top
,
6330 mHorResizeMargin
- aNonClientOffset
.right
,
6331 mVertResizeMargin
- aNonClientOffset
.bottom
,
6332 mHorResizeMargin
- aNonClientOffset
.left
);
6335 int32_t nsWindow::ClientMarginHitTestPoint(int32_t aX
, int32_t aY
) {
6336 const nsSizeMode sizeMode
= mFrameState
->GetSizeMode();
6337 if (sizeMode
== nsSizeMode_Minimized
|| sizeMode
== nsSizeMode_Fullscreen
) {
6341 // Calculations are done in screen coords
6342 const LayoutDeviceIntRect winRect
= GetScreenBounds();
6343 const LayoutDeviceIntPoint
point(aX
, aY
);
6345 // hit return constants:
6346 // HTBORDER - non-resizable border
6347 // HTBOTTOM, HTLEFT, HTRIGHT, HTTOP - resizable border
6348 // HTBOTTOMLEFT, HTBOTTOMRIGHT - resizable corner
6349 // HTTOPLEFT, HTTOPRIGHT - resizable corner
6350 // HTCAPTION - general title bar area
6351 // HTCLIENT - area considered the client
6352 // HTCLOSE - hovering over the close button
6353 // HTMAXBUTTON - maximize button
6354 // HTMINBUTTON - minimize button
6356 int32_t testResult
= HTCLIENT
;
6357 const bool isResizable
=
6358 sizeMode
!= nsSizeMode_Maximized
&&
6360 (BorderStyle::All
| BorderStyle::ResizeH
| BorderStyle::Default
));
6362 LayoutDeviceIntMargin nonClientSizeMargin
= NonClientSizeMargin();
6364 // Ensure being accessible to borders of window. Even if contents are in
6365 // this area, the area must behave as border.
6366 nonClientSizeMargin
.EnsureAtLeast(
6367 LayoutDeviceIntMargin(kResizableBorderMinSize
, kResizableBorderMinSize
,
6368 kResizableBorderMinSize
, kResizableBorderMinSize
));
6370 LayoutDeviceIntRect clientRect
= winRect
;
6371 clientRect
.Deflate(nonClientSizeMargin
);
6373 const bool allowContentOverride
=
6374 sizeMode
== nsSizeMode_Maximized
|| clientRect
.Contains(point
);
6376 // The border size. If there is no content under mouse cursor, the border
6377 // size should be larger than the values in system settings. Otherwise,
6378 // contents under the mouse cursor should be able to override the behavior.
6379 // E.g., user must expect that Firefox button always opens the popup menu
6380 // even when the user clicks on the above edge of it.
6381 LayoutDeviceIntMargin borderSize
= nonClientSizeMargin
;
6382 borderSize
.EnsureAtLeast(
6383 LayoutDeviceIntMargin(mVertResizeMargin
, mHorResizeMargin
,
6384 mVertResizeMargin
, mHorResizeMargin
));
6387 bool bottom
= false;
6391 if (point
.y
>= winRect
.y
&& point
.y
< winRect
.y
+ borderSize
.top
) {
6393 } else if (point
.y
<= winRect
.YMost() &&
6394 point
.y
> winRect
.YMost() - borderSize
.bottom
) {
6398 // (the 2x case here doubles the resize area for corners)
6399 int multiplier
= (top
|| bottom
) ? 2 : 1;
6400 if (point
.x
>= winRect
.x
&&
6401 point
.x
< winRect
.x
+ (multiplier
* borderSize
.left
)) {
6403 } else if (point
.x
<= winRect
.XMost() &&
6404 point
.x
> winRect
.XMost() - (multiplier
* borderSize
.right
)) {
6408 bool inResizeRegion
= false;
6413 testResult
= HTTOPLEFT
;
6415 testResult
= HTTOPRIGHT
;
6417 } else if (bottom
) {
6418 testResult
= HTBOTTOM
;
6420 testResult
= HTBOTTOMLEFT
;
6422 testResult
= HTBOTTOMRIGHT
;
6426 testResult
= HTLEFT
;
6429 testResult
= HTRIGHT
;
6432 inResizeRegion
= (testResult
!= HTCLIENT
);
6435 testResult
= HTCAPTION
;
6436 } else if (bottom
|| left
|| right
) {
6437 testResult
= HTBORDER
;
6441 if (!sIsInMouseCapture
&& allowContentOverride
) {
6443 POINT pt
= {aX
, aY
};
6444 ::ScreenToClient(mWnd
, &pt
);
6446 if (pt
.x
== mCachedHitTestPoint
.x
.value
&&
6447 pt
.y
== mCachedHitTestPoint
.y
.value
&&
6448 TimeStamp::Now() - mCachedHitTestTime
<
6449 TimeDuration::FromMilliseconds(HITTEST_CACHE_LIFETIME_MS
)) {
6450 return mCachedHitTestResult
;
6453 mCachedHitTestPoint
= {pt
.x
, pt
.y
};
6454 mCachedHitTestTime
= TimeStamp::Now();
6457 auto pt
= mCachedHitTestPoint
;
6459 if (mWindowBtnRect
[WindowButtonType::Minimize
].Contains(pt
)) {
6460 testResult
= HTMINBUTTON
;
6461 } else if (mWindowBtnRect
[WindowButtonType::Maximize
].Contains(pt
)) {
6462 testResult
= HTMAXBUTTON
;
6463 } else if (mWindowBtnRect
[WindowButtonType::Close
].Contains(pt
)) {
6464 testResult
= HTCLOSE
;
6465 } else if (!inResizeRegion
) {
6466 // If we're in the resize region, avoid overriding that with either a
6467 // drag or a client result; resize takes priority over either (but not
6468 // over the window controls, which is why we check this after those).
6469 if (mDraggableRegion
.Contains(pt
)) {
6470 testResult
= HTCAPTION
;
6472 testResult
= HTCLIENT
;
6476 mCachedHitTestResult
= testResult
;
6482 bool nsWindow::IsSimulatedClientArea(int32_t screenX
, int32_t screenY
) {
6483 int32_t testResult
= ClientMarginHitTestPoint(screenX
, screenY
);
6484 return testResult
== HTCAPTION
|| IsWindowButton(testResult
);
6487 bool nsWindow::IsWindowButton(int32_t hitTestResult
) {
6488 return hitTestResult
== HTMINBUTTON
|| hitTestResult
== HTMAXBUTTON
||
6489 hitTestResult
== HTCLOSE
;
6492 TimeStamp
nsWindow::GetMessageTimeStamp(LONG aEventTime
) const {
6493 CurrentWindowsTimeGetter
getCurrentTime(mWnd
);
6494 return TimeConverter().GetTimeStampFromSystemTime(aEventTime
, getCurrentTime
);
6497 void nsWindow::PostSleepWakeNotification(const bool aIsSleepMode
) {
6498 // Retain the previous mode that was notified to observers
6499 static bool sWasSleepMode
= false;
6501 // Only notify observers if mode changed
6502 if (aIsSleepMode
== sWasSleepMode
) return;
6504 sWasSleepMode
= aIsSleepMode
;
6506 nsCOMPtr
<nsIObserverService
> observerService
=
6507 mozilla::services::GetObserverService();
6508 if (observerService
)
6509 observerService
->NotifyObservers(nullptr,
6511 ? NS_WIDGET_SLEEP_OBSERVER_TOPIC
6512 : NS_WIDGET_WAKE_OBSERVER_TOPIC
,
6516 LRESULT
nsWindow::ProcessCharMessage(const MSG
& aMsg
, bool* aEventDispatched
) {
6517 if (IMEHandler::IsComposingOn(this)) {
6518 IMEHandler::NotifyIME(this, REQUEST_TO_COMMIT_COMPOSITION
);
6520 // These must be checked here too as a lone WM_CHAR could be received
6521 // if a child window didn't handle it (for example Alt+Space in a content
6523 ModifierKeyState modKeyState
;
6524 NativeKey
nativeKey(this, aMsg
, modKeyState
);
6525 return static_cast<LRESULT
>(nativeKey
.HandleCharMessage(aEventDispatched
));
6528 LRESULT
nsWindow::ProcessKeyUpMessage(const MSG
& aMsg
, bool* aEventDispatched
) {
6529 ModifierKeyState modKeyState
;
6530 NativeKey
nativeKey(this, aMsg
, modKeyState
);
6531 bool result
= nativeKey
.HandleKeyUpMessage(aEventDispatched
);
6532 if (aMsg
.wParam
== VK_F10
) {
6533 // Bug 1382199: Windows default behavior will trigger the System menu bar
6534 // when F10 is released. Among other things, this causes the System menu bar
6535 // to appear when a web page overrides the contextmenu event. We *never*
6536 // want this default behavior, so eat this key (never pass it to Windows).
6542 LRESULT
nsWindow::ProcessKeyDownMessage(const MSG
& aMsg
,
6543 bool* aEventDispatched
) {
6544 // If this method doesn't call NativeKey::HandleKeyDownMessage(), this method
6545 // must clean up the redirected message information itself. For more
6546 // information, see above comment of
6547 // RedirectedKeyDownMessageManager::AutoFlusher class definition in
6548 // KeyboardLayout.h.
6549 RedirectedKeyDownMessageManager::AutoFlusher
redirectedMsgFlusher(this, aMsg
);
6551 ModifierKeyState modKeyState
;
6553 NativeKey
nativeKey(this, aMsg
, modKeyState
);
6555 static_cast<LRESULT
>(nativeKey
.HandleKeyDownMessage(aEventDispatched
));
6556 // HandleKeyDownMessage cleaned up the redirected message information
6557 // itself, so, we should do nothing.
6558 redirectedMsgFlusher
.Cancel();
6560 if (aMsg
.wParam
== VK_MENU
||
6561 (aMsg
.wParam
== VK_F10
&& !modKeyState
.IsShift())) {
6562 // We need to let Windows handle this keypress,
6563 // by returning false, if there's a native menu
6564 // bar somewhere in our containing window hierarchy.
6565 // Otherwise we handle the keypress and don't pass
6566 // it on to Windows, by returning true.
6567 bool hasNativeMenu
= false;
6570 if (::GetMenu(hWnd
)) {
6571 hasNativeMenu
= true;
6574 hWnd
= ::GetParent(hWnd
);
6576 result
= !hasNativeMenu
;
6582 nsresult
nsWindow::SynthesizeNativeKeyEvent(
6583 int32_t aNativeKeyboardLayout
, int32_t aNativeKeyCode
,
6584 uint32_t aModifierFlags
, const nsAString
& aCharacters
,
6585 const nsAString
& aUnmodifiedCharacters
, nsIObserver
* aObserver
) {
6586 AutoObserverNotifier
notifier(aObserver
, "keyevent");
6588 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
6589 return keyboardLayout
->SynthesizeNativeKeyEvent(
6590 this, aNativeKeyboardLayout
, aNativeKeyCode
, aModifierFlags
, aCharacters
,
6591 aUnmodifiedCharacters
);
6594 nsresult
nsWindow::SynthesizeNativeMouseEvent(
6595 LayoutDeviceIntPoint aPoint
, NativeMouseMessage aNativeMessage
,
6596 MouseButton aButton
, nsIWidget::Modifiers aModifierFlags
,
6597 nsIObserver
* aObserver
) {
6598 AutoObserverNotifier
notifier(aObserver
, "mouseevent");
6601 memset(&input
, 0, sizeof(input
));
6603 // TODO (bug 1693240):
6604 // Now, we synthesize native mouse events asynchronously since we want to
6605 // synthesize the event on the front window at the point. However, Windows
6606 // does not provide a way to set modifier only while a mouse message is
6607 // being handled, and MOUSEEVENTF_MOVE may be coalesced by Windows. So, we
6608 // need a trick for handling it.
6610 switch (aNativeMessage
) {
6611 case NativeMouseMessage::Move
:
6612 input
.mi
.dwFlags
= MOUSEEVENTF_MOVE
;
6613 // Reset sLastMouseMovePoint so that even if we're moving the mouse
6614 // to the position it's already at, we still dispatch a mousemove
6615 // event, because the callers of this function expect that.
6616 sLastMouseMovePoint
= {0};
6618 case NativeMouseMessage::ButtonDown
:
6619 case NativeMouseMessage::ButtonUp
: {
6620 const bool isDown
= aNativeMessage
== NativeMouseMessage::ButtonDown
;
6622 case MouseButton::ePrimary
:
6623 input
.mi
.dwFlags
= isDown
? MOUSEEVENTF_LEFTDOWN
: MOUSEEVENTF_LEFTUP
;
6625 case MouseButton::eMiddle
:
6627 isDown
? MOUSEEVENTF_MIDDLEDOWN
: MOUSEEVENTF_MIDDLEUP
;
6629 case MouseButton::eSecondary
:
6631 isDown
? MOUSEEVENTF_RIGHTDOWN
: MOUSEEVENTF_RIGHTUP
;
6633 case MouseButton::eX1
:
6634 input
.mi
.dwFlags
= isDown
? MOUSEEVENTF_XDOWN
: MOUSEEVENTF_XUP
;
6635 input
.mi
.mouseData
= XBUTTON1
;
6637 case MouseButton::eX2
:
6638 input
.mi
.dwFlags
= isDown
? MOUSEEVENTF_XDOWN
: MOUSEEVENTF_XUP
;
6639 input
.mi
.mouseData
= XBUTTON2
;
6642 return NS_ERROR_INVALID_ARG
;
6646 case NativeMouseMessage::EnterWindow
:
6647 case NativeMouseMessage::LeaveWindow
:
6648 MOZ_ASSERT_UNREACHABLE("Non supported mouse event on Windows");
6649 return NS_ERROR_INVALID_ARG
;
6652 input
.type
= INPUT_MOUSE
;
6653 ::SetCursorPos(aPoint
.x
, aPoint
.y
);
6654 ::SendInput(1, &input
, sizeof(INPUT
));
6659 nsresult
nsWindow::SynthesizeNativeMouseScrollEvent(
6660 LayoutDeviceIntPoint aPoint
, uint32_t aNativeMessage
, double aDeltaX
,
6661 double aDeltaY
, double aDeltaZ
, uint32_t aModifierFlags
,
6662 uint32_t aAdditionalFlags
, nsIObserver
* aObserver
) {
6663 AutoObserverNotifier
notifier(aObserver
, "mousescrollevent");
6664 return MouseScrollHandler::SynthesizeNativeMouseScrollEvent(
6665 this, aPoint
, aNativeMessage
,
6666 (aNativeMessage
== WM_MOUSEWHEEL
|| aNativeMessage
== WM_VSCROLL
)
6667 ? static_cast<int32_t>(aDeltaY
)
6668 : static_cast<int32_t>(aDeltaX
),
6669 aModifierFlags
, aAdditionalFlags
);
6672 nsresult
nsWindow::SynthesizeNativeTouchpadPan(TouchpadGesturePhase aEventPhase
,
6673 LayoutDeviceIntPoint aPoint
,
6674 double aDeltaX
, double aDeltaY
,
6675 int32_t aModifierFlags
,
6676 nsIObserver
* aObserver
) {
6677 AutoObserverNotifier
notifier(aObserver
, "touchpadpanevent");
6678 DirectManipulationOwner::SynthesizeNativeTouchpadPan(
6679 this, aEventPhase
, aPoint
, aDeltaX
, aDeltaY
, aModifierFlags
);
6683 static void MaybeLogPosChanged(HWND aWnd
, WINDOWPOS
* wp
) {
6684 #ifdef WINSTATE_DEBUG_OUTPUT
6685 if (aWnd
== WinUtils::GetTopLevelHWND(aWnd
)) {
6686 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("*** OnWindowPosChanged: [ top] "));
6688 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("*** OnWindowPosChanged: [child] "));
6690 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("WINDOWPOS flags:"));
6691 if (wp
->flags
& SWP_FRAMECHANGED
) {
6692 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_FRAMECHANGED "));
6694 if (wp
->flags
& SWP_SHOWWINDOW
) {
6695 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_SHOWWINDOW "));
6697 if (wp
->flags
& SWP_NOSIZE
) {
6698 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_NOSIZE "));
6700 if (wp
->flags
& SWP_HIDEWINDOW
) {
6701 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_HIDEWINDOW "));
6703 if (wp
->flags
& SWP_NOZORDER
) {
6704 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_NOZORDER "));
6706 if (wp
->flags
& SWP_NOACTIVATE
) {
6707 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_NOACTIVATE "));
6709 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("\n"));
6713 /**************************************************************
6715 * SECTION: OnXXX message handlers
6717 * For message handlers that need to be broken out or
6718 * implemented in specific platform code.
6720 **************************************************************/
6722 void nsWindow::OnWindowPosChanged(WINDOWPOS
* wp
) {
6727 MaybeLogPosChanged(mWnd
, wp
);
6729 // Handle window size mode changes
6730 if (wp
->flags
& SWP_FRAMECHANGED
) {
6731 // Bug 566135 - Windows theme code calls show window on SW_SHOWMINIMIZED
6732 // windows when fullscreen games disable desktop composition. If we're
6733 // minimized and not being activated, ignore the event and let windows
6735 if (mFrameState
->GetSizeMode() == nsSizeMode_Minimized
&&
6736 (wp
->flags
& SWP_NOACTIVATE
)) {
6740 mFrameState
->OnFrameChanged();
6742 if (mFrameState
->GetSizeMode() == nsSizeMode_Minimized
) {
6743 // Skip window size change events below on minimization.
6748 // Notify visibility change when window is activated.
6749 if (!(wp
->flags
& SWP_NOACTIVATE
) && NeedsToTrackWindowOcclusionState()) {
6750 WinWindowOcclusionTracker::Get()->OnWindowVisibilityChanged(
6751 this, mFrameState
->GetSizeMode() != nsSizeMode_Minimized
);
6754 // Handle window position changes
6755 if (!(wp
->flags
& SWP_NOMOVE
)) {
6756 mBounds
.MoveTo(wp
->x
, wp
->y
);
6757 NotifyWindowMoved(wp
->x
, wp
->y
);
6760 // Handle window size changes
6761 if (!(wp
->flags
& SWP_NOSIZE
)) {
6763 int32_t newWidth
, newHeight
;
6765 ::GetWindowRect(mWnd
, &r
);
6767 newWidth
= r
.right
- r
.left
;
6768 newHeight
= r
.bottom
- r
.top
;
6770 if (newWidth
> mLastSize
.width
) {
6774 drect
.left
= wp
->x
+ mLastSize
.width
;
6776 drect
.right
= drect
.left
+ (newWidth
- mLastSize
.width
);
6777 drect
.bottom
= drect
.top
+ newHeight
;
6779 ::RedrawWindow(mWnd
, &drect
, nullptr,
6780 RDW_INVALIDATE
| RDW_NOERASE
| RDW_NOINTERNALPAINT
|
6781 RDW_ERASENOW
| RDW_ALLCHILDREN
);
6783 if (newHeight
> mLastSize
.height
) {
6788 drect
.top
= wp
->y
+ mLastSize
.height
;
6789 drect
.right
= drect
.left
+ newWidth
;
6790 drect
.bottom
= drect
.top
+ (newHeight
- mLastSize
.height
);
6792 ::RedrawWindow(mWnd
, &drect
, nullptr,
6793 RDW_INVALIDATE
| RDW_NOERASE
| RDW_NOINTERNALPAINT
|
6794 RDW_ERASENOW
| RDW_ALLCHILDREN
);
6797 mBounds
.SizeTo(newWidth
, newHeight
);
6798 mLastSize
.width
= newWidth
;
6799 mLastSize
.height
= newHeight
;
6801 #ifdef WINSTATE_DEBUG_OUTPUT
6802 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
6803 ("*** Resize window: %d x %d x %d x %d\n", wp
->x
, wp
->y
, newWidth
,
6807 if (mAspectRatio
> 0) {
6808 // It's possible (via Windows Aero Snap) that the size of the window
6809 // has changed such that it violates the aspect ratio constraint. If so,
6810 // queue up an event to enforce the aspect ratio constraint and repaint.
6811 // When resized with Windows Aero Snap, we are in the NOT_RESIZING state.
6812 float newAspectRatio
= (float)newWidth
/ newHeight
;
6813 if (mResizeState
== NOT_RESIZING
&& mAspectRatio
!= newAspectRatio
) {
6814 // Hold a reference to self alive and pass it into the lambda to make
6815 // sure this nsIWidget stays alive long enough to run this function.
6816 nsCOMPtr
<nsIWidget
> self(this);
6817 NS_DispatchToMainThread(NS_NewRunnableFunction(
6818 "EnforceAspectRatio", [self
, this, newWidth
]() -> void {
6820 Resize(newWidth
, newWidth
/ mAspectRatio
, true);
6826 // If a maximized window is resized, recalculate the non-client margins.
6827 if (mFrameState
->GetSizeMode() == nsSizeMode_Maximized
) {
6828 if (UpdateNonClientMargins(true)) {
6829 // gecko resize event already sent by UpdateNonClientMargins.
6835 // Notify the widget listener for size change of client area for gecko
6836 // events. This needs to be done when either window size is changed,
6837 // or window frame is changed. They may not happen together.
6838 // However, we don't invoke that for popup when window frame changes,
6839 // because popups may trigger frame change before size change via
6840 // {Set,Clear}ThemeRegion they invoke in Resize. That would make the
6841 // code below call OnResize with a wrong client size first, which can
6842 // lead to flickerling for some popups.
6843 if (!(wp
->flags
& SWP_NOSIZE
) ||
6844 ((wp
->flags
& SWP_FRAMECHANGED
) && !IsPopup())) {
6846 LayoutDeviceIntSize clientSize
;
6847 if (::GetClientRect(mWnd
, &r
)) {
6848 clientSize
= WinUtils::ToIntRect(r
).Size();
6850 clientSize
= mBounds
.Size();
6852 // Send a gecko resize event
6853 OnResize(clientSize
);
6857 void nsWindow::OnWindowPosChanging(WINDOWPOS
* info
) {
6858 // Update non-client margins if the frame size is changing, and let the
6859 // browser know we are changing size modes, so alternative css can kick in.
6860 // If we're going into fullscreen mode, ignore this, since it'll reset
6861 // margins to normal mode.
6862 if (info
->flags
& SWP_FRAMECHANGED
&& !(info
->flags
& SWP_NOSIZE
)) {
6863 mFrameState
->OnFrameChanging();
6866 // Force fullscreen. This works around a bug in Windows 10 1809 where
6867 // using fullscreen when a window is "snapped" causes a spurious resize
6868 // smaller than the full screen, see bug 1482920.
6869 if (mFrameState
->GetSizeMode() == nsSizeMode_Fullscreen
&&
6870 !(info
->flags
& SWP_NOMOVE
) && !(info
->flags
& SWP_NOSIZE
)) {
6871 nsCOMPtr
<nsIScreenManager
> screenmgr
=
6872 do_GetService(sScreenManagerContractID
);
6874 LayoutDeviceIntRect
bounds(info
->x
, info
->y
, info
->cx
, info
->cy
);
6875 DesktopIntRect deskBounds
=
6876 RoundedToInt(bounds
/ GetDesktopToDeviceScale());
6877 nsCOMPtr
<nsIScreen
> screen
;
6878 screenmgr
->ScreenForRect(deskBounds
.X(), deskBounds
.Y(),
6879 deskBounds
.Width(), deskBounds
.Height(),
6880 getter_AddRefs(screen
));
6883 auto rect
= screen
->GetRect();
6886 info
->cx
= rect
.width
;
6887 info
->cy
= rect
.height
;
6892 // enforce local z-order rules
6893 if (!(info
->flags
& SWP_NOZORDER
)) {
6894 HWND hwndAfter
= info
->hwndInsertAfter
;
6896 nsWindow
* aboveWindow
= 0;
6897 nsWindowZ placement
;
6899 if (hwndAfter
== HWND_BOTTOM
)
6900 placement
= nsWindowZBottom
;
6901 else if (hwndAfter
== HWND_TOP
|| hwndAfter
== HWND_TOPMOST
||
6902 hwndAfter
== HWND_NOTOPMOST
)
6903 placement
= nsWindowZTop
;
6905 placement
= nsWindowZRelative
;
6906 aboveWindow
= WinUtils::GetNSWindowPtr(hwndAfter
);
6909 if (mWidgetListener
) {
6910 nsCOMPtr
<nsIWidget
> actualBelow
= nullptr;
6911 if (mWidgetListener
->ZLevelChanged(false, &placement
, aboveWindow
,
6912 getter_AddRefs(actualBelow
))) {
6913 if (placement
== nsWindowZBottom
)
6914 info
->hwndInsertAfter
= HWND_BOTTOM
;
6915 else if (placement
== nsWindowZTop
)
6916 info
->hwndInsertAfter
= HWND_TOP
;
6918 info
->hwndInsertAfter
=
6919 (HWND
)actualBelow
->GetNativeData(NS_NATIVE_WINDOW
);
6924 // prevent rude external programs from making hidden window visible
6925 if (mWindowType
== WindowType::Invisible
) info
->flags
&= ~SWP_SHOWWINDOW
;
6927 // When waking from sleep or switching out of tablet mode, Windows 10
6928 // Version 1809 will reopen popup windows that should be hidden. Detect
6929 // this case and refuse to show the window.
6930 static bool sDWMUnhidesPopups
= IsWin10Sep2018UpdateOrLater();
6931 if (sDWMUnhidesPopups
&& (info
->flags
& SWP_SHOWWINDOW
) &&
6932 mWindowType
== WindowType::Popup
&& mWidgetListener
&&
6933 mWidgetListener
->ShouldNotBeVisible()) {
6934 info
->flags
&= ~SWP_SHOWWINDOW
;
6938 void nsWindow::UserActivity() {
6939 // Check if we have the idle service, if not we try to get it.
6940 if (!mIdleService
) {
6941 mIdleService
= do_GetService("@mozilla.org/widget/useridleservice;1");
6944 // Check that we now have the idle service.
6946 mIdleService
->ResetIdleTimeOut(0);
6950 // Helper function for TouchDeviceNeedsPanGestureConversion(PTOUCHINPUT,
6952 static bool TouchDeviceNeedsPanGestureConversion(HANDLE aSource
) {
6953 std::string deviceName
;
6955 // The first call just queries how long the name string will be.
6956 GetRawInputDeviceInfoA(aSource
, RIDI_DEVICENAME
, nullptr, &dataSize
);
6957 if (!dataSize
|| dataSize
> 0x10000) {
6960 deviceName
.resize(dataSize
);
6961 // The second call actually populates the string.
6962 UINT result
= GetRawInputDeviceInfoA(aSource
, RIDI_DEVICENAME
, &deviceName
[0],
6964 if (result
== UINT_MAX
) {
6967 // The affected device name is "\\?\VIRTUAL_DIGITIZER", but each backslash
6968 // needs to be escaped with another one.
6969 std::string expectedDeviceName
= "\\\\?\\VIRTUAL_DIGITIZER";
6970 // For some reason, the dataSize returned by the first call is double the
6971 // actual length of the device name (as if it were returning the size of a
6972 // wide-character string in bytes) even though we are using the narrow
6973 // version of the API. For the comparison against the expected device name
6974 // to pass, we truncate the buffer to be no longer tha the expected device
6976 if (deviceName
.substr(0, expectedDeviceName
.length()) != expectedDeviceName
) {
6980 RID_DEVICE_INFO deviceInfo
;
6981 deviceInfo
.cbSize
= sizeof(deviceInfo
);
6982 dataSize
= sizeof(deviceInfo
);
6984 GetRawInputDeviceInfoA(aSource
, RIDI_DEVICEINFO
, &deviceInfo
, &dataSize
);
6985 if (result
== UINT_MAX
) {
6988 // The device identifiers that we check for here come from bug 1355162
6989 // comment 1 (see also bug 1511901 comment 35).
6990 return deviceInfo
.dwType
== RIM_TYPEHID
&& deviceInfo
.hid
.dwVendorId
== 0 &&
6991 deviceInfo
.hid
.dwProductId
== 0 &&
6992 deviceInfo
.hid
.dwVersionNumber
== 1 &&
6993 deviceInfo
.hid
.usUsagePage
== 13 && deviceInfo
.hid
.usUsage
== 4;
6996 // Determine if the touch device that originated |aOSEvent| needs to have
6997 // touch events representing a two-finger gesture converted to pan
6999 // We only do this for touch devices with a specific name and identifiers.
7000 static bool TouchDeviceNeedsPanGestureConversion(PTOUCHINPUT aOSEvent
,
7001 uint32_t aTouchCount
) {
7002 if (!StaticPrefs::apz_windows_check_for_pan_gesture_conversion()) {
7005 if (aTouchCount
== 0) {
7008 HANDLE source
= aOSEvent
[0].hSource
;
7010 // Cache the result of this computation for each touch device.
7011 // Touch devices are identified by the HANDLE stored in the hSource
7012 // field of TOUCHINPUT.
7013 static std::map
<HANDLE
, bool> sResultCache
;
7014 auto [iter
, inserted
] = sResultCache
.emplace(source
, false);
7016 iter
->second
= TouchDeviceNeedsPanGestureConversion(source
);
7018 return iter
->second
;
7021 Maybe
<PanGestureInput
> nsWindow::ConvertTouchToPanGesture(
7022 const MultiTouchInput
& aTouchInput
, PTOUCHINPUT aOSEvent
) {
7023 // Checks if the touch device that originated the touch event is one
7024 // for which we want to convert the touch events to pang gesture events.
7025 bool shouldConvert
= TouchDeviceNeedsPanGestureConversion(
7026 aOSEvent
, aTouchInput
.mTouches
.Length());
7027 if (!shouldConvert
) {
7031 // Only two-finger gestures need conversion.
7032 if (aTouchInput
.mTouches
.Length() != 2) {
7036 PanGestureInput::PanGestureType eventType
= PanGestureInput::PANGESTURE_PAN
;
7037 if (aTouchInput
.mType
== MultiTouchInput::MULTITOUCH_START
) {
7038 eventType
= PanGestureInput::PANGESTURE_START
;
7039 } else if (aTouchInput
.mType
== MultiTouchInput::MULTITOUCH_END
) {
7040 eventType
= PanGestureInput::PANGESTURE_END
;
7041 } else if (aTouchInput
.mType
== MultiTouchInput::MULTITOUCH_CANCEL
) {
7042 eventType
= PanGestureInput::PANGESTURE_CANCELLED
;
7045 // Use the midpoint of the two touches as the start point of the pan gesture.
7046 ScreenPoint focusPoint
= (aTouchInput
.mTouches
[0].mScreenPoint
+
7047 aTouchInput
.mTouches
[1].mScreenPoint
) /
7049 // To compute the displacement of the pan gesture, we keep track of the
7050 // location of the previous event.
7051 ScreenPoint displacement
= (eventType
== PanGestureInput::PANGESTURE_START
)
7053 : (focusPoint
- mLastPanGestureFocus
);
7054 mLastPanGestureFocus
= focusPoint
;
7056 // We need to negate the displacement because for a touch event, moving the
7057 // fingers down results in scrolling up, but for a touchpad gesture, we want
7058 // moving the fingers down to result in scrolling down.
7059 PanGestureInput
result(eventType
, aTouchInput
.mTimeStamp
, focusPoint
,
7060 -displacement
, aTouchInput
.modifiers
);
7061 result
.mSimulateMomentum
= true;
7063 return Some(result
);
7066 // Dispatch an event that originated as an OS touch event.
7067 // Usually, we want to dispatch it as a touch event, but some touchpads
7068 // produce touch events for two-finger scrolling, which need to be converted
7069 // to pan gesture events for correct behaviour.
7070 void nsWindow::DispatchTouchOrPanGestureInput(MultiTouchInput
& aTouchInput
,
7071 PTOUCHINPUT aOSEvent
) {
7072 if (Maybe
<PanGestureInput
> panInput
=
7073 ConvertTouchToPanGesture(aTouchInput
, aOSEvent
)) {
7074 DispatchPanGestureInput(*panInput
);
7078 DispatchTouchInput(aTouchInput
);
7081 bool nsWindow::OnTouch(WPARAM wParam
, LPARAM lParam
) {
7082 uint32_t cInputs
= LOWORD(wParam
);
7083 PTOUCHINPUT pInputs
= new TOUCHINPUT
[cInputs
];
7085 if (GetTouchInputInfo((HTOUCHINPUT
)lParam
, cInputs
, pInputs
,
7086 sizeof(TOUCHINPUT
))) {
7087 MultiTouchInput touchInput
, touchEndInput
;
7089 // Walk across the touch point array processing each contact point.
7090 for (uint32_t i
= 0; i
< cInputs
; i
++) {
7091 bool addToEvent
= false, addToEndEvent
= false;
7093 // N.B.: According with MS documentation
7094 // https://msdn.microsoft.com/en-us/library/windows/desktop/dd317334(v=vs.85).aspx
7095 // TOUCHEVENTF_DOWN cannot be combined with TOUCHEVENTF_MOVE or
7096 // TOUCHEVENTF_UP. Possibly, it means that TOUCHEVENTF_MOVE and
7097 // TOUCHEVENTF_UP can be combined together.
7099 if (pInputs
[i
].dwFlags
& (TOUCHEVENTF_DOWN
| TOUCHEVENTF_MOVE
)) {
7100 if (touchInput
.mTimeStamp
.IsNull()) {
7101 // Initialize a touch event to send.
7102 touchInput
.mType
= MultiTouchInput::MULTITOUCH_MOVE
;
7103 touchInput
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
7104 ModifierKeyState modifierKeyState
;
7105 touchInput
.modifiers
= modifierKeyState
.GetModifiers();
7107 // Pres shell expects this event to be a eTouchStart
7108 // if any new contact points have been added since the last event sent.
7109 if (pInputs
[i
].dwFlags
& TOUCHEVENTF_DOWN
) {
7110 touchInput
.mType
= MultiTouchInput::MULTITOUCH_START
;
7114 if (pInputs
[i
].dwFlags
& TOUCHEVENTF_UP
) {
7115 // Pres shell expects removed contacts points to be delivered in a
7116 // separate eTouchEnd event containing only the contact points that were
7118 if (touchEndInput
.mTimeStamp
.IsNull()) {
7119 // Initialize a touch event to send.
7120 touchEndInput
.mType
= MultiTouchInput::MULTITOUCH_END
;
7121 touchEndInput
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
7122 ModifierKeyState modifierKeyState
;
7123 touchEndInput
.modifiers
= modifierKeyState
.GetModifiers();
7125 addToEndEvent
= true;
7127 if (!addToEvent
&& !addToEndEvent
) {
7128 // Filter out spurious Windows events we don't understand, like palm
7133 // Setup the touch point we'll append to the touch event array.
7134 nsPointWin touchPoint
;
7135 touchPoint
.x
= TOUCH_COORD_TO_PIXEL(pInputs
[i
].x
);
7136 touchPoint
.y
= TOUCH_COORD_TO_PIXEL(pInputs
[i
].y
);
7137 touchPoint
.ScreenToClient(mWnd
);
7139 // Initialize the touch data.
7140 SingleTouchData
touchData(
7141 pInputs
[i
].dwID
, // aIdentifier
7142 ScreenIntPoint::FromUnknownPoint(touchPoint
), // aScreenPoint
7143 // The contact area info cannot be trusted even when
7144 // TOUCHINPUTMASKF_CONTACTAREA is set when the input source is pen,
7145 // which somehow violates the API docs. (bug 1710509) Ultimately the
7146 // dwFlags check will become redundant since we want to migrate to
7147 // WM_POINTER for pens. (bug 1707075)
7148 (pInputs
[i
].dwMask
& TOUCHINPUTMASKF_CONTACTAREA
) &&
7149 !(pInputs
[i
].dwFlags
& TOUCHEVENTF_PEN
)
7150 ? ScreenSize(TOUCH_COORD_TO_PIXEL(pInputs
[i
].cxContact
) / 2,
7151 TOUCH_COORD_TO_PIXEL(pInputs
[i
].cyContact
) / 2)
7152 : ScreenSize(1, 1), // aRadius
7153 0.0f
, // aRotationAngle
7156 // Append touch data to the appropriate event.
7158 touchInput
.mTouches
.AppendElement(touchData
);
7160 if (addToEndEvent
) {
7161 touchEndInput
.mTouches
.AppendElement(touchData
);
7165 // Dispatch touch start and touch move event if we have one.
7166 if (!touchInput
.mTimeStamp
.IsNull()) {
7167 DispatchTouchOrPanGestureInput(touchInput
, pInputs
);
7169 // Dispatch touch end event if we have one.
7170 if (!touchEndInput
.mTimeStamp
.IsNull()) {
7171 DispatchTouchOrPanGestureInput(touchEndInput
, pInputs
);
7176 CloseTouchInputHandle((HTOUCHINPUT
)lParam
);
7180 // Gesture event processing. Handles WM_GESTURE events.
7181 bool nsWindow::OnGesture(WPARAM wParam
, LPARAM lParam
) {
7182 // Treatment for pan events which translate into scroll events:
7183 if (mGesture
.IsPanEvent(lParam
)) {
7184 if (!mGesture
.ProcessPanMessage(mWnd
, wParam
, lParam
))
7185 return false; // ignore
7187 nsEventStatus status
;
7189 WidgetWheelEvent
wheelEvent(true, eWheel
, this);
7191 ModifierKeyState modifierKeyState
;
7192 modifierKeyState
.InitInputEvent(wheelEvent
);
7194 wheelEvent
.mButton
= 0;
7195 wheelEvent
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
7196 wheelEvent
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
7198 bool endFeedback
= true;
7200 if (mGesture
.PanDeltaToPixelScroll(wheelEvent
)) {
7201 DispatchEvent(&wheelEvent
, status
);
7204 if (mDisplayPanFeedback
) {
7205 mGesture
.UpdatePanFeedbackX(
7206 mWnd
, DeprecatedAbs(RoundDown(wheelEvent
.mOverflowDeltaX
)),
7208 mGesture
.UpdatePanFeedbackY(
7209 mWnd
, DeprecatedAbs(RoundDown(wheelEvent
.mOverflowDeltaY
)),
7211 mGesture
.PanFeedbackFinalize(mWnd
, endFeedback
);
7214 CloseGestureInfoHandle((HGESTUREINFO
)lParam
);
7219 // Other gestures translate into simple gesture events:
7220 WidgetSimpleGestureEvent
event(true, eVoidEvent
, this);
7221 if (!mGesture
.ProcessGestureMessage(mWnd
, wParam
, lParam
, event
)) {
7222 return false; // fall through to DefWndProc
7225 // Polish up and send off the new event
7226 ModifierKeyState modifierKeyState
;
7227 modifierKeyState
.InitInputEvent(event
);
7229 event
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
7230 event
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
7232 nsEventStatus status
;
7233 DispatchEvent(&event
, status
);
7234 if (status
== nsEventStatus_eIgnore
) {
7235 return false; // Ignored, fall through
7238 // Only close this if we process and return true.
7239 CloseGestureInfoHandle((HGESTUREINFO
)lParam
);
7241 return true; // Handled
7244 // WM_DESTROY event handler
7245 void nsWindow::OnDestroy() {
7246 mOnDestroyCalled
= true;
7248 // If this is a toplevel window, notify the taskbar concealer to clean up any
7251 TaskbarConcealer::OnWindowDestroyed(mWnd
);
7254 // Make sure we don't get destroyed in the process of tearing down.
7255 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
7257 // Dispatch the destroy notification.
7258 if (!mInDtor
) NotifyWindowDestroyed();
7260 // Prevent the widget from sending additional events.
7261 mWidgetListener
= nullptr;
7262 mAttachedWidgetListener
= nullptr;
7264 DestroyDirectManipulation();
7266 if (mWnd
== mLastKillFocusWindow
) {
7267 mLastKillFocusWindow
= nullptr;
7269 // Unregister notifications from terminal services
7270 ::WTSUnRegisterSessionNotification(mWnd
);
7272 // We will stop receiving native events after dissociating from our native
7273 // window. We will also disappear from the output of WinUtils::GetNSWindowPtr
7275 DissociateFromNativeWindow();
7277 // Once mWidgetListener is cleared and the subclass is reset, sCurrentWindow
7278 // can be cleared. (It's used in tracking windows for mouse events.)
7279 if (sCurrentWindow
== this) sCurrentWindow
= nullptr;
7281 // Disconnects us from our parent, will call our GetParent().
7282 nsBaseWidget::Destroy();
7284 // Release references to children, device context, toolkit, and app shell.
7285 nsBaseWidget::OnDestroy();
7287 // Clear our native parent handle.
7288 // XXX Windows will take care of this in the proper order, and
7289 // SetParent(nullptr)'s remove child on the parent already took place in
7290 // nsBaseWidget's Destroy call above.
7291 // SetParent(nullptr);
7294 // We have to destroy the native drag target before we null out our window
7296 EnableDragDrop(false);
7298 // If we're going away and for some reason we're still the rollup widget,
7299 // rollup and turn off capture.
7300 nsIRollupListener
* rollupListener
= nsBaseWidget::GetActiveRollupListener();
7301 nsCOMPtr
<nsIWidget
> rollupWidget
;
7302 if (rollupListener
) {
7303 rollupWidget
= rollupListener
->GetRollupWidget();
7305 if (this == rollupWidget
) {
7306 rollupListener
->Rollup({});
7307 CaptureRollupEvents(false);
7310 IMEHandler::OnDestroyWindow(this);
7312 // Free GDI window class objects
7314 VERIFY(::DeleteObject(mBrush
));
7318 // Destroy any custom cursor resources.
7319 if (mCursor
.IsCustom()) {
7320 SetCursor(Cursor
{eCursor_standard
});
7323 if (mCompositorWidgetDelegate
) {
7324 mCompositorWidgetDelegate
->OnDestroyWindow();
7326 mBasicLayersSurface
= nullptr;
7328 // Finalize panning feedback to possibly restore window displacement
7329 mGesture
.PanFeedbackFinalize(mWnd
, true);
7331 // Clear the main HWND.
7335 // Send a resize message to the listener
7336 bool nsWindow::OnResize(const LayoutDeviceIntSize
& aSize
) {
7337 if (mCompositorWidgetDelegate
&&
7338 !mCompositorWidgetDelegate
->OnWindowResize(aSize
)) {
7342 bool result
= false;
7343 if (mWidgetListener
) {
7344 result
= mWidgetListener
->WindowResized(this, aSize
.width
, aSize
.height
);
7347 // If there is an attached view, inform it as well as the normal widget
7349 if (mAttachedWidgetListener
) {
7350 return mAttachedWidgetListener
->WindowResized(this, aSize
.width
,
7357 void nsWindow::OnSizeModeChange() {
7358 const nsSizeMode mode
= mFrameState
->GetSizeMode();
7360 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7361 ("nsWindow::OnSizeModeChange() sizeMode %d", mode
));
7363 if (NeedsToTrackWindowOcclusionState()) {
7364 WinWindowOcclusionTracker::Get()->OnWindowVisibilityChanged(
7365 this, mode
!= nsSizeMode_Minimized
);
7367 wr::DebugFlags flags
{0};
7368 flags
.bits
= gfx::gfxVars::WebRenderDebugFlags();
7369 bool debugEnabled
= bool(flags
& wr::DebugFlags::WINDOW_VISIBILITY_DBG
);
7370 if (debugEnabled
&& mCompositorWidgetDelegate
) {
7371 mCompositorWidgetDelegate
->NotifyVisibilityUpdated(mode
,
7376 if (mCompositorWidgetDelegate
) {
7377 mCompositorWidgetDelegate
->OnWindowModeChange(mode
);
7380 if (mWidgetListener
) {
7381 mWidgetListener
->SizeModeChanged(mode
);
7385 bool nsWindow::OnHotKey(WPARAM wParam
, LPARAM lParam
) { return true; }
7387 bool nsWindow::IsPopup() { return mWindowType
== WindowType::Popup
; }
7389 bool nsWindow::ShouldUseOffMainThreadCompositing() {
7390 if (mWindowType
== WindowType::Popup
&& mPopupType
== PopupType::Tooltip
) {
7394 // Content rendering of popup is always done by child window.
7395 // See nsDocumentViewer::ShouldAttachToTopLevel().
7396 if (mWindowType
== WindowType::Popup
&& !mIsChildWindow
) {
7397 MOZ_ASSERT(!mParent
);
7401 return nsBaseWidget::ShouldUseOffMainThreadCompositing();
7404 void nsWindow::WindowUsesOMTC() {
7405 ULONG_PTR style
= ::GetClassLongPtr(mWnd
, GCL_STYLE
);
7407 NS_WARNING("Could not get window class style");
7410 style
|= CS_HREDRAW
| CS_VREDRAW
;
7411 DebugOnly
<ULONG_PTR
> result
= ::SetClassLongPtr(mWnd
, GCL_STYLE
, style
);
7412 NS_WARNING_ASSERTION(result
, "Could not reset window class style");
7416 bool nsWindow::HasBogusPopupsDropShadowOnMultiMonitor() {
7417 static const bool sHasBogusPopupsDropShadowOnMultiMonitor
= [] {
7418 // Since any change in the preferences requires a restart, this can be
7420 // Check for Direct2D first.
7421 if (gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend()) {
7424 // Otherwise check if Direct3D 9 may be used.
7425 if (gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING
) &&
7426 !gfxConfig::IsEnabled(gfx::Feature::OPENGL_COMPOSITING
)) {
7427 nsCOMPtr
<nsIGfxInfo
> gfxInfo
= components::GfxInfo::Service();
7430 nsCString discardFailureId
;
7432 gfxInfo
->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS
,
7433 discardFailureId
, &status
))) {
7434 if (status
== nsIGfxInfo::FEATURE_STATUS_OK
||
7435 gfxConfig::IsForcedOnByUser(gfx::Feature::HW_COMPOSITING
)) {
7443 return sHasBogusPopupsDropShadowOnMultiMonitor
;
7446 void nsWindow::OnDPIChanged(int32_t x
, int32_t y
, int32_t width
,
7448 // Don't try to handle WM_DPICHANGED for popup windows (see bug 1239353);
7449 // they remain tied to their original parent's resolution.
7450 if (mWindowType
== WindowType::Popup
) {
7453 if (StaticPrefs::layout_css_devPixelsPerPx() > 0.0) {
7456 mDefaultScale
= -1.0; // force recomputation of scale factor
7458 if (mResizeState
!= RESIZING
&&
7459 mFrameState
->GetSizeMode() == nsSizeMode_Normal
) {
7460 // Limit the position (if not in the middle of a drag-move) & size,
7461 // if it would overflow the destination screen
7462 nsCOMPtr
<nsIScreenManager
> sm
= do_GetService(sScreenManagerContractID
);
7464 nsCOMPtr
<nsIScreen
> screen
;
7465 sm
->ScreenForRect(x
, y
, width
, height
, getter_AddRefs(screen
));
7467 int32_t availLeft
, availTop
, availWidth
, availHeight
;
7468 screen
->GetAvailRect(&availLeft
, &availTop
, &availWidth
, &availHeight
);
7469 if (mResizeState
!= MOVING
) {
7470 x
= std::max(x
, availLeft
);
7471 y
= std::max(y
, availTop
);
7473 width
= std::min(width
, availWidth
);
7474 height
= std::min(height
, availHeight
);
7478 Resize(x
, y
, width
, height
, true);
7480 UpdateNonClientMargins();
7485 // Callback to generate OnCloakChanged pseudo-events.
7487 void nsWindow::OnCloakEvent(HWND aWnd
, bool aCloaked
) {
7488 MOZ_ASSERT(NS_IsMainThread());
7489 MOZ_ASSERT(IsWin8OrLater());
7491 const char* const kEventName
= aCloaked
? "CLOAKED" : "UNCLOAKED";
7492 nsWindow
* pWin
= WinUtils::GetNSWindowPtr(aWnd
);
7495 sCloakingLog
, LogLevel::Debug
,
7496 ("Received %s event for HWND %p (not an nsWindow)", kEventName
, aWnd
));
7500 const char* const kWasCloakedStr
= pWin
->mIsCloaked
? "cloaked" : "uncloaked";
7501 if (mozilla::IsCloaked(aWnd
) == pWin
->mIsCloaked
) {
7502 MOZ_LOG(sCloakingLog
, LogLevel::Debug
,
7503 ("Received redundant %s event for %s HWND %p; discarding",
7504 kEventName
, kWasCloakedStr
, aWnd
));
7509 sCloakingLog
, LogLevel::Info
,
7510 ("Received %s event for %s HWND %p", kEventName
, kWasCloakedStr
, aWnd
));
7512 // Cloaking events like the one we've just received are sent asynchronously.
7513 // Rather than process them one-by-one, we jump the gun a bit and perform
7514 // updates on all newly cloaked/uncloaked nsWindows at once. This also lets us
7515 // batch operations that consider more than one window's state.
7520 nsTArray
<Item
> changedWindows
;
7522 mozilla::EnumerateThreadWindows([&](HWND hwnd
) {
7523 nsWindow
* pWin
= WinUtils::GetNSWindowPtr(hwnd
);
7528 const bool isCloaked
= mozilla::IsCloaked(hwnd
);
7529 if (isCloaked
!= pWin
->mIsCloaked
) {
7530 changedWindows
.AppendElement(Item
{pWin
, isCloaked
});
7534 if (changedWindows
.IsEmpty()) {
7538 for (const Item
& item
: changedWindows
) {
7539 item
.win
->OnCloakChanged(item
.nowCloaked
);
7542 nsWindow::TaskbarConcealer::OnCloakChanged();
7545 void nsWindow::OnCloakChanged(bool aCloaked
) {
7546 MOZ_LOG(sCloakingLog
, LogLevel::Info
,
7547 ("Calling OnCloakChanged(): HWND %p, aCloaked %s", mWnd
,
7548 aCloaked
? "true" : "false"));
7549 mIsCloaked
= aCloaked
;
7552 /**************************************************************
7553 **************************************************************
7555 ** BLOCK: IME management and accessibility
7557 ** Handles managing IME input and accessibility.
7559 **************************************************************
7560 **************************************************************/
7562 void nsWindow::SetInputContext(const InputContext
& aContext
,
7563 const InputContextAction
& aAction
) {
7564 InputContext newInputContext
= aContext
;
7565 IMEHandler::SetInputContext(this, newInputContext
, aAction
);
7566 mInputContext
= newInputContext
;
7569 InputContext
nsWindow::GetInputContext() {
7570 mInputContext
.mIMEState
.mOpen
= IMEState::CLOSED
;
7571 if (WinUtils::IsIMEEnabled(mInputContext
) && IMEHandler::GetOpenState(this)) {
7572 mInputContext
.mIMEState
.mOpen
= IMEState::OPEN
;
7574 mInputContext
.mIMEState
.mOpen
= IMEState::CLOSED
;
7576 return mInputContext
;
7579 TextEventDispatcherListener
* nsWindow::GetNativeTextEventDispatcherListener() {
7580 return IMEHandler::GetNativeTextEventDispatcherListener();
7583 #ifdef ACCESSIBILITY
7585 # define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc) \
7586 if (a11y::logging::IsEnabled(a11y::logging::ePlatforms)) { \
7588 "Get the window:\n {\n HWND: %p, parent HWND: %p, wndobj: " \
7590 aHwnd, ::GetParent(aHwnd), aWnd); \
7591 printf(" acc: %p", aAcc); \
7593 nsAutoString name; \
7595 printf(", accname: %s", NS_ConvertUTF16toUTF8(name).get()); \
7601 # define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc)
7604 a11y::LocalAccessible
* nsWindow::GetAccessible() {
7605 // If the pref was ePlatformIsDisabled, return null here, disabling a11y.
7606 if (a11y::PlatformDisabledState() == a11y::ePlatformIsDisabled
)
7609 if (mInDtor
|| mOnDestroyCalled
|| mWindowType
== WindowType::Invisible
) {
7613 // In case of popup window return a popup accessible.
7614 nsView
* view
= nsView::GetViewFor(this);
7616 nsIFrame
* frame
= view
->GetFrame();
7617 if (frame
&& nsLayoutUtils::IsPopup(frame
)) {
7618 nsAccessibilityService
* accService
= GetOrCreateAccService();
7620 a11y::DocAccessible
* docAcc
=
7621 GetAccService()->GetDocAccessible(frame
->PresShell());
7625 docAcc
->GetAccessibleOrDescendant(frame
->GetContent()));
7626 return docAcc
->GetAccessibleOrDescendant(frame
->GetContent());
7632 // otherwise root document accessible.
7633 NS_LOG_WMGETOBJECT(this, mWnd
, GetRootAccessible());
7634 return GetRootAccessible();
7638 /**************************************************************
7639 **************************************************************
7641 ** BLOCK: Transparency
7643 ** Window transparency helpers.
7645 **************************************************************
7646 **************************************************************/
7648 void nsWindow::SetWindowTranslucencyInner(TransparencyMode aMode
) {
7649 if (aMode
== mTransparencyMode
) return;
7651 // stop on dialogs and popups!
7652 HWND hWnd
= WinUtils::GetTopLevelHWND(mWnd
, true);
7653 nsWindow
* parent
= WinUtils::GetNSWindowPtr(hWnd
);
7656 NS_WARNING("Trying to use transparent chrome in an embedded context");
7660 if (parent
!= this) {
7662 "Setting SetWindowTranslucencyInner on a parent this is not us!");
7665 if (aMode
== TransparencyMode::Transparent
) {
7666 // If we're switching to the use of a transparent window, hide the chrome
7668 HideWindowChrome(true);
7669 } else if (mHideChrome
&&
7670 mTransparencyMode
== TransparencyMode::Transparent
) {
7671 // if we're switching out of transparent, re-enable our parent's chrome.
7672 HideWindowChrome(false);
7675 LONG_PTR style
= ::GetWindowLongPtrW(hWnd
, GWL_STYLE
),
7676 exStyle
= ::GetWindowLongPtr(hWnd
, GWL_EXSTYLE
);
7678 if (parent
->mIsVisible
) {
7679 style
|= WS_VISIBLE
;
7680 if (parent
->mFrameState
->GetSizeMode() == nsSizeMode_Maximized
) {
7681 style
|= WS_MAXIMIZE
;
7682 } else if (parent
->mFrameState
->GetSizeMode() == nsSizeMode_Minimized
) {
7683 style
|= WS_MINIMIZE
;
7687 if (aMode
== TransparencyMode::Transparent
)
7688 exStyle
|= WS_EX_LAYERED
;
7690 exStyle
&= ~WS_EX_LAYERED
;
7692 VERIFY_WINDOW_STYLE(style
);
7693 ::SetWindowLongPtrW(hWnd
, GWL_STYLE
, style
);
7694 ::SetWindowLongPtrW(hWnd
, GWL_EXSTYLE
, exStyle
);
7696 mTransparencyMode
= aMode
;
7698 if (mCompositorWidgetDelegate
) {
7699 mCompositorWidgetDelegate
->UpdateTransparency(aMode
);
7703 /**************************************************************
7704 **************************************************************
7706 ** BLOCK: Popup rollup hooks
7708 ** Deals with CaptureRollup on popup windows.
7710 **************************************************************
7711 **************************************************************/
7713 // Schedules a timer for a window, so we can rollup after processing the hook
7715 void nsWindow::ScheduleHookTimer(HWND aWnd
, UINT aMsgId
) {
7716 // In some cases multiple hooks may be scheduled
7717 // so ignore any other requests once one timer is scheduled
7718 if (sHookTimerId
== 0) {
7719 // Remember the window handle and the message ID to be used later
7720 sRollupMsgId
= aMsgId
;
7721 sRollupMsgWnd
= aWnd
;
7722 // Schedule native timer for doing the rollup after
7723 // this event is done being processed
7724 sHookTimerId
= ::SetTimer(nullptr, 0, 0, (TIMERPROC
)HookTimerForPopups
);
7725 NS_ASSERTION(sHookTimerId
, "Timer couldn't be created.");
7729 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7730 int gLastMsgCode
= 0;
7731 extern MSGFEventMsgInfo gMSGFEvents
[];
7734 // Process Menu messages, rollup when popup is clicked.
7735 LRESULT CALLBACK
nsWindow::MozSpecialMsgFilter(int code
, WPARAM wParam
,
7737 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7739 MSG
* pMsg
= (MSG
*)lParam
;
7742 while (gMSGFEvents
[inx
].mId
!= code
&& gMSGFEvents
[inx
].mStr
!= nullptr) {
7745 if (code
!= gLastMsgCode
) {
7746 if (gMSGFEvents
[inx
].mId
== code
) {
7748 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7749 ("MozSpecialMessageProc - code: 0x%X - %s hw: %p\n", code
,
7750 gMSGFEvents
[inx
].mStr
, pMsg
->hwnd
));
7754 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7755 ("MozSpecialMessageProc - code: 0x%X - %d hw: %p\n", code
,
7756 gMSGFEvents
[inx
].mId
, pMsg
->hwnd
));
7759 gLastMsgCode
= code
;
7761 PrintEvent(pMsg
->message
, FALSE
, FALSE
);
7763 #endif // #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7765 if (sProcessHook
&& code
== MSGF_MENU
) {
7766 MSG
* pMsg
= (MSG
*)lParam
;
7767 ScheduleHookTimer(pMsg
->hwnd
, pMsg
->message
);
7770 return ::CallNextHookEx(sMsgFilterHook
, code
, wParam
, lParam
);
7773 // Process all mouse messages. Roll up when a click is in a native window
7774 // that doesn't have an nsIWidget.
7775 LRESULT CALLBACK
nsWindow::MozSpecialMouseProc(int code
, WPARAM wParam
,
7778 switch (WinUtils::GetNativeMessage(wParam
)) {
7779 case WM_LBUTTONDOWN
:
7780 case WM_RBUTTONDOWN
:
7781 case WM_MBUTTONDOWN
:
7783 case WM_MOUSEHWHEEL
: {
7784 MOUSEHOOKSTRUCT
* ms
= (MOUSEHOOKSTRUCT
*)lParam
;
7785 nsIWidget
* mozWin
= WinUtils::GetNSWindowPtr(ms
->hwnd
);
7787 ScheduleHookTimer(ms
->hwnd
, (UINT
)wParam
);
7793 return ::CallNextHookEx(sCallMouseHook
, code
, wParam
, lParam
);
7796 // Process all messages. Roll up when the window is moving, or
7797 // is resizing or when maximized or mininized.
7798 LRESULT CALLBACK
nsWindow::MozSpecialWndProc(int code
, WPARAM wParam
,
7800 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7802 CWPSTRUCT
* cwpt
= (CWPSTRUCT
*)lParam
;
7803 PrintEvent(cwpt
->message
, FALSE
, FALSE
);
7808 CWPSTRUCT
* cwpt
= (CWPSTRUCT
*)lParam
;
7809 if (cwpt
->message
== WM_MOVING
|| cwpt
->message
== WM_SIZING
||
7810 cwpt
->message
== WM_GETMINMAXINFO
) {
7811 ScheduleHookTimer(cwpt
->hwnd
, (UINT
)cwpt
->message
);
7815 return ::CallNextHookEx(sCallProcHook
, code
, wParam
, lParam
);
7818 // Register the special "hooks" for dropdown processing.
7819 void nsWindow::RegisterSpecialDropdownHooks() {
7820 NS_ASSERTION(!sMsgFilterHook
, "sMsgFilterHook must be NULL!");
7821 NS_ASSERTION(!sCallProcHook
, "sCallProcHook must be NULL!");
7823 DISPLAY_NMM_PRT("***************** Installing Msg Hooks ***************\n");
7825 // Install msg hook for moving the window and resizing
7826 if (!sMsgFilterHook
) {
7827 DISPLAY_NMM_PRT("***** Hooking sMsgFilterHook!\n");
7828 sMsgFilterHook
= SetWindowsHookEx(WH_MSGFILTER
, MozSpecialMsgFilter
,
7829 nullptr, GetCurrentThreadId());
7830 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7831 if (!sMsgFilterHook
) {
7832 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7833 ("***** SetWindowsHookEx is NOT installed for WH_MSGFILTER!\n"));
7838 // Install msg hook for menus
7839 if (!sCallProcHook
) {
7840 DISPLAY_NMM_PRT("***** Hooking sCallProcHook!\n");
7841 sCallProcHook
= SetWindowsHookEx(WH_CALLWNDPROC
, MozSpecialWndProc
, nullptr,
7842 GetCurrentThreadId());
7843 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7844 if (!sCallProcHook
) {
7846 gWindowsLog
, LogLevel::Info
,
7847 ("***** SetWindowsHookEx is NOT installed for WH_CALLWNDPROC!\n"));
7852 // Install msg hook for the mouse
7853 if (!sCallMouseHook
) {
7854 DISPLAY_NMM_PRT("***** Hooking sCallMouseHook!\n");
7855 sCallMouseHook
= SetWindowsHookEx(WH_MOUSE
, MozSpecialMouseProc
, nullptr,
7856 GetCurrentThreadId());
7857 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7858 if (!sCallMouseHook
) {
7859 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7860 ("***** SetWindowsHookEx is NOT installed for WH_MOUSE!\n"));
7866 // Unhook special message hooks for dropdowns.
7867 void nsWindow::UnregisterSpecialDropdownHooks() {
7869 "***************** De-installing Msg Hooks ***************\n");
7871 if (sCallProcHook
) {
7872 DISPLAY_NMM_PRT("***** Unhooking sCallProcHook!\n");
7873 if (!::UnhookWindowsHookEx(sCallProcHook
)) {
7874 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallProcHook!\n");
7876 sCallProcHook
= nullptr;
7879 if (sMsgFilterHook
) {
7880 DISPLAY_NMM_PRT("***** Unhooking sMsgFilterHook!\n");
7881 if (!::UnhookWindowsHookEx(sMsgFilterHook
)) {
7882 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sMsgFilterHook!\n");
7884 sMsgFilterHook
= nullptr;
7887 if (sCallMouseHook
) {
7888 DISPLAY_NMM_PRT("***** Unhooking sCallMouseHook!\n");
7889 if (!::UnhookWindowsHookEx(sCallMouseHook
)) {
7890 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallMouseHook!\n");
7892 sCallMouseHook
= nullptr;
7896 // This timer is designed to only fire one time at most each time a "hook"
7897 // function is used to rollup the dropdown. In some cases, the timer may be
7898 // scheduled from the hook, but that hook event or a subsequent event may roll
7899 // up the dropdown before this timer function is executed.
7901 // For example, if an MFC control takes focus, the combobox will lose focus and
7902 // rollup before this function fires.
7903 VOID CALLBACK
nsWindow::HookTimerForPopups(HWND hwnd
, UINT uMsg
, UINT idEvent
,
7905 if (sHookTimerId
!= 0) {
7906 // if the window is nullptr then we need to use the ID to kill the timer
7907 DebugOnly
<BOOL
> status
= ::KillTimer(nullptr, sHookTimerId
);
7908 NS_ASSERTION(status
, "Hook Timer was not killed.");
7912 if (sRollupMsgId
!= 0) {
7913 // Note: DealWithPopups does the check to make sure that the rollup widget
7915 LRESULT popupHandlingResult
;
7916 nsAutoRollup autoRollup
;
7917 DealWithPopups(sRollupMsgWnd
, sRollupMsgId
, 0, 0, &popupHandlingResult
);
7919 sRollupMsgWnd
= nullptr;
7923 static bool IsDifferentThreadWindow(HWND aWnd
) {
7924 return ::GetCurrentThreadId() != ::GetWindowThreadProcessId(aWnd
, nullptr);
7928 bool nsWindow::EventIsInsideWindow(nsWindow
* aWindow
,
7929 Maybe
<POINT
> aEventPoint
) {
7931 ::GetWindowRect(aWindow
->mWnd
, &r
);
7936 DWORD pos
= ::GetMessagePos();
7937 mp
.x
= GET_X_LPARAM(pos
);
7938 mp
.y
= GET_Y_LPARAM(pos
);
7941 auto margin
= aWindow
->mInputRegion
.mMargin
;
7949 // was the event inside this window?
7950 return static_cast<bool>(::PtInRect(&r
, mp
));
7954 bool nsWindow::GetPopupsToRollup(nsIRollupListener
* aRollupListener
,
7955 uint32_t* aPopupsToRollup
,
7956 Maybe
<POINT
> aEventPoint
) {
7957 // If we're dealing with menus, we probably have submenus and we don't want
7958 // to rollup some of them if the click is in a parent menu of the current
7960 *aPopupsToRollup
= UINT32_MAX
;
7961 AutoTArray
<nsIWidget
*, 5> widgetChain
;
7962 uint32_t sameTypeCount
= aRollupListener
->GetSubmenuWidgetChain(&widgetChain
);
7963 for (uint32_t i
= 0; i
< widgetChain
.Length(); ++i
) {
7964 nsIWidget
* widget
= widgetChain
[i
];
7965 if (EventIsInsideWindow(static_cast<nsWindow
*>(widget
), aEventPoint
)) {
7966 // Don't roll up if the mouse event occurred within a menu of the
7967 // same type. If the mouse event occurred in a menu higher than that,
7968 // roll up, but pass the number of popups to Rollup so that only those
7969 // of the same type close up.
7970 if (i
< sameTypeCount
) {
7974 *aPopupsToRollup
= sameTypeCount
;
7982 bool nsWindow::NeedsToHandleNCActivateDelayed(HWND aWnd
) {
7983 // While popup is open, popup window might be activated by other application.
7984 // At this time, we need to take back focus to the previous window but it
7985 // causes flickering its nonclient area because WM_NCACTIVATE comes before
7986 // WM_ACTIVATE and we cannot know which window will take focus at receiving
7987 // WM_NCACTIVATE. Therefore, we need a hack for preventing the flickerling.
7989 // If non-popup window receives WM_NCACTIVATE at deactivating, default
7990 // wndproc shouldn't handle it as deactivating. Instead, at receiving
7991 // WM_ACTIVIATE after that, WM_NCACTIVATE should be sent again manually.
7992 // This returns true if the window needs to handle WM_NCACTIVATE later.
7994 nsWindow
* window
= WinUtils::GetNSWindowPtr(aWnd
);
7995 return window
&& !window
->IsPopup();
7998 static bool IsTouchSupportEnabled(HWND aWnd
) {
7999 nsWindow
* topWindow
=
8000 WinUtils::GetNSWindowPtr(WinUtils::GetTopLevelHWND(aWnd
, true));
8001 return topWindow
? topWindow
->IsTouchWindow() : false;
8004 static Maybe
<POINT
> GetSingleTouch(WPARAM wParam
, LPARAM lParam
) {
8006 uint32_t cInputs
= LOWORD(wParam
);
8011 if (GetTouchInputInfo((HTOUCHINPUT
)lParam
, cInputs
, &input
,
8012 sizeof(TOUCHINPUT
))) {
8014 ret
->x
= TOUCH_COORD_TO_PIXEL(input
.x
);
8015 ret
->y
= TOUCH_COORD_TO_PIXEL(input
.y
);
8017 // Note that we don't call CloseTouchInputHandle here because we need
8018 // to read the touch input info again in OnTouch later.
8023 bool nsWindow::DealWithPopups(HWND aWnd
, UINT aMessage
, WPARAM aWParam
,
8024 LPARAM aLParam
, LRESULT
* aResult
) {
8025 NS_ASSERTION(aResult
, "Bad outResult");
8027 // XXX Why do we use the return value of WM_MOUSEACTIVATE for all messages?
8028 *aResult
= MA_NOACTIVATE
;
8030 if (!::IsWindowVisible(aWnd
)) {
8034 if (MOZ_UNLIKELY(aMessage
== WM_KILLFOCUS
)) {
8035 // NOTE: We deal with this here rather than on the switch below because we
8036 // want to do this even if there are no menus to rollup (tooltips don't set
8037 // the rollup listener etc).
8038 if (RefPtr pm
= nsXULPopupManager::GetInstance()) {
8039 pm
->RollupTooltips();
8043 nsIRollupListener
* rollupListener
= nsBaseWidget::GetActiveRollupListener();
8044 NS_ENSURE_TRUE(rollupListener
, false);
8046 nsCOMPtr
<nsIWidget
> popup
= rollupListener
->GetRollupWidget();
8051 // Don't rollup a popup if it has an open file picker
8052 nsWindow
* window
= WinUtils::GetNSWindowPtr(aWnd
);
8053 if (!window
|| window
->mPickerDisplayCount
) {
8057 static bool sSendingNCACTIVATE
= false;
8058 static bool sPendingNCACTIVATE
= false;
8059 uint32_t popupsToRollup
= UINT32_MAX
;
8061 bool consumeRollupEvent
= false;
8062 Maybe
<POINT
> touchPoint
; // In screen coords.
8064 // If we rollup with animations but get occluded right away, we might not
8065 // advance the refresh driver enough for the animation to finish.
8066 auto allowAnimations
= nsIRollupListener::AllowAnimations::Yes
;
8067 nsWindow
* popupWindow
= static_cast<nsWindow
*>(popup
.get());
8068 UINT nativeMessage
= WinUtils::GetNativeMessage(aMessage
);
8069 switch (nativeMessage
) {
8071 if (!IsTouchSupportEnabled(aWnd
)) {
8072 // If APZ is disabled, don't allow touch inputs to dismiss popups. The
8073 // compatibility mouse events will do it instead.
8076 touchPoint
= GetSingleTouch(aWParam
, aLParam
);
8081 case WM_LBUTTONDOWN
:
8082 case WM_RBUTTONDOWN
:
8083 case WM_MBUTTONDOWN
:
8084 case WM_NCLBUTTONDOWN
:
8085 case WM_NCRBUTTONDOWN
:
8086 case WM_NCMBUTTONDOWN
:
8087 if (nativeMessage
!= WM_TOUCH
&& IsTouchSupportEnabled(aWnd
) &&
8088 MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
8089 // If any of these mouse events are really compatibility events that
8090 // Windows is sending for touch inputs, then don't allow them to dismiss
8091 // popups when APZ is enabled (instead we do the dismissing as part of
8092 // WM_TOUCH handling which is more correct).
8093 // If we don't do this, then when the user lifts their finger after a
8094 // long-press, the WM_RBUTTONDOWN compatibility event that Windows sends
8095 // us will dismiss the contextmenu popup that we displayed as part of
8096 // handling the long-tap-up.
8099 if (!EventIsInsideWindow(popupWindow
, touchPoint
) &&
8100 GetPopupsToRollup(rollupListener
, &popupsToRollup
, touchPoint
)) {
8104 case WM_POINTERDOWN
: {
8105 WinPointerEvents pointerEvents
;
8106 if (!pointerEvents
.ShouldRollupOnPointerEvent(nativeMessage
, aWParam
)) {
8110 pt
.x
= GET_X_LPARAM(aLParam
);
8111 pt
.y
= GET_Y_LPARAM(aLParam
);
8112 if (!GetPopupsToRollup(rollupListener
, &popupsToRollup
, Some(pt
))) {
8115 if (EventIsInsideWindow(popupWindow
, Some(pt
))) {
8116 // Don't roll up if the event is inside the popup window.
8120 case MOZ_WM_DMANIP
: {
8122 ::GetCursorPos(&pt
);
8123 if (!GetPopupsToRollup(rollupListener
, &popupsToRollup
, Some(pt
))) {
8126 if (EventIsInsideWindow(popupWindow
, Some(pt
))) {
8127 // Don't roll up if the event is inside the popup window
8132 case WM_MOUSEHWHEEL
:
8133 // We need to check if the popup thinks that it should cause closing
8134 // itself when mouse wheel events are fired outside the rollup widget.
8135 if (!EventIsInsideWindow(popupWindow
)) {
8136 // Check if we should consume this event even if we don't roll-up:
8137 consumeRollupEvent
= rollupListener
->ShouldConsumeOnMouseWheelEvent();
8138 *aResult
= MA_ACTIVATE
;
8139 if (rollupListener
->ShouldRollupOnMouseWheelEvent() &&
8140 GetPopupsToRollup(rollupListener
, &popupsToRollup
)) {
8144 return consumeRollupEvent
;
8146 case WM_ACTIVATEAPP
:
8147 allowAnimations
= nsIRollupListener::AllowAnimations::No
;
8151 // NOTE: Don't handle WA_INACTIVE for preventing popup taking focus
8152 // because we cannot distinguish it's caused by mouse or not.
8153 if (LOWORD(aWParam
) == WA_ACTIVE
&& aLParam
) {
8154 if (window
&& window
->IsPopup()) {
8155 // Cancel notifying widget listeners of deactivating the previous
8156 // active window (see WM_KILLFOCUS case in ProcessMessage()).
8157 sJustGotDeactivate
= false;
8158 // Reactivate the window later.
8159 ::PostMessageW(aWnd
, MOZ_WM_REACTIVATE
, aWParam
, aLParam
);
8162 // Don't rollup the popup when focus moves back to the parent window
8163 // from a popup because such case is caused by strange mouse drivers.
8164 nsWindow
* prevWindow
=
8165 WinUtils::GetNSWindowPtr(reinterpret_cast<HWND
>(aLParam
));
8166 if (prevWindow
&& prevWindow
->IsPopup()) {
8167 // Consume this message here since previous window must not have
8168 // been inactivated since we've already stopped accepting the
8169 // inactivation below.
8172 } else if (LOWORD(aWParam
) == WA_INACTIVE
) {
8173 nsWindow
* activeWindow
=
8174 WinUtils::GetNSWindowPtr(reinterpret_cast<HWND
>(aLParam
));
8175 if (sPendingNCACTIVATE
&& NeedsToHandleNCActivateDelayed(aWnd
)) {
8176 // If focus moves to non-popup widget or focusable popup, the window
8177 // needs to update its nonclient area.
8178 if (!activeWindow
|| !activeWindow
->IsPopup()) {
8179 sSendingNCACTIVATE
= true;
8180 ::SendMessageW(aWnd
, WM_NCACTIVATE
, false, 0);
8181 sSendingNCACTIVATE
= false;
8183 sPendingNCACTIVATE
= false;
8185 // If focus moves from/to popup, we don't need to rollup the popup
8186 // because such case is caused by strange mouse drivers. And in
8187 // such case, we should consume the message here since we need to
8188 // hide this odd focus move from our content. (If we didn't consume
8189 // the message here, ProcessMessage() will notify widget listener of
8190 // inactivation and that causes unnecessary reflow for supporting
8191 // -moz-window-inactive pseudo class.
8193 if (activeWindow
->IsPopup()) {
8196 nsWindow
* deactiveWindow
= WinUtils::GetNSWindowPtr(aWnd
);
8197 if (deactiveWindow
&& deactiveWindow
->IsPopup()) {
8201 } else if (LOWORD(aWParam
) == WA_CLICKACTIVE
) {
8202 // If the WM_ACTIVATE message is caused by a click in a popup,
8203 // we should not rollup any popups.
8204 nsWindow
* window
= WinUtils::GetNSWindowPtr(aWnd
);
8205 if ((window
&& window
->IsPopup()) ||
8206 !GetPopupsToRollup(rollupListener
, &popupsToRollup
)) {
8210 allowAnimations
= nsIRollupListener::AllowAnimations::No
;
8213 case MOZ_WM_REACTIVATE
:
8214 // The previous active window should take back focus.
8215 if (::IsWindow(reinterpret_cast<HWND
>(aLParam
))) {
8216 // FYI: Even without this API call, you see expected result (e.g., the
8217 // owner window of the popup keeps active without flickering
8218 // the non-client area). And also this causes initializing
8219 // TSF and it causes using CPU time a lot. However, even if we
8220 // consume WM_ACTIVE messages, native focus change has already
8221 // been occurred. I.e., a popup window is active now. Therefore,
8222 // you'll see some odd behavior if we don't reactivate the owner
8223 // window here. For example, if you do:
8224 // 1. Turn wheel on a bookmark panel.
8225 // 2. Turn wheel on another window.
8226 // then, you'll see that the another window becomes active but the
8227 // owner window of the bookmark panel looks still active and the
8228 // bookmark panel keeps open. The reason is that the first wheel
8229 // operation gives focus to the bookmark panel. Therefore, when
8230 // the next operation gives focus to the another window, previous
8231 // focus window is the bookmark panel (i.e., a popup window).
8232 // So, in this case, our hack around here prevents to inactivate
8233 // the owner window and roll up the bookmark panel.
8234 ::SetForegroundWindow(reinterpret_cast<HWND
>(aLParam
));
8239 if (!aWParam
&& !sSendingNCACTIVATE
&&
8240 NeedsToHandleNCActivateDelayed(aWnd
)) {
8241 // Don't just consume WM_NCACTIVATE. It doesn't handle only the
8242 // nonclient area state change.
8243 ::DefWindowProcW(aWnd
, aMessage
, TRUE
, aLParam
);
8244 // Accept the deactivating because it's necessary to receive following
8247 sPendingNCACTIVATE
= true;
8252 case WM_MOUSEACTIVATE
:
8253 if (!EventIsInsideWindow(popupWindow
) &&
8254 GetPopupsToRollup(rollupListener
, &popupsToRollup
)) {
8255 // WM_MOUSEACTIVATE may be caused by moving the mouse (e.g., X-mouse
8256 // of TweakUI is enabled. Then, check if the popup should be rolled up
8257 // with rollup listener. If not, just consume the message.
8258 if (HIWORD(aLParam
) == WM_MOUSEMOVE
&&
8259 !rollupListener
->ShouldRollupOnMouseActivate()) {
8262 // Otherwise, it should be handled by wndproc.
8266 // Prevent the click inside the popup from causing a change in window
8267 // activation. Since the popup is shown non-activated, we need to eat any
8268 // requests to activate the window while it is displayed. Windows will
8269 // automatically activate the popup on the mousedown otherwise.
8273 // If the window is being minimized, close popups.
8274 if (aLParam
== SW_PARENTCLOSING
) {
8275 allowAnimations
= nsIRollupListener::AllowAnimations::No
;
8281 // If focus moves to other window created in different process/thread,
8282 // e.g., a plugin window, popups should be rolled up.
8283 if (IsDifferentThreadWindow(reinterpret_cast<HWND
>(aWParam
))) {
8284 allowAnimations
= nsIRollupListener::AllowAnimations::No
;
8297 // Only exit if the touchpoint is within the browser window
8298 // EventIsInsideWindow handles for Maybe conditions
8299 // It does not matter if not a touch because otherwise it justs gets the event
8301 if (!EventIsInsideWindow(window
, touchPoint
)) {
8305 // Only need to deal with the last rollup for left mouse down events.
8306 NS_ASSERTION(!nsAutoRollup::GetLastRollup(), "last rollup is null");
8308 nsIRollupListener::RollupOptions rollupOptions
{
8310 nsIRollupListener::FlushViews::Yes
,
8311 /* mPoint = */ nullptr,
8315 if (nativeMessage
== WM_TOUCH
|| nativeMessage
== WM_LBUTTONDOWN
||
8316 nativeMessage
== WM_POINTERDOWN
) {
8317 LayoutDeviceIntPoint pos
;
8318 if (nativeMessage
== WM_TOUCH
) {
8319 pos
.x
= touchPoint
->x
;
8320 pos
.y
= touchPoint
->y
;
8323 pt
.x
= GET_X_LPARAM(aLParam
);
8324 pt
.y
= GET_Y_LPARAM(aLParam
);
8325 // POINTERDOWN is already in screen coords.
8326 if (nativeMessage
== WM_LBUTTONDOWN
) {
8327 ::ClientToScreen(aWnd
, &pt
);
8329 pos
= LayoutDeviceIntPoint(pt
.x
, pt
.y
);
8332 rollupOptions
.mPoint
= &pos
;
8333 nsIContent
* lastRollup
= nullptr;
8334 consumeRollupEvent
= rollupListener
->Rollup(rollupOptions
, &lastRollup
);
8335 nsAutoRollup::SetLastRollup(lastRollup
);
8337 consumeRollupEvent
= rollupListener
->Rollup(rollupOptions
);
8340 // Tell hook to stop processing messages
8341 sProcessHook
= false;
8343 sRollupMsgWnd
= nullptr;
8345 // If we are NOT supposed to be consuming events, let it go through
8346 if (consumeRollupEvent
&& nativeMessage
!= WM_RBUTTONDOWN
) {
8347 *aResult
= MA_ACTIVATE
;
8354 /**************************************************************
8355 **************************************************************
8357 ** BLOCK: Misc. utility methods and functions.
8361 **************************************************************
8362 **************************************************************/
8364 // Note that the result of GetTopLevelWindow method can be different from the
8365 // result of WinUtils::GetTopLevelHWND(). The result can be non-floating
8366 // window. Because our top level window may be contained in another window
8367 // which is not managed by us.
8368 nsWindow
* nsWindow::GetTopLevelWindow(bool aStopOnDialogOrPopup
) {
8369 nsWindow
* curWindow
= this;
8372 if (aStopOnDialogOrPopup
) {
8373 switch (curWindow
->mWindowType
) {
8374 case WindowType::Dialog
:
8375 case WindowType::Popup
:
8382 // Retrieve the top level parent or owner window
8383 nsWindow
* parentWindow
= curWindow
->GetParentWindow(true);
8385 if (!parentWindow
) return curWindow
;
8387 curWindow
= parentWindow
;
8391 // Set a flag if hwnd is a (non-popup) visible window from this process,
8392 // and bail out of the enumeration. Otherwise leave the flag unmodified
8393 // and continue the enumeration.
8394 // lParam must be a bool* pointing at the flag to be set.
8395 static BOOL CALLBACK
EnumVisibleWindowsProc(HWND hwnd
, LPARAM lParam
) {
8397 ::GetWindowThreadProcessId(hwnd
, &pid
);
8398 if (pid
== ::GetCurrentProcessId() && ::IsWindowVisible(hwnd
)) {
8399 // Don't count popups as visible windows, since they don't take focus,
8400 // in case we only have a popup visible (see bug 1554490 where the gfx
8401 // test window is an offscreen popup).
8402 nsWindow
* window
= WinUtils::GetNSWindowPtr(hwnd
);
8403 if (!window
|| !window
->IsPopup()) {
8404 bool* windowsVisible
= reinterpret_cast<bool*>(lParam
);
8405 *windowsVisible
= true;
8412 // Determine if it would be ok to activate a window, taking focus.
8413 // We want to avoid stealing focus from another app (bug 225305).
8414 bool nsWindow::CanTakeFocus() {
8415 HWND fgWnd
= ::GetForegroundWindow();
8417 // There is no foreground window, so don't worry about stealing focus.
8420 // We can take focus if the current foreground window is already from
8423 ::GetWindowThreadProcessId(fgWnd
, &pid
);
8424 if (pid
== ::GetCurrentProcessId()) {
8428 bool windowsVisible
= false;
8429 ::EnumWindows(EnumVisibleWindowsProc
,
8430 reinterpret_cast<LPARAM
>(&windowsVisible
));
8432 if (!windowsVisible
) {
8433 // We're probably creating our first visible window, allow that to
8440 /* static */ const wchar_t* nsWindow::GetMainWindowClass() {
8441 static const wchar_t* sMainWindowClass
= nullptr;
8442 if (!sMainWindowClass
) {
8443 nsAutoString className
;
8444 Preferences::GetString("ui.window_class_override", className
);
8445 if (!className
.IsEmpty()) {
8446 sMainWindowClass
= wcsdup(className
.get());
8448 sMainWindowClass
= kClassNameGeneral
;
8451 return sMainWindowClass
;
8454 LPARAM
nsWindow::lParamToScreen(LPARAM lParam
) {
8456 pt
.x
= GET_X_LPARAM(lParam
);
8457 pt
.y
= GET_Y_LPARAM(lParam
);
8458 ::ClientToScreen(mWnd
, &pt
);
8459 return MAKELPARAM(pt
.x
, pt
.y
);
8462 LPARAM
nsWindow::lParamToClient(LPARAM lParam
) {
8464 pt
.x
= GET_X_LPARAM(lParam
);
8465 pt
.y
= GET_Y_LPARAM(lParam
);
8466 ::ScreenToClient(mWnd
, &pt
);
8467 return MAKELPARAM(pt
.x
, pt
.y
);
8470 WPARAM
nsWindow::wParamFromGlobalMouseState() {
8473 if (!!::GetKeyState(VK_CONTROL
)) {
8474 result
|= MK_CONTROL
;
8477 if (!!::GetKeyState(VK_SHIFT
)) {
8481 if (!!::GetKeyState(VK_LBUTTON
)) {
8482 result
|= MK_LBUTTON
;
8485 if (!!::GetKeyState(VK_MBUTTON
)) {
8486 result
|= MK_MBUTTON
;
8489 if (!!::GetKeyState(VK_RBUTTON
)) {
8490 result
|= MK_RBUTTON
;
8493 if (!!::GetKeyState(VK_XBUTTON1
)) {
8494 result
|= MK_XBUTTON1
;
8497 if (!!::GetKeyState(VK_XBUTTON2
)) {
8498 result
|= MK_XBUTTON2
;
8504 void nsWindow::PickerOpen() { mPickerDisplayCount
++; }
8506 void nsWindow::PickerClosed() {
8507 NS_ASSERTION(mPickerDisplayCount
> 0, "mPickerDisplayCount out of sync!");
8508 if (!mPickerDisplayCount
) return;
8509 mPickerDisplayCount
--;
8510 if (!mPickerDisplayCount
&& mDestroyCalled
) {
8515 bool nsWindow::WidgetTypeSupportsAcceleration() {
8516 // We don't currently support using an accelerated layer manager with
8517 // transparent windows so don't even try. I'm also not sure if we even
8518 // want to support this case. See bug 593471.
8520 // Windows' support for transparent accelerated surfaces isn't great.
8521 // Some possible approaches:
8522 // - Readback the data and update it using
8523 // UpdateLayeredWindow/UpdateLayeredWindowIndirect
8524 // This is what WPF does. See
8525 // CD3DDeviceLevel1::PresentWithGDI/CD3DSwapChainWithSwDC in WpfGfx. The
8526 // rationale for not using IDirect3DSurface9::GetDC is explained here:
8527 // https://web.archive.org/web/20160521191104/https://blogs.msdn.microsoft.com/dwayneneed/2008/09/08/transparent-windows-in-wpf/
8528 // - Use D3D11_RESOURCE_MISC_GDI_COMPATIBLE, IDXGISurface1::GetDC(),
8529 // and UpdateLayeredWindowIndirect.
8530 // This is suggested here:
8531 // https://docs.microsoft.com/en-us/archive/msdn-magazine/2009/december/windows-with-c-layered-windows-with-direct2d
8532 // but might have the same problem that IDirect3DSurface9::GetDC has.
8533 // - Creating the window with the WS_EX_NOREDIRECTIONBITMAP flag and use
8534 // DirectComposition.
8535 // Not supported on Win7.
8536 // - Using DwmExtendFrameIntoClientArea with negative margins and something
8537 // to turn off the glass effect.
8538 // This doesn't work when the DWM is not running (Win7)
8540 // Also see bug 1150376, D3D11 composition can cause issues on some devices
8541 // on Windows 7 where presentation fails randomly for windows with drop
8543 return mTransparencyMode
!= TransparencyMode::Transparent
&&
8544 !(IsPopup() && DeviceManagerDx::Get()->IsWARP());
8547 bool nsWindow::DispatchTouchEventFromWMPointer(
8548 UINT msg
, LPARAM aLParam
, const WinPointerInfo
& aPointerInfo
,
8549 mozilla::MouseButton aButton
) {
8550 MultiTouchInput::MultiTouchType touchType
;
8552 case WM_POINTERDOWN
:
8553 touchType
= MultiTouchInput::MULTITOUCH_START
;
8555 case WM_POINTERUPDATE
:
8556 if (aPointerInfo
.mPressure
== 0) {
8557 return false; // hover
8559 touchType
= MultiTouchInput::MULTITOUCH_MOVE
;
8562 touchType
= MultiTouchInput::MULTITOUCH_END
;
8568 nsPointWin touchPoint
;
8569 touchPoint
.x
= GET_X_LPARAM(aLParam
);
8570 touchPoint
.y
= GET_Y_LPARAM(aLParam
);
8571 touchPoint
.ScreenToClient(mWnd
);
8573 SingleTouchData
touchData(static_cast<int32_t>(aPointerInfo
.pointerId
),
8574 ScreenIntPoint::FromUnknownPoint(touchPoint
),
8575 ScreenSize(1, 1), // pixel size radius for pen
8576 0.0f
, // no radius rotation
8577 aPointerInfo
.mPressure
);
8578 touchData
.mTiltX
= aPointerInfo
.tiltX
;
8579 touchData
.mTiltY
= aPointerInfo
.tiltY
;
8580 touchData
.mTwist
= aPointerInfo
.twist
;
8582 MultiTouchInput touchInput
;
8583 touchInput
.mType
= touchType
;
8584 touchInput
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
8585 touchInput
.mTouches
.AppendElement(touchData
);
8586 touchInput
.mButton
= aButton
;
8587 touchInput
.mButtons
= aPointerInfo
.mButtons
;
8589 // POINTER_INFO.dwKeyStates can't be used as it only supports Shift and Ctrl
8590 ModifierKeyState modifierKeyState
;
8591 touchInput
.modifiers
= modifierKeyState
.GetModifiers();
8593 DispatchTouchInput(touchInput
, MouseEvent_Binding::MOZ_SOURCE_PEN
);
8597 static MouseButton
PenFlagsToMouseButton(PEN_FLAGS aPenFlags
) {
8598 // Theoretically flags can be set together but they do not
8599 if (aPenFlags
& PEN_FLAG_BARREL
) {
8600 return MouseButton::eSecondary
;
8602 if (aPenFlags
& PEN_FLAG_ERASER
) {
8603 return MouseButton::eEraser
;
8605 return MouseButton::ePrimary
;
8608 bool nsWindow::OnPointerEvents(UINT msg
, WPARAM aWParam
, LPARAM aLParam
) {
8610 // APZ is not available on context menu. Follow the behavior of touch input
8611 // which fallbacks to WM_LBUTTON* and WM_GESTURE, to keep consistency.
8614 if (!mPointerEvents
.ShouldHandleWinPointerMessages(msg
, aWParam
)) {
8617 if (!mPointerEvents
.ShouldFirePointerEventByWinPointerMessages()) {
8618 // We have to handle WM_POINTER* to fetch and cache pen related information
8619 // and fire WidgetMouseEvent with the cached information the WM_*BUTTONDOWN
8620 // handler. This is because Windows doesn't support ::DoDragDrop in the
8621 // touch or pen message handlers.
8622 mPointerEvents
.ConvertAndCachePointerInfo(msg
, aWParam
);
8623 // Don't consume the Windows WM_POINTER* messages
8627 uint32_t pointerId
= mPointerEvents
.GetPointerId(aWParam
);
8628 POINTER_PEN_INFO penInfo
{};
8629 if (!mPointerEvents
.GetPointerPenInfo(pointerId
, &penInfo
)) {
8633 // When dispatching mouse events with pen, there may be some
8634 // WM_POINTERUPDATE messages between WM_POINTERDOWN and WM_POINTERUP with
8635 // small movements. Those events will reset sLastMousePoint and reset
8636 // sLastClickCount. To prevent that, we keep the last pen down position
8637 // and compare it with the subsequent WM_POINTERUPDATE. If the movement is
8638 // smaller than GetSystemMetrics(SM_CXDRAG), then we suppress firing
8639 // eMouseMove for WM_POINTERUPDATE.
8640 static POINT sLastPointerDownPoint
= {0};
8642 // We don't support chorded buttons for pen. Keep the button at
8644 static mozilla::MouseButton sLastPenDownButton
= MouseButton::ePrimary
;
8645 static bool sPointerDown
= false;
8647 EventMessage message
;
8648 mozilla::MouseButton button
= MouseButton::ePrimary
;
8650 case WM_POINTERDOWN
: {
8651 LayoutDeviceIntPoint
eventPoint(GET_X_LPARAM(aLParam
),
8652 GET_Y_LPARAM(aLParam
));
8653 sLastPointerDownPoint
.x
= eventPoint
.x
;
8654 sLastPointerDownPoint
.y
= eventPoint
.y
;
8655 message
= eMouseDown
;
8656 button
= PenFlagsToMouseButton(penInfo
.penFlags
);
8657 sLastPenDownButton
= button
;
8658 sPointerDown
= true;
8662 MOZ_ASSERT(sPointerDown
, "receive WM_POINTERUP w/o WM_POINTERDOWN");
8663 button
= sPointerDown
? sLastPenDownButton
: MouseButton::ePrimary
;
8664 sPointerDown
= false;
8666 case WM_POINTERUPDATE
:
8667 message
= eMouseMove
;
8669 LayoutDeviceIntPoint
eventPoint(GET_X_LPARAM(aLParam
),
8670 GET_Y_LPARAM(aLParam
));
8671 int32_t movementX
= sLastPointerDownPoint
.x
> eventPoint
.x
8672 ? sLastPointerDownPoint
.x
- eventPoint
.x
.value
8673 : eventPoint
.x
.value
- sLastPointerDownPoint
.x
;
8674 int32_t movementY
= sLastPointerDownPoint
.y
> eventPoint
.y
8675 ? sLastPointerDownPoint
.y
- eventPoint
.y
.value
8676 : eventPoint
.y
.value
- sLastPointerDownPoint
.y
;
8677 bool insideMovementThreshold
=
8678 movementX
< (int32_t)::GetSystemMetrics(SM_CXDRAG
) &&
8679 movementY
< (int32_t)::GetSystemMetrics(SM_CYDRAG
);
8681 if (insideMovementThreshold
) {
8682 // Suppress firing eMouseMove for WM_POINTERUPDATE if the movement
8683 // from last WM_POINTERDOWN is smaller than SM_CXDRAG / SM_CYDRAG
8686 button
= sLastPenDownButton
;
8689 case WM_POINTERLEAVE
:
8690 message
= eMouseExitFromWidget
;
8696 // Windows defines the pen pressure is normalized to a range between 0 and
8697 // 1024. Convert it to float.
8698 float pressure
= penInfo
.pressure
? (float)penInfo
.pressure
/ 1024 : 0;
8699 int16_t buttons
= sPointerDown
8700 ? nsContentUtils::GetButtonsFlagForButton(button
)
8701 : MouseButtonsFlag::eNoButtons
;
8702 WinPointerInfo
pointerInfo(pointerId
, penInfo
.tiltX
, penInfo
.tiltY
, pressure
,
8705 // https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-pointer_pen_info,
8706 // the rotation is normalized in a range of 0 to 359.
8707 MOZ_ASSERT(penInfo
.rotation
<= 359);
8708 pointerInfo
.twist
= (int32_t)penInfo
.rotation
;
8710 // Fire touch events but not when the barrel button is pressed.
8711 if (button
!= MouseButton::eSecondary
&&
8712 StaticPrefs::dom_w3c_pointer_events_scroll_by_pen_enabled() &&
8713 DispatchTouchEventFromWMPointer(msg
, aLParam
, pointerInfo
, button
)) {
8717 // The aLParam of WM_POINTER* is the screen location. Convert it to client
8719 LPARAM newLParam
= lParamToClient(aLParam
);
8720 DispatchMouseEvent(message
, aWParam
, newLParam
, false, button
,
8721 MouseEvent_Binding::MOZ_SOURCE_PEN
, &pointerInfo
);
8723 if (button
== MouseButton::eSecondary
&& message
== eMouseUp
) {
8724 // Fire eContextMenu manually since consuming WM_POINTER* blocks
8726 DispatchMouseEvent(eContextMenu
, aWParam
, newLParam
, false, button
,
8727 MouseEvent_Binding::MOZ_SOURCE_PEN
, &pointerInfo
);
8729 // Consume WM_POINTER* to stop Windows fires WM_*BUTTONDOWN / WM_*BUTTONUP
8734 void nsWindow::GetCompositorWidgetInitData(
8735 mozilla::widget::CompositorWidgetInitData
* aInitData
) {
8736 *aInitData
= WinCompositorWidgetInitData(
8737 reinterpret_cast<uintptr_t>(mWnd
),
8738 reinterpret_cast<uintptr_t>(static_cast<nsIWidget
*>(this)),
8739 mTransparencyMode
, mFrameState
->GetSizeMode());
8742 bool nsWindow::SynchronouslyRepaintOnResize() {
8743 return !gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled();
8746 void nsWindow::MaybeDispatchInitialFocusEvent() {
8747 if (mIsShowingPreXULSkeletonUI
&& ::GetActiveWindow() == mWnd
) {
8748 DispatchFocusToTopLevelWindow(true);
8752 already_AddRefed
<nsIWidget
> nsIWidget::CreateTopLevelWindow() {
8753 nsCOMPtr
<nsIWidget
> window
= new nsWindow();
8754 return window
.forget();
8757 already_AddRefed
<nsIWidget
> nsIWidget::CreateChildWindow() {
8758 nsCOMPtr
<nsIWidget
> window
= new nsWindow(true);
8759 return window
.forget();
8763 bool nsWindow::InitTouchInjection() {
8764 if (!sTouchInjectInitialized
) {
8765 // Initialize touch injection on the first call
8766 HMODULE hMod
= LoadLibraryW(kUser32LibName
);
8771 InitializeTouchInjectionPtr func
=
8772 (InitializeTouchInjectionPtr
)GetProcAddress(hMod
,
8773 "InitializeTouchInjection");
8775 WinUtils::Log("InitializeTouchInjection not available.");
8779 if (!func(TOUCH_INJECT_MAX_POINTS
, TOUCH_FEEDBACK_DEFAULT
)) {
8780 WinUtils::Log("InitializeTouchInjection failure. GetLastError=%d",
8785 sInjectTouchFuncPtr
=
8786 (InjectTouchInputPtr
)GetProcAddress(hMod
, "InjectTouchInput");
8787 if (!sInjectTouchFuncPtr
) {
8788 WinUtils::Log("InjectTouchInput not available.");
8791 sTouchInjectInitialized
= true;
8796 bool nsWindow::InjectTouchPoint(uint32_t aId
, LayoutDeviceIntPoint
& aPoint
,
8797 POINTER_FLAGS aFlags
, uint32_t aPressure
,
8798 uint32_t aOrientation
) {
8799 if (aId
> TOUCH_INJECT_MAX_POINTS
) {
8800 WinUtils::Log("Pointer ID exceeds maximum. See TOUCH_INJECT_MAX_POINTS.");
8804 POINTER_TOUCH_INFO info
{};
8806 info
.touchFlags
= TOUCH_FLAG_NONE
;
8808 TOUCH_MASK_CONTACTAREA
| TOUCH_MASK_ORIENTATION
| TOUCH_MASK_PRESSURE
;
8809 info
.pressure
= aPressure
;
8810 info
.orientation
= aOrientation
;
8812 info
.pointerInfo
.pointerFlags
= aFlags
;
8813 info
.pointerInfo
.pointerType
= PT_TOUCH
;
8814 info
.pointerInfo
.pointerId
= aId
;
8815 info
.pointerInfo
.ptPixelLocation
.x
= aPoint
.x
;
8816 info
.pointerInfo
.ptPixelLocation
.y
= aPoint
.y
;
8818 info
.rcContact
.top
= info
.pointerInfo
.ptPixelLocation
.y
- 2;
8819 info
.rcContact
.bottom
= info
.pointerInfo
.ptPixelLocation
.y
+ 2;
8820 info
.rcContact
.left
= info
.pointerInfo
.ptPixelLocation
.x
- 2;
8821 info
.rcContact
.right
= info
.pointerInfo
.ptPixelLocation
.x
+ 2;
8823 for (int i
= 0; i
< 3; i
++) {
8824 if (sInjectTouchFuncPtr(1, &info
)) {
8827 DWORD error
= GetLastError();
8828 if (error
== ERROR_NOT_READY
&& i
< 2) {
8829 // We sent it too quickly after the previous injection (see bug 1535140
8830 // comment 10). On the first loop iteration we just yield (via Sleep(0))
8831 // and try again. If it happens again on the second loop iteration we
8832 // explicitly Sleep(1) and try again. If that doesn't work either we just
8837 WinUtils::Log("InjectTouchInput failure. GetLastError=%d", error
);
8843 void nsWindow::ChangedDPI() {
8844 if (mWidgetListener
) {
8845 if (PresShell
* presShell
= mWidgetListener
->GetPresShell()) {
8846 presShell
->BackingScaleFactorChanged();
8851 static Result
<POINTER_FLAGS
, nsresult
> PointerStateToFlag(
8852 nsWindow::TouchPointerState aPointerState
, bool isUpdate
) {
8853 bool hover
= aPointerState
& nsWindow::TOUCH_HOVER
;
8854 bool contact
= aPointerState
& nsWindow::TOUCH_CONTACT
;
8855 bool remove
= aPointerState
& nsWindow::TOUCH_REMOVE
;
8856 bool cancel
= aPointerState
& nsWindow::TOUCH_CANCEL
;
8858 POINTER_FLAGS flags
;
8860 // We know about this pointer, send an update
8861 flags
= POINTER_FLAG_UPDATE
;
8863 flags
|= POINTER_FLAG_INRANGE
;
8864 } else if (contact
) {
8865 flags
|= POINTER_FLAG_INCONTACT
| POINTER_FLAG_INRANGE
;
8866 } else if (remove
) {
8867 flags
= POINTER_FLAG_UP
;
8871 flags
|= POINTER_FLAG_CANCELED
;
8874 // Missing init state, error out
8875 if (remove
|| cancel
) {
8876 return Err(NS_ERROR_INVALID_ARG
);
8879 // Create a new pointer
8880 flags
= POINTER_FLAG_INRANGE
;
8882 flags
|= POINTER_FLAG_INCONTACT
| POINTER_FLAG_DOWN
;
8888 nsresult
nsWindow::SynthesizeNativeTouchPoint(
8889 uint32_t aPointerId
, nsIWidget::TouchPointerState aPointerState
,
8890 LayoutDeviceIntPoint aPoint
, double aPointerPressure
,
8891 uint32_t aPointerOrientation
, nsIObserver
* aObserver
) {
8892 AutoObserverNotifier
notifier(aObserver
, "touchpoint");
8894 if (StaticPrefs::apz_test_fails_with_native_injection() ||
8895 !InitTouchInjection()) {
8896 // If we don't have touch injection from the OS, or if we are running a test
8897 // that cannot properly inject events to satisfy the OS requirements (see
8898 // bug 1313170) we can just fake it and synthesize the events from here.
8899 MOZ_ASSERT(NS_IsMainThread());
8900 if (aPointerState
== TOUCH_HOVER
) {
8901 return NS_ERROR_UNEXPECTED
;
8904 if (!mSynthesizedTouchInput
) {
8905 mSynthesizedTouchInput
= MakeUnique
<MultiTouchInput
>();
8908 WidgetEventTime time
= CurrentMessageWidgetEventTime();
8909 LayoutDeviceIntPoint pointInWindow
= aPoint
- WidgetToScreenOffset();
8910 MultiTouchInput inputToDispatch
= UpdateSynthesizedTouchState(
8911 mSynthesizedTouchInput
.get(), time
.mTimeStamp
, aPointerId
,
8912 aPointerState
, pointInWindow
, aPointerPressure
, aPointerOrientation
);
8913 DispatchTouchInput(inputToDispatch
);
8917 // win api expects a value from 0 to 1024. aPointerPressure is a value
8919 uint32_t pressure
= (uint32_t)ceil(aPointerPressure
* 1024);
8921 // If we already know about this pointer id get it's record
8922 return mActivePointers
.WithEntryHandle(aPointerId
, [&](auto&& entry
) {
8923 POINTER_FLAGS flags
;
8924 // Can't use MOZ_TRY_VAR because it confuses WithEntryHandle
8925 auto result
= PointerStateToFlag(aPointerState
, !!entry
);
8926 if (result
.isOk()) {
8927 flags
= result
.unwrap();
8929 return result
.unwrapErr();
8933 entry
.Insert(MakeUnique
<PointerInfo
>(aPointerId
, aPoint
,
8934 PointerInfo::PointerType::TOUCH
));
8936 if (entry
.Data()->mType
!= PointerInfo::PointerType::TOUCH
) {
8937 return NS_ERROR_UNEXPECTED
;
8939 if (aPointerState
& TOUCH_REMOVE
) {
8940 // Remove the pointer from our tracking list. This is UniquePtr wrapped,
8941 // so shouldn't leak.
8946 return !InjectTouchPoint(aPointerId
, aPoint
, flags
, pressure
,
8947 aPointerOrientation
)
8948 ? NS_ERROR_UNEXPECTED
8953 nsresult
nsWindow::ClearNativeTouchSequence(nsIObserver
* aObserver
) {
8954 AutoObserverNotifier
notifier(aObserver
, "cleartouch");
8955 if (!sTouchInjectInitialized
) {
8959 // cancel all input points
8960 for (auto iter
= mActivePointers
.Iter(); !iter
.Done(); iter
.Next()) {
8961 auto* info
= iter
.UserData();
8962 if (info
->mType
!= PointerInfo::PointerType::TOUCH
) {
8965 InjectTouchPoint(info
->mPointerId
, info
->mPosition
, POINTER_FLAG_CANCELED
);
8969 nsBaseWidget::ClearNativeTouchSequence(nullptr);
8974 #if !defined(NTDDI_WIN10_RS5) || (NTDDI_VERSION < NTDDI_WIN10_RS5)
8975 static CreateSyntheticPointerDevicePtr CreateSyntheticPointerDevice
;
8976 static DestroySyntheticPointerDevicePtr DestroySyntheticPointerDevice
;
8977 static InjectSyntheticPointerInputPtr InjectSyntheticPointerInput
;
8979 static HSYNTHETICPOINTERDEVICE sSyntheticPenDevice
;
8981 static bool InitPenInjection() {
8982 if (sSyntheticPenDevice
) {
8985 #if !defined(NTDDI_WIN10_RS5) || (NTDDI_VERSION < NTDDI_WIN10_RS5)
8986 HMODULE hMod
= LoadLibraryW(kUser32LibName
);
8990 CreateSyntheticPointerDevice
=
8991 (CreateSyntheticPointerDevicePtr
)GetProcAddress(
8992 hMod
, "CreateSyntheticPointerDevice");
8993 if (!CreateSyntheticPointerDevice
) {
8994 WinUtils::Log("CreateSyntheticPointerDevice not available.");
8997 DestroySyntheticPointerDevice
=
8998 (DestroySyntheticPointerDevicePtr
)GetProcAddress(
8999 hMod
, "DestroySyntheticPointerDevice");
9000 if (!DestroySyntheticPointerDevice
) {
9001 WinUtils::Log("DestroySyntheticPointerDevice not available.");
9004 InjectSyntheticPointerInput
= (InjectSyntheticPointerInputPtr
)GetProcAddress(
9005 hMod
, "InjectSyntheticPointerInput");
9006 if (!InjectSyntheticPointerInput
) {
9007 WinUtils::Log("InjectSyntheticPointerInput not available.");
9011 sSyntheticPenDevice
=
9012 CreateSyntheticPointerDevice(PT_PEN
, 1, POINTER_FEEDBACK_DEFAULT
);
9013 return !!sSyntheticPenDevice
;
9016 nsresult
nsWindow::SynthesizeNativePenInput(
9017 uint32_t aPointerId
, nsIWidget::TouchPointerState aPointerState
,
9018 LayoutDeviceIntPoint aPoint
, double aPressure
, uint32_t aRotation
,
9019 int32_t aTiltX
, int32_t aTiltY
, int32_t aButton
, nsIObserver
* aObserver
) {
9020 AutoObserverNotifier
notifier(aObserver
, "peninput");
9021 if (!InitPenInjection()) {
9022 return NS_ERROR_UNEXPECTED
;
9025 // win api expects a value from 0 to 1024. aPointerPressure is a value
9027 uint32_t pressure
= (uint32_t)ceil(aPressure
* 1024);
9029 // If we already know about this pointer id get it's record
9030 return mActivePointers
.WithEntryHandle(aPointerId
, [&](auto&& entry
) {
9031 POINTER_FLAGS flags
;
9032 // Can't use MOZ_TRY_VAR because it confuses WithEntryHandle
9033 auto result
= PointerStateToFlag(aPointerState
, !!entry
);
9034 if (result
.isOk()) {
9035 flags
= result
.unwrap();
9037 return result
.unwrapErr();
9041 entry
.Insert(MakeUnique
<PointerInfo
>(aPointerId
, aPoint
,
9042 PointerInfo::PointerType::PEN
));
9044 if (entry
.Data()->mType
!= PointerInfo::PointerType::PEN
) {
9045 return NS_ERROR_UNEXPECTED
;
9047 if (aPointerState
& TOUCH_REMOVE
) {
9048 // Remove the pointer from our tracking list. This is UniquePtr wrapped,
9049 // so shouldn't leak.
9054 POINTER_TYPE_INFO info
{};
9057 info
.penInfo
.pointerInfo
.pointerType
= PT_PEN
;
9058 info
.penInfo
.pointerInfo
.pointerFlags
= flags
;
9059 info
.penInfo
.pointerInfo
.pointerId
= aPointerId
;
9060 info
.penInfo
.pointerInfo
.ptPixelLocation
.x
= aPoint
.x
;
9061 info
.penInfo
.pointerInfo
.ptPixelLocation
.y
= aPoint
.y
;
9063 info
.penInfo
.penFlags
= PEN_FLAG_NONE
;
9064 // PEN_FLAG_ERASER is not supported this way, unfortunately.
9066 info
.penInfo
.penFlags
|= PEN_FLAG_BARREL
;
9068 info
.penInfo
.penMask
= PEN_MASK_PRESSURE
| PEN_MASK_ROTATION
|
9069 PEN_MASK_TILT_X
| PEN_MASK_TILT_Y
;
9070 info
.penInfo
.pressure
= pressure
;
9071 info
.penInfo
.rotation
= aRotation
;
9072 info
.penInfo
.tiltX
= aTiltX
;
9073 info
.penInfo
.tiltY
= aTiltY
;
9075 return InjectSyntheticPointerInput(sSyntheticPenDevice
, &info
, 1)
9077 : NS_ERROR_UNEXPECTED
;
9081 bool nsWindow::HandleAppCommandMsg(const MSG
& aAppCommandMsg
,
9082 LRESULT
* aRetValue
) {
9083 ModifierKeyState modKeyState
;
9084 NativeKey
nativeKey(this, aAppCommandMsg
, modKeyState
);
9085 bool consumed
= nativeKey
.HandleAppCommandMessage();
9086 *aRetValue
= consumed
? 1 : 0;
9091 nsresult
nsWindow::SetHiDPIMode(bool aHiDPI
) {
9092 return WinUtils::SetHiDPIMode(aHiDPI
);
9095 nsresult
nsWindow::RestoreHiDPIMode() { return WinUtils::RestoreHiDPIMode(); }
9098 mozilla::Maybe
<UINT
> nsWindow::GetHiddenTaskbarEdge() {
9099 HMONITOR windowMonitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONEAREST
);
9101 if (!IsWin8OrLater()) {
9102 // Per-monitor taskbar information is not available.
9103 APPBARDATA appBarData
;
9104 appBarData
.cbSize
= sizeof(appBarData
);
9105 UINT taskbarState
= SHAppBarMessage(ABM_GETSTATE
, &appBarData
);
9106 if (ABS_AUTOHIDE
& taskbarState
) {
9107 appBarData
.hWnd
= FindWindow(L
"Shell_TrayWnd", nullptr);
9108 if (appBarData
.hWnd
) {
9109 HMONITOR taskbarMonitor
=
9110 ::MonitorFromWindow(appBarData
.hWnd
, MONITOR_DEFAULTTOPRIMARY
);
9111 if (taskbarMonitor
== windowMonitor
) {
9112 SHAppBarMessage(ABM_GETTASKBARPOS
, &appBarData
);
9113 return Some(appBarData
.uEdge
);
9120 // Check all four sides of our monitor for an appbar. Skip any that aren't
9121 // the system taskbar.
9123 mi
.cbSize
= sizeof(MONITORINFO
);
9124 ::GetMonitorInfo(windowMonitor
, &mi
);
9126 APPBARDATA appBarData
;
9127 appBarData
.cbSize
= sizeof(appBarData
);
9128 appBarData
.rc
= mi
.rcMonitor
;
9129 const auto kEdges
= {ABE_BOTTOM
, ABE_TOP
, ABE_LEFT
, ABE_RIGHT
};
9130 for (auto edge
: kEdges
) {
9131 appBarData
.uEdge
= edge
;
9132 // ABM_GETAUTOHIDEBAREX is not defined before Windows 8.
9133 static constexpr DWORD ABM_GETAUTOHIDEBAREX
= 0x000b;
9134 HWND appBarHwnd
= (HWND
)SHAppBarMessage(ABM_GETAUTOHIDEBAREX
, &appBarData
);
9136 nsAutoString className
;
9137 if (WinUtils::GetClassName(appBarHwnd
, className
)) {
9138 if (className
.Equals(L
"Shell_TrayWnd") ||
9139 className
.Equals(L
"Shell_SecondaryTrayWnd")) {
9149 static nsSizeMode
GetSizeModeForWindowFrame(HWND aWnd
, bool aFullscreenMode
) {
9151 pl
.length
= sizeof(pl
);
9152 ::GetWindowPlacement(aWnd
, &pl
);
9154 if (pl
.showCmd
== SW_SHOWMINIMIZED
) {
9155 return nsSizeMode_Minimized
;
9156 } else if (aFullscreenMode
) {
9157 return nsSizeMode_Fullscreen
;
9158 } else if (pl
.showCmd
== SW_SHOWMAXIMIZED
) {
9159 return nsSizeMode_Maximized
;
9161 return nsSizeMode_Normal
;
9165 static void ShowWindowWithMode(HWND aWnd
, nsSizeMode aMode
) {
9166 // This will likely cause a callback to
9167 // nsWindow::FrameState::{OnFrameChanging() and OnFrameChanged()}
9169 case nsSizeMode_Fullscreen
:
9170 ::ShowWindow(aWnd
, SW_SHOW
);
9173 case nsSizeMode_Maximized
:
9174 ::ShowWindow(aWnd
, SW_MAXIMIZE
);
9177 case nsSizeMode_Minimized
:
9178 ::ShowWindow(aWnd
, SW_MINIMIZE
);
9182 // Don't call ::ShowWindow if we're trying to "restore" a window that is
9183 // already in a normal state. Prevents a bug where snapping to one side
9184 // of the screen and then minimizing would cause Windows to forget our
9185 // window's correct restored position/size.
9186 if (GetCurrentShowCmd(aWnd
) != SW_SHOWNORMAL
) {
9187 ::ShowWindow(aWnd
, SW_RESTORE
);
9192 nsWindow::FrameState::FrameState(nsWindow
* aWindow
) : mWindow(aWindow
) {}
9194 nsSizeMode
nsWindow::FrameState::GetSizeMode() const { return mSizeMode
; }
9196 void nsWindow::FrameState::CheckInvariant() const {
9197 MOZ_ASSERT(mSizeMode
>= 0 && mSizeMode
< nsSizeMode_Invalid
);
9198 MOZ_ASSERT(mLastSizeMode
>= 0 && mLastSizeMode
< nsSizeMode_Invalid
);
9199 MOZ_ASSERT(mPreFullscreenSizeMode
>= 0 &&
9200 mPreFullscreenSizeMode
< nsSizeMode_Invalid
);
9201 MOZ_ASSERT(mWindow
);
9203 // We should never observe fullscreen sizemode unless fullscreen is enabled
9204 MOZ_ASSERT_IF(mSizeMode
== nsSizeMode_Fullscreen
, mFullscreenMode
);
9205 MOZ_ASSERT_IF(!mFullscreenMode
, mSizeMode
!= nsSizeMode_Fullscreen
);
9207 // Something went wrong if we somehow saved fullscreen mode when we are
9208 // changing into fullscreen mode
9209 MOZ_ASSERT(mPreFullscreenSizeMode
!= nsSizeMode_Fullscreen
);
9212 void nsWindow::FrameState::ConsumePreXULSkeletonState(bool aWasMaximized
) {
9213 mSizeMode
= aWasMaximized
? nsSizeMode_Maximized
: nsSizeMode_Normal
;
9216 void nsWindow::FrameState::EnsureSizeMode(nsSizeMode aMode
,
9217 DoShowWindow aDoShowWindow
) {
9218 if (mSizeMode
== aMode
) {
9222 if (aMode
== nsSizeMode_Fullscreen
) {
9223 EnsureFullscreenMode(true, aDoShowWindow
);
9224 MOZ_ASSERT(mSizeMode
== nsSizeMode_Fullscreen
);
9225 } else if (mSizeMode
== nsSizeMode_Fullscreen
&& aMode
== nsSizeMode_Normal
) {
9226 // If we are in fullscreen mode, minimize should work like normal and
9227 // return us to fullscreen mode when unminimized. Maximize isn't really
9228 // available and won't do anything. "Restore" should do the same thing as
9229 // requesting to end fullscreen.
9230 EnsureFullscreenMode(false, aDoShowWindow
);
9232 SetSizeModeInternal(aMode
, aDoShowWindow
);
9236 void nsWindow::FrameState::EnsureFullscreenMode(bool aFullScreen
,
9237 DoShowWindow aDoShowWindow
) {
9238 const bool changed
= aFullScreen
!= mFullscreenMode
;
9239 if (changed
&& aFullScreen
) {
9240 // Save the size mode from before fullscreen.
9241 mPreFullscreenSizeMode
= mSizeMode
;
9243 mFullscreenMode
= aFullScreen
;
9244 if (changed
|| aFullScreen
) {
9245 // NOTE(emilio): When minimizing a fullscreen window we remain with
9246 // mFullscreenMode = true, but mSizeMode = nsSizeMode_Minimized. We need to
9247 // make sure to call SetSizeModeInternal even if mFullscreenMode didn't
9248 // change, to ensure we actually end up with a fullscreen sizemode when
9249 // restoring a window from that state.
9250 SetSizeModeInternal(
9251 aFullScreen
? nsSizeMode_Fullscreen
: mPreFullscreenSizeMode
,
9256 void nsWindow::FrameState::OnFrameChanging() {
9257 const nsSizeMode newSizeMode
=
9258 GetSizeModeForWindowFrame(mWindow
->mWnd
, mFullscreenMode
);
9259 EnsureSizeMode(newSizeMode
);
9260 mWindow
->UpdateNonClientMargins(false);
9263 void nsWindow::FrameState::OnFrameChanged() {
9264 // We don't want to perform the ShowWindow ourselves if we're on the frame
9265 // changed message. Windows has done the frame change for us, and we take care
9266 // of activating as needed. We also don't want to potentially trigger
9267 // more focus / restore. Among other things, this addresses a bug on Win7
9268 // related to window docking. (bug 489258)
9269 const auto newSizeMode
=
9270 GetSizeModeForWindowFrame(mWindow
->mWnd
, mFullscreenMode
);
9271 EnsureSizeMode(newSizeMode
, DoShowWindow::No
);
9273 // If window was restored, activate the window now to get correct attributes.
9274 if (mWindow
->mIsVisible
&& mWindow
->IsForegroundWindow() &&
9275 mLastSizeMode
== nsSizeMode_Minimized
&&
9276 mSizeMode
!= nsSizeMode_Minimized
) {
9277 mWindow
->DispatchFocusToTopLevelWindow(true);
9279 mLastSizeMode
= mSizeMode
;
9282 static void MaybeLogSizeMode(nsSizeMode aMode
) {
9283 #ifdef WINSTATE_DEBUG_OUTPUT
9284 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("*** SizeMode: %d\n", int(aMode
)));
9288 void nsWindow::FrameState::SetSizeModeInternal(nsSizeMode aMode
,
9289 DoShowWindow aDoShowWindow
) {
9290 if (mSizeMode
== aMode
) {
9294 const auto oldSizeMode
= mSizeMode
;
9295 const bool fullscreenChange
=
9296 mSizeMode
== nsSizeMode_Fullscreen
|| aMode
== nsSizeMode_Fullscreen
;
9297 const bool fullscreen
= aMode
== nsSizeMode_Fullscreen
;
9299 mLastSizeMode
= mSizeMode
;
9302 MaybeLogSizeMode(mSizeMode
);
9304 if (bool(aDoShowWindow
) && mWindow
->mIsVisible
) {
9305 ShowWindowWithMode(mWindow
->mWnd
, aMode
);
9308 mWindow
->UpdateNonClientMargins(false);
9310 if (fullscreenChange
) {
9311 mWindow
->OnFullscreenChanged(oldSizeMode
, fullscreen
);
9314 mWindow
->OnSizeModeChange();
9317 void nsWindow::ContextMenuPreventer::Update(
9318 const WidgetMouseEvent
& aEvent
,
9319 const nsIWidget::ContentAndAPZEventStatus
& aEventStatus
) {
9320 mNeedsToPreventContextMenu
=
9321 aEvent
.mMessage
== eMouseUp
&&
9322 aEvent
.mButton
== MouseButton::eSecondary
&&
9323 aEvent
.mInputSource
== MouseEvent_Binding::MOZ_SOURCE_MOUSE
&&
9324 aEventStatus
.mApzStatus
== nsEventStatus_eConsumeNoDefault
;